]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Fix polling for new data in cups print backend (bug #599664)
[~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_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, backend);
2112       g_list_free (removed_printer_checklist);
2113       list_has_changed = TRUE;
2114     }
2115   
2116 done:
2117   if (list_has_changed)
2118     g_signal_emit_by_name (backend, "printer-list-changed");
2119   
2120   gtk_print_backend_set_list_done (backend);
2121
2122   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
2123     {
2124       cups_backend->default_printer = g_strdup (remote_default_printer);
2125       cups_backend->got_default_printer = TRUE;
2126       g_free (remote_default_printer);
2127
2128       if (cups_backend->default_printer != NULL)
2129         {
2130           GtkPrinter *default_printer = NULL;
2131           default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
2132                                                             cups_backend->default_printer);
2133           if (default_printer != NULL)
2134             {
2135               gtk_printer_set_is_default (default_printer, TRUE);
2136               g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
2137                                      "printer-status-changed", default_printer);
2138             }
2139         }
2140     }
2141
2142   GDK_THREADS_LEAVE ();
2143 }
2144
2145 static void
2146 update_backend_status (GtkPrintBackendCups    *cups_backend,
2147                        GtkCupsConnectionState  state)
2148 {
2149   switch (state)
2150     {
2151     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
2152       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
2153       break;
2154     case GTK_CUPS_CONNECTION_AVAILABLE:
2155       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
2156       break;
2157     default: ;
2158     }
2159 }
2160
2161 static gboolean
2162 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
2163 {
2164   GtkCupsConnectionState state;
2165   GtkCupsRequest *request;
2166   static const char * const pattrs[] =  /* Attributes we're interested in */
2167     {
2168       "printer-name",
2169       "printer-uri-supported",
2170       "member-uris",
2171       "printer-location",
2172       "printer-info",
2173       "printer-state-message",
2174       "printer-state-reasons",
2175       "printer-state",
2176       "queued-job-count",
2177       "printer-is-accepting-jobs",
2178       "job-sheets-supported",
2179       "job-sheets-default",
2180       "printer-type",
2181       "auth-info-required"
2182     };
2183
2184   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
2185     return TRUE;
2186
2187   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
2188   update_backend_status (cups_backend, state);
2189
2190   if (cups_backend->list_printers_attempts == 60)
2191     {
2192       cups_backend->list_printers_attempts = -1;
2193       if (cups_backend->list_printers_poll > 0)
2194         g_source_remove (cups_backend->list_printers_poll);
2195       cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
2196                                            (GSourceFunc) cups_request_printer_list,
2197                                            cups_backend);
2198     }
2199   else if (cups_backend->list_printers_attempts != -1)
2200     cups_backend->list_printers_attempts++;
2201
2202   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2203     return TRUE;
2204   else
2205     if (cups_backend->list_printers_attempts > 0)
2206       cups_backend->list_printers_attempts = 60;
2207
2208   cups_backend->list_printers_pending = TRUE;
2209
2210   request = gtk_cups_request_new_with_username (NULL,
2211                                                 GTK_CUPS_POST,
2212                                                 CUPS_GET_PRINTERS,
2213                                                 NULL,
2214                                                 NULL,
2215                                                 NULL,
2216                                                 cups_backend->username);
2217
2218   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2219                                     "requested-attributes", G_N_ELEMENTS (pattrs),
2220                                     NULL, pattrs);
2221
2222   cups_request_execute (cups_backend,
2223                         request,
2224                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
2225                         request,
2226                         NULL);
2227
2228   return TRUE;
2229 }
2230
2231 static void
2232 cups_get_printer_list (GtkPrintBackend *backend)
2233 {
2234   GtkPrintBackendCups *cups_backend;
2235
2236   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
2237
2238   if (cups_backend->cups_connection_test == NULL)
2239     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2240
2241   if (cups_backend->list_printers_poll == 0)
2242     {
2243       if (cups_request_printer_list (cups_backend))
2244         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
2245                                              (GSourceFunc) cups_request_printer_list,
2246                                              backend);
2247     }
2248 }
2249
2250 typedef struct {
2251   GtkPrinterCups *printer;
2252   GIOChannel *ppd_io;
2253   http_t *http;
2254 } GetPPDData;
2255
2256 static void
2257 get_ppd_data_free (GetPPDData *data)
2258 {
2259   GTK_NOTE (PRINTING,
2260             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2261   httpClose (data->http);
2262   g_io_channel_unref (data->ppd_io);
2263   g_object_unref (data->printer);
2264   g_free (data);
2265 }
2266
2267 static void
2268 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
2269                      GtkCupsResult       *result,
2270                      GetPPDData          *data)
2271 {
2272   GtkPrinter *printer;
2273
2274   GDK_THREADS_ENTER ();
2275
2276   GTK_NOTE (PRINTING,
2277             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2278
2279   printer = GTK_PRINTER (data->printer);
2280   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
2281   print_backend->reading_ppds--;
2282
2283   if (gtk_cups_result_is_error (result))
2284     {
2285       gboolean success = FALSE;
2286
2287       /* if we get a 404 then it is just a raw printer without a ppd
2288          and not an error */
2289       if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
2290           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
2291         {
2292           gtk_printer_set_has_details (printer, TRUE);
2293           success = TRUE;
2294         } 
2295         
2296       g_signal_emit_by_name (printer, "details-acquired", success);
2297       goto done;
2298     }
2299
2300   /* let ppdOpenFd take over the ownership of the open file */
2301   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
2302   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
2303
2304   ppdMarkDefaults (data->printer->ppd_file);
2305   
2306   gtk_printer_set_has_details (printer, TRUE);
2307   g_signal_emit_by_name (printer, "details-acquired", TRUE);
2308
2309 done:
2310   GDK_THREADS_LEAVE ();
2311 }
2312
2313 static gboolean
2314 cups_request_ppd (GtkPrinter *printer)
2315 {
2316   GError *error;
2317   GtkPrintBackend *print_backend;
2318   GtkPrinterCups *cups_printer;
2319   GtkCupsRequest *request;
2320   char *ppd_filename;
2321   gchar *resource;
2322   http_t *http;
2323   GetPPDData *data;
2324   int fd;
2325
2326   cups_printer = GTK_PRINTER_CUPS (printer);
2327
2328   error = NULL;
2329
2330   GTK_NOTE (PRINTING,
2331             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2332
2333   if (cups_printer->remote)
2334     {
2335       GtkCupsConnectionState state;
2336
2337       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
2338
2339       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
2340         {
2341           if (cups_printer->get_remote_ppd_attempts == 60)
2342             {
2343               cups_printer->get_remote_ppd_attempts = -1;
2344               if (cups_printer->get_remote_ppd_poll > 0)
2345                 g_source_remove (cups_printer->get_remote_ppd_poll);
2346               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
2347                                                     (GSourceFunc) cups_request_ppd,
2348                                                     printer);
2349             }
2350           else if (cups_printer->get_remote_ppd_attempts != -1)
2351             cups_printer->get_remote_ppd_attempts++;
2352
2353           return TRUE;
2354         }
2355
2356       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
2357       cups_printer->remote_cups_connection_test = NULL;
2358       cups_printer->get_remote_ppd_poll = 0;
2359       cups_printer->get_remote_ppd_attempts = 0;
2360
2361       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2362         {
2363           g_signal_emit_by_name (printer, "details-acquired", FALSE);
2364           return FALSE;
2365         }
2366     }
2367
2368   http = httpConnectEncrypt (cups_printer->hostname, 
2369                              cups_printer->port,
2370                              cupsEncryption ());
2371   
2372   data = g_new0 (GetPPDData, 1);
2373
2374   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
2375                         &ppd_filename, 
2376                         &error);
2377
2378 #ifdef G_ENABLE_DEBUG 
2379   /* If we are debugging printing don't delete the tmp files */
2380   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
2381     unlink (ppd_filename);
2382 #else
2383   unlink (ppd_filename);
2384 #endif /* G_ENABLE_DEBUG */
2385
2386   if (error != NULL)
2387     {
2388       GTK_NOTE (PRINTING, 
2389                 g_warning ("CUPS Backend: Failed to create temp file, %s\n", 
2390                            error->message));
2391       g_error_free (error);
2392       httpClose (http);
2393       g_free (ppd_filename);
2394       g_free (data);
2395
2396       g_signal_emit_by_name (printer, "details-acquired", FALSE);
2397       return FALSE;
2398     }
2399     
2400   data->http = http;
2401   fchmod (fd, S_IRUSR | S_IWUSR);
2402   data->ppd_io = g_io_channel_unix_new (fd);
2403   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
2404   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
2405
2406   data->printer = g_object_ref (printer);
2407
2408   resource = g_strdup_printf ("/printers/%s.ppd", 
2409                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
2410
2411   print_backend = gtk_printer_get_backend (printer);
2412
2413   request = gtk_cups_request_new_with_username (data->http,
2414                                                 GTK_CUPS_GET,
2415                                                 0,
2416                                                 data->ppd_io,
2417                                                 cups_printer->hostname,
2418                                                 resource,
2419                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
2420
2421   GTK_NOTE (PRINTING,
2422             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
2423
2424
2425   cups_printer->reading_ppd = TRUE;
2426   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
2427
2428   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
2429                         request,
2430                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
2431                         data,
2432                         (GDestroyNotify)get_ppd_data_free);
2433
2434   g_free (resource);
2435   g_free (ppd_filename);
2436
2437   return FALSE;
2438 }
2439
2440 /* Ordering matters for default preference */
2441 static const char *lpoptions_locations[] = {
2442   "/etc/cups/lpoptions",
2443   ".lpoptions", 
2444   ".cups/lpoptions"
2445 };
2446
2447 static void
2448 cups_parse_user_default_printer (const char  *filename,
2449                                  char       **printer_name)
2450 {
2451   FILE *fp;
2452   char line[1024], *lineptr, *defname = NULL;
2453   
2454   if ((fp = g_fopen (filename, "r")) == NULL)
2455     return;
2456
2457   while (fgets (line, sizeof (line), fp) != NULL)
2458     {
2459       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
2460         continue;
2461
2462       lineptr = line + 8;
2463       while (isspace (*lineptr))
2464         lineptr++;
2465
2466       if (!*lineptr)
2467         continue;
2468
2469       defname = lineptr;
2470       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
2471         lineptr++;
2472
2473       *lineptr = '\0';
2474
2475       if (*printer_name != NULL)
2476         g_free (*printer_name);
2477
2478       *printer_name = g_strdup (defname);
2479     }
2480
2481   fclose (fp);
2482 }
2483
2484 static void
2485 cups_get_user_default_printer (char **printer_name)
2486 {
2487   int i;
2488
2489   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2490     {
2491       if (g_path_is_absolute (lpoptions_locations[i]))
2492         {
2493           cups_parse_user_default_printer (lpoptions_locations[i],
2494                                            printer_name);
2495         }
2496       else 
2497         {
2498           char *filename;
2499
2500           filename = g_build_filename (g_get_home_dir (), 
2501                                        lpoptions_locations[i], NULL);
2502           cups_parse_user_default_printer (filename, printer_name);
2503           g_free (filename);
2504         }
2505     }
2506 }
2507
2508 static int
2509 cups_parse_user_options (const char     *filename,
2510                          const char     *printer_name,
2511                          int             num_options,
2512                          cups_option_t **options)
2513 {
2514   FILE *fp;
2515   gchar line[1024], *lineptr, *name;
2516
2517   if ((fp = g_fopen (filename, "r")) == NULL)
2518     return num_options;
2519
2520   while (fgets (line, sizeof (line), fp) != NULL)
2521     {
2522       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
2523         lineptr = line + 4;
2524       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
2525         lineptr = line + 7;
2526       else
2527         continue;
2528
2529       /* Skip leading whitespace */
2530       while (isspace (*lineptr))
2531         lineptr++;
2532
2533       if (!*lineptr)
2534         continue;
2535
2536       /* NUL-terminate the name, stripping the instance name */
2537       name = lineptr;
2538       while (!isspace (*lineptr) && *lineptr)
2539         {
2540           if (*lineptr == '/')
2541             *lineptr = '\0';
2542           lineptr++;
2543         }
2544
2545       if (!*lineptr)
2546         continue;
2547
2548       *lineptr++ = '\0';
2549
2550       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
2551           continue;
2552
2553       /* We found our printer, parse the options */
2554       num_options = cupsParseOptions (lineptr, num_options, options);
2555     }
2556
2557   fclose (fp);
2558
2559   return num_options;
2560 }
2561
2562 static int
2563 cups_get_user_options (const char     *printer_name,
2564                        int             num_options,
2565                        cups_option_t **options)
2566 {
2567   int i;
2568
2569   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2570     {
2571       if (g_path_is_absolute (lpoptions_locations[i]))
2572         { 
2573            num_options = cups_parse_user_options (lpoptions_locations[i],
2574                                                   printer_name,
2575                                                   num_options,
2576                                                   options);
2577         }
2578       else
2579         {
2580           char *filename;
2581
2582           filename = g_build_filename (g_get_home_dir (), 
2583                                        lpoptions_locations[i], NULL);
2584           num_options = cups_parse_user_options (filename, printer_name,
2585                                                  num_options, options);
2586           g_free (filename);
2587         }
2588     }
2589
2590   return num_options;
2591 }
2592
2593 /* This function requests default printer from a CUPS server in regular intervals.
2594  * In the case of unreachable CUPS server the request is repeated later.
2595  * The default printer is not requested in the case of previous success.
2596  */
2597 static void
2598 cups_get_default_printer (GtkPrintBackendCups *backend)
2599 {
2600   GtkPrintBackendCups *cups_backend;
2601
2602   cups_backend = backend;
2603
2604   if (cups_backend->cups_connection_test == NULL)
2605     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2606
2607   if (cups_backend->default_printer_poll == 0)
2608     {
2609       if (cups_request_default_printer (cups_backend))
2610         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
2611                                                (GSourceFunc) cups_request_default_printer,
2612                                                backend);
2613     }
2614 }
2615
2616 /* This function gets default printer from local settings.*/
2617 static void
2618 cups_get_local_default_printer (GtkPrintBackendCups *backend)
2619 {
2620   const char *str;
2621   char *name = NULL;
2622
2623   if ((str = g_getenv ("LPDEST")) != NULL)
2624     {
2625       backend->default_printer = g_strdup (str);
2626       backend->got_default_printer = TRUE;
2627       return;
2628     }
2629   else if ((str = g_getenv ("PRINTER")) != NULL &&
2630            strcmp (str, "lp") != 0)
2631     {
2632       backend->default_printer = g_strdup (str);
2633       backend->got_default_printer = TRUE;
2634       return;
2635     }
2636   
2637   /* Figure out user setting for default printer */  
2638   cups_get_user_default_printer (&name);
2639   if (name != NULL)
2640     {
2641       backend->default_printer = name;
2642       backend->got_default_printer = TRUE;
2643       return;
2644     }
2645 }
2646
2647 static void
2648 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
2649                                  GtkCupsResult       *result,
2650                                  gpointer             user_data)
2651 {
2652   ipp_t *response;
2653   ipp_attribute_t *attr;
2654   GtkPrinter *printer;
2655
2656   GDK_THREADS_ENTER ();
2657
2658   if (gtk_cups_result_is_error (result))
2659     {
2660       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2661           gtk_cups_result_get_error_code (result) == 1)
2662         {
2663           /* Canceled by user, stop popping up more password dialogs */
2664           if (print_backend->list_printers_poll > 0)
2665             g_source_remove (print_backend->list_printers_poll);
2666           print_backend->list_printers_poll = 0;
2667         }
2668
2669       return;
2670     }
2671
2672   response = gtk_cups_result_get_response (result);
2673   
2674   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
2675     print_backend->default_printer = g_strdup (attr->values[0].string.text);
2676
2677   print_backend->got_default_printer = TRUE;
2678
2679   if (print_backend->default_printer != NULL)
2680     {
2681       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
2682       if (printer != NULL)
2683         {
2684           gtk_printer_set_is_default (printer, TRUE);
2685           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
2686         }
2687     }
2688
2689   /* Make sure to kick off get_printers if we are polling it, 
2690    * as we could have blocked this reading the default printer 
2691    */
2692   if (print_backend->list_printers_poll != 0)
2693     cups_request_printer_list (print_backend);
2694
2695   GDK_THREADS_LEAVE ();
2696 }
2697
2698 static gboolean
2699 cups_request_default_printer (GtkPrintBackendCups *print_backend)
2700 {
2701   GtkCupsConnectionState state;
2702   GtkCupsRequest *request;
2703
2704   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
2705   update_backend_status (print_backend, state);
2706
2707   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2708     return TRUE;
2709
2710   request = gtk_cups_request_new_with_username (NULL,
2711                                                 GTK_CUPS_POST,
2712                                                 CUPS_GET_DEFAULT,
2713                                                 NULL,
2714                                                 NULL,
2715                                                 NULL,
2716                                                 print_backend->username);
2717   
2718   cups_request_execute (print_backend,
2719                         request,
2720                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
2721                         g_object_ref (print_backend),
2722                         g_object_unref);
2723
2724   return FALSE;
2725 }
2726
2727 static void
2728 cups_printer_request_details (GtkPrinter *printer)
2729 {
2730   GtkPrinterCups *cups_printer;
2731
2732   cups_printer = GTK_PRINTER_CUPS (printer);
2733   if (!cups_printer->reading_ppd && 
2734       gtk_printer_cups_get_ppd (cups_printer) == NULL)
2735     {
2736       if (cups_printer->remote)
2737         {
2738           if (cups_printer->get_remote_ppd_poll == 0)
2739             {
2740               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
2741
2742               if (cups_request_ppd (printer))
2743                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
2744                                                     (GSourceFunc) cups_request_ppd,
2745                                                     printer);
2746             }
2747         }
2748       else
2749         cups_request_ppd (printer);
2750     }
2751 }
2752
2753 static char *
2754 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
2755                   const char *text)
2756 {
2757   const char *encoding = NULL;
2758   char *res;
2759   
2760   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
2761     {
2762       return g_strdup (text);
2763     }
2764   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
2765     {
2766       encoding = "ISO-8859-1";
2767     }
2768   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
2769     {
2770       encoding = "ISO-8859-2";
2771     }
2772   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
2773     {
2774       encoding = "ISO-8859-5";
2775     }
2776   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
2777     {
2778       encoding = "SHIFT-JIS";
2779     }
2780   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
2781     {
2782       encoding = "MACINTOSH";
2783     }
2784   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
2785     {
2786       encoding = "WINDOWS-1252";
2787     }
2788   else 
2789     {
2790       /* Fallback, try iso-8859-1... */
2791       encoding = "ISO-8859-1";
2792     }
2793
2794   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
2795
2796   if (res == NULL)
2797     {
2798       GTK_NOTE (PRINTING,
2799                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
2800       res = g_strdup ("???");
2801     }
2802   
2803   return res;
2804 }
2805
2806 /* TODO: Add more translations for common settings here */
2807
2808 static const struct {
2809   const char *keyword;
2810   const char *translation;
2811 } cups_option_translations[] = {
2812   { "Duplex", N_("Two Sided") },
2813   { "MediaType", N_("Paper Type") },
2814   { "InputSlot", N_("Paper Source") },
2815   { "OutputBin", N_("Output Tray") },
2816   { "Resolution", N_("Resolution") },
2817   { "PreFilter", N_("GhostScript pre-filtering") },
2818 };
2819
2820
2821 static const struct {
2822   const char *keyword;
2823   const char *choice;
2824   const char *translation;
2825 } cups_choice_translations[] = {
2826   { "Duplex", "None", N_("One Sided") },
2827   /* Translators: this is an option of "Two Sided" */
2828   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
2829   /* Translators: this is an option of "Two Sided" */
2830   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
2831   /* Translators: this is an option of "Paper Source" */
2832   { "InputSlot", "Auto", N_("Auto Select") },
2833   /* Translators: this is an option of "Paper Source" */
2834   { "InputSlot", "AutoSelect", N_("Auto Select") },
2835   /* Translators: this is an option of "Paper Source" */
2836   { "InputSlot", "Default", N_("Printer Default") },
2837   /* Translators: this is an option of "Paper Source" */
2838   { "InputSlot", "None", N_("Printer Default") },
2839   /* Translators: this is an option of "Paper Source" */
2840   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2841   /* Translators: this is an option of "Paper Source" */
2842   { "InputSlot", "Unspecified", N_("Auto Select") },
2843   /* Translators: this is an option of "Resolution" */
2844   { "Resolution", "default", N_("Printer Default") },
2845   /* Translators: this is an option of "GhostScript" */
2846   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
2847   /* Translators: this is an option of "GhostScript" */
2848   { "PreFilter", "Level1", N_("Convert to PS level 1") },
2849   /* Translators: this is an option of "GhostScript" */
2850   { "PreFilter", "Level2", N_("Convert to PS level 2") },
2851   /* Translators: this is an option of "GhostScript" */
2852   { "PreFilter", "No", N_("No pre-filtering") },
2853 };
2854
2855 static const struct {
2856   const char *name;
2857   const char *translation;
2858 } cups_group_translations[] = {
2859 /* Translators: "Miscellaneous" is the label for a button, that opens
2860    up an extra panel of settings in a print dialog. */
2861   { "Miscellaneous", N_("Miscellaneous") },
2862 };
2863
2864 static const struct {
2865   const char *ppd_keyword;
2866   const char *name;
2867 } ppd_option_names[] = {
2868   {"Duplex", "gtk-duplex" },
2869   {"MediaType", "gtk-paper-type"},
2870   {"InputSlot", "gtk-paper-source"},
2871   {"OutputBin", "gtk-output-tray"},
2872 };
2873
2874 static const struct {
2875   const char *lpoption;
2876   const char *name;
2877 } lpoption_names[] = {
2878   {"number-up", "gtk-n-up" },
2879   {"number-up-layout", "gtk-n-up-layout"},
2880   {"job-billing", "gtk-billing-info"},
2881   {"job-priority", "gtk-job-prio"},
2882 };
2883
2884 /* keep sorted when changing */
2885 static const char *color_option_whitelist[] = {
2886   "BRColorEnhancement",
2887   "BRColorMatching",
2888   "BRColorMatching",
2889   "BRColorMode",
2890   "BRGammaValue",
2891   "BRImprovedGray",
2892   "BlackSubstitution",
2893   "ColorModel",
2894   "HPCMYKInks",
2895   "HPCSGraphics",
2896   "HPCSImages",
2897   "HPCSText",
2898   "HPColorSmart",
2899   "RPSBlackMode",
2900   "RPSBlackOverPrint",
2901   "Rcmyksimulation",
2902 };
2903
2904 /* keep sorted when changing */
2905 static const char *color_group_whitelist[] = {
2906   "ColorPage",
2907   "FPColorWise1",
2908   "FPColorWise2",
2909   "FPColorWise3",
2910   "FPColorWise4",
2911   "FPColorWise5",
2912   "HPColorOptionsPanel",
2913 };
2914   
2915 /* keep sorted when changing */
2916 static const char *image_quality_option_whitelist[] = {
2917   "BRDocument",
2918   "BRHalfTonePattern",
2919   "BRNormalPrt",
2920   "BRPrintQuality",
2921   "BitsPerPixel",
2922   "Darkness",
2923   "Dithering",
2924   "EconoMode",
2925   "Economode",
2926   "HPEconoMode",
2927   "HPEdgeControl",
2928   "HPGraphicsHalftone",
2929   "HPHalftone",
2930   "HPLJDensity",
2931   "HPPhotoHalftone",
2932   "OutputMode",
2933   "REt",
2934   "RPSBitsPerPixel",
2935   "RPSDitherType",
2936   "Resolution",
2937   "ScreenLock",
2938   "Smoothing",
2939   "TonerSaveMode",
2940   "UCRGCRForImage",
2941 };
2942
2943 /* keep sorted when changing */
2944 static const char *image_quality_group_whitelist[] = {
2945   "FPImageQuality1",
2946   "FPImageQuality2",
2947   "FPImageQuality3",
2948   "ImageQualityPage",
2949 };
2950
2951 /* keep sorted when changing */
2952 static const char * finishing_option_whitelist[] = {
2953   "BindColor",
2954   "BindEdge",
2955   "BindType",
2956   "BindWhen",
2957   "Booklet",
2958   "FoldType",
2959   "FoldWhen",
2960   "HPStaplerOptions",
2961   "Jog",
2962   "Slipsheet",
2963   "Sorter",
2964   "StapleLocation",
2965   "StapleOrientation",
2966   "StapleWhen",
2967   "StapleX",
2968   "StapleY",
2969 };
2970
2971 /* keep sorted when changing */
2972 static const char *finishing_group_whitelist[] = {
2973   "FPFinishing1",
2974   "FPFinishing2",
2975   "FPFinishing3",
2976   "FPFinishing4",
2977   "FinishingPage",
2978   "HPFinishingPanel",
2979 };
2980
2981 /* keep sorted when changing */
2982 static const char *cups_option_blacklist[] = {
2983   "Collate",
2984   "Copies", 
2985   "OutputOrder",
2986   "PageRegion",
2987   "PageSize",
2988 };
2989
2990 static char *
2991 get_option_text (ppd_file_t   *ppd_file, 
2992                  ppd_option_t *option)
2993 {
2994   int i;
2995   char *utf8;
2996   
2997   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
2998     {
2999       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
3000         return g_strdup (_(cups_option_translations[i].translation));
3001     }
3002
3003   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
3004
3005   /* Some ppd files have spaces in the text before the colon */
3006   g_strchomp (utf8);
3007   
3008   return utf8;
3009 }
3010
3011 static char *
3012 get_choice_text (ppd_file_t   *ppd_file, 
3013                  ppd_choice_t *choice)
3014 {
3015   int i;
3016   ppd_option_t *option = choice->option;
3017   const char *keyword = option->keyword;
3018   
3019   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
3020     {
3021       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
3022           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
3023         return g_strdup (_(cups_choice_translations[i].translation));
3024     }
3025   return ppd_text_to_utf8 (ppd_file, choice->text);
3026 }
3027
3028 static gboolean
3029 group_has_option (ppd_group_t  *group, 
3030                   ppd_option_t *option)
3031 {
3032   int i;
3033
3034   if (group == NULL)
3035     return FALSE;
3036   
3037   if (group->num_options > 0 &&
3038       option >= group->options && option < group->options + group->num_options)
3039     return TRUE;
3040   
3041   for (i = 0; i < group->num_subgroups; i++)
3042     {
3043       if (group_has_option (&group->subgroups[i],option))
3044         return TRUE;
3045     }
3046   return FALSE;
3047 }
3048
3049 static void
3050 set_option_off (GtkPrinterOption *option)
3051 {
3052   /* Any of these will do, _set only applies the value
3053    * if its allowed of the option */
3054   gtk_printer_option_set (option, "False");
3055   gtk_printer_option_set (option, "Off");
3056   gtk_printer_option_set (option, "None");
3057 }
3058
3059 static gboolean
3060 value_is_off (const char *value)
3061 {
3062   return  (strcasecmp (value, "None") == 0 ||
3063            strcasecmp (value, "Off") == 0 ||
3064            strcasecmp (value, "False") == 0);
3065 }
3066
3067 static char *
3068 ppd_group_name (ppd_group_t *group)
3069 {
3070 #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) 
3071   return group->name;
3072 #else
3073   return group->text;
3074 #endif
3075 }
3076
3077 static int
3078 available_choices (ppd_file_t     *ppd,
3079                    ppd_option_t   *option,
3080                    ppd_choice_t ***available,
3081                    gboolean        keep_if_only_one_option)
3082 {
3083   ppd_option_t *other_option;
3084   int i, j;
3085   gchar *conflicts;
3086   ppd_const_t *constraint;
3087   const char *choice, *other_choice;
3088   ppd_option_t *option1, *option2;
3089   ppd_group_t *installed_options;
3090   int num_conflicts;
3091   gboolean all_default;
3092   int add_auto;
3093
3094   if (available)
3095     *available = NULL;
3096
3097   conflicts = g_new0 (char, option->num_choices);
3098
3099   installed_options = NULL;
3100   for (i = 0; i < ppd->num_groups; i++)
3101     {
3102       char *name; 
3103
3104       name = ppd_group_name (&ppd->groups[i]);
3105       if (strcmp (name, "InstallableOptions") == 0)
3106         {
3107           installed_options = &ppd->groups[i];
3108           break;
3109         }
3110     }
3111
3112   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
3113     {
3114       option1 = ppdFindOption (ppd, constraint->option1);
3115       if (option1 == NULL)
3116         continue;
3117
3118       option2 = ppdFindOption (ppd, constraint->option2);
3119       if (option2 == NULL)
3120         continue;
3121
3122       if (option == option1)
3123         {
3124           choice = constraint->choice1;
3125           other_option = option2;
3126           other_choice = constraint->choice2;
3127         }
3128       else if (option == option2)
3129         {
3130           choice = constraint->choice2;
3131           other_option = option1;
3132           other_choice = constraint->choice1;
3133         }
3134       else
3135         continue;
3136
3137       /* We only care of conflicts with installed_options and
3138          PageSize */
3139       if (!group_has_option (installed_options, other_option) &&
3140           (strcmp (other_option->keyword, "PageSize") != 0))
3141         continue;
3142
3143       if (*other_choice == 0)
3144         {
3145           /* Conflict only if the installed option is not off */
3146           if (value_is_off (other_option->defchoice))
3147             continue;
3148         }
3149       /* Conflict if the installed option has the specified default */
3150       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
3151         continue;
3152
3153       if (*choice == 0)
3154         {
3155           /* Conflict with all non-off choices */
3156           for (j = 0; j < option->num_choices; j++)
3157             {
3158               if (!value_is_off (option->choices[j].choice))
3159                 conflicts[j] = 1;
3160             }
3161         }
3162       else
3163         {
3164           for (j = 0; j < option->num_choices; j++)
3165             {
3166               if (strcasecmp (option->choices[j].choice, choice) == 0)
3167                 conflicts[j] = 1;
3168             }
3169         }
3170     }
3171
3172   num_conflicts = 0;
3173   all_default = TRUE;
3174   for (j = 0; j < option->num_choices; j++)
3175     {
3176       if (conflicts[j])
3177         num_conflicts++;
3178       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
3179         all_default = FALSE;
3180     }
3181
3182   if ((all_default && !keep_if_only_one_option) ||
3183       (num_conflicts == option->num_choices))
3184     {
3185       g_free (conflicts);
3186
3187       return 0;
3188     }
3189
3190   /* Some ppds don't have a "use printer default" option for
3191    * InputSlot. This means you always have to select a particular slot,
3192    * and you can't auto-pick source based on the paper size. To support
3193    * this we always add an auto option if there isn't one already. If
3194    * the user chooses the generated option we don't send any InputSlot
3195    * value when printing. The way we detect existing auto-cases is based
3196    * on feedback from Michael Sweet of cups fame.
3197    */
3198   add_auto = 0;
3199   if (strcmp (option->keyword, "InputSlot") == 0)
3200     {
3201       gboolean found_auto = FALSE;
3202       for (j = 0; j < option->num_choices; j++)
3203         {
3204           if (!conflicts[j])
3205             {
3206               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
3207                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
3208                   strcmp (option->choices[j].choice, "Default") == 0 ||
3209                   strcmp (option->choices[j].choice, "None") == 0 ||
3210                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
3211                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
3212                   option->choices[j].code == NULL ||
3213                   option->choices[j].code[0] == 0)
3214                 {
3215                   found_auto = TRUE;
3216                   break;
3217                 }
3218             }
3219         }
3220
3221       if (!found_auto)
3222         add_auto = 1;
3223     }
3224   
3225   if (available)
3226     {
3227       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
3228
3229       i = 0;
3230       for (j = 0; j < option->num_choices; j++)
3231         {
3232           if (!conflicts[j])
3233             (*available)[i++] = &option->choices[j];
3234         }
3235
3236       if (add_auto) 
3237         (*available)[i++] = NULL;
3238     }
3239
3240   g_free (conflicts);
3241   
3242   return option->num_choices - num_conflicts + add_auto;
3243 }
3244
3245 static GtkPrinterOption *
3246 create_pickone_option (ppd_file_t   *ppd_file,
3247                        ppd_option_t *ppd_option,
3248                        const gchar  *gtk_name)
3249 {
3250   GtkPrinterOption *option;
3251   ppd_choice_t **available;
3252   char *label;
3253   int n_choices;
3254   int i;
3255 #ifdef HAVE_CUPS_API_1_2
3256   ppd_coption_t *coption;
3257 #endif
3258
3259   g_assert (ppd_option->ui == PPD_UI_PICKONE);
3260   
3261   option = NULL;
3262
3263   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3264   if (n_choices > 0)
3265     {
3266       
3267       /* right now only support one parameter per custom option 
3268        * if more than one print warning and only offer the default choices
3269        */
3270
3271       label = get_option_text (ppd_file, ppd_option);
3272
3273 #ifdef HAVE_CUPS_API_1_2
3274       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
3275
3276       if (coption)
3277         {
3278           ppd_cparam_t *cparam;
3279
3280           cparam = ppdFirstCustomParam (coption);
3281
3282           if (ppdNextCustomParam (coption) == NULL)
3283             {
3284               switch (cparam->type)
3285                 {
3286                 case PPD_CUSTOM_INT:
3287                   option = gtk_printer_option_new (gtk_name, label,
3288                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
3289                   break;
3290                 case PPD_CUSTOM_PASSCODE:
3291                   option = gtk_printer_option_new (gtk_name, label,
3292                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
3293                   break;
3294                 case PPD_CUSTOM_PASSWORD:
3295                     option = gtk_printer_option_new (gtk_name, label,
3296                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
3297                   break;
3298                case PPD_CUSTOM_REAL:
3299                     option = gtk_printer_option_new (gtk_name, label,
3300                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
3301                   break;
3302                 case PPD_CUSTOM_STRING:
3303                   option = gtk_printer_option_new (gtk_name, label,
3304                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
3305                   break;
3306 #ifdef PRINT_IGNORED_OPTIONS
3307                 case PPD_CUSTOM_POINTS: 
3308                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
3309                   break;
3310                 case PPD_CUSTOM_CURVE:
3311                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
3312                   break;
3313                 case PPD_CUSTOM_INVCURVE:       
3314                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
3315                   break;
3316 #endif
3317                 default: 
3318                   break;
3319                 }
3320             }
3321 #ifdef PRINT_IGNORED_OPTIONS
3322           else
3323             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
3324 #endif
3325         }
3326 #endif /* HAVE_CUPS_API_1_2 */
3327
3328       if (!option)
3329         option = gtk_printer_option_new (gtk_name, label,
3330                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
3331       g_free (label);
3332       
3333       gtk_printer_option_allocate_choices (option, n_choices);
3334       for (i = 0; i < n_choices; i++)
3335         {
3336           if (available[i] == NULL)
3337             {
3338               /* This was auto-added */
3339               option->choices[i] = g_strdup ("gtk-ignore-value");
3340               option->choices_display[i] = g_strdup (_("Printer Default"));
3341             }
3342           else
3343             {
3344               option->choices[i] = g_strdup (available[i]->choice);
3345               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
3346             }
3347         }
3348       gtk_printer_option_set (option, ppd_option->defchoice);
3349     }
3350 #ifdef PRINT_IGNORED_OPTIONS
3351   else
3352     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
3353 #endif
3354   g_free (available);
3355
3356   return option;
3357 }
3358
3359 static GtkPrinterOption *
3360 create_boolean_option (ppd_file_t   *ppd_file,
3361                        ppd_option_t *ppd_option,
3362                        const gchar  *gtk_name)
3363 {
3364   GtkPrinterOption *option;
3365   ppd_choice_t **available;
3366   char *label;
3367   int n_choices;
3368
3369   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
3370   
3371   option = NULL;
3372
3373   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3374   if (n_choices == 2)
3375     {
3376       label = get_option_text (ppd_file, ppd_option);
3377       option = gtk_printer_option_new (gtk_name, label,
3378                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
3379       g_free (label);
3380       
3381       gtk_printer_option_allocate_choices (option, 2);
3382       option->choices[0] = g_strdup ("True");
3383       option->choices_display[0] = g_strdup ("True");
3384       option->choices[1] = g_strdup ("False");
3385       option->choices_display[1] = g_strdup ("False");
3386       
3387       gtk_printer_option_set (option, ppd_option->defchoice);
3388     }
3389 #ifdef PRINT_IGNORED_OPTIONS
3390   else
3391     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
3392 #endif
3393   g_free (available);
3394
3395   return option;
3396 }
3397
3398 static gchar *
3399 get_ppd_option_name (const gchar *keyword)
3400 {
3401   int i;
3402
3403   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3404     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
3405       return g_strdup (ppd_option_names[i].name);
3406
3407   return g_strdup_printf ("cups-%s", keyword);
3408 }
3409
3410 static gchar *
3411 get_lpoption_name (const gchar *lpoption)
3412 {
3413   int i;
3414
3415   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3416     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
3417       return g_strdup (ppd_option_names[i].name);
3418
3419   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
3420     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
3421       return g_strdup (lpoption_names[i].name);
3422
3423   return g_strdup_printf ("cups-%s", lpoption);
3424 }
3425
3426 static int
3427 strptr_cmp (const void *a, 
3428             const void *b)
3429 {
3430   char **aa = (char **)a;
3431   char **bb = (char **)b;
3432   return strcmp (*aa, *bb);
3433 }
3434
3435
3436 static gboolean
3437 string_in_table (gchar       *str, 
3438                  const gchar *table[], 
3439                  gint         table_len)
3440 {
3441   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
3442 }
3443
3444 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
3445
3446 static void
3447 handle_option (GtkPrinterOptionSet *set,
3448                ppd_file_t          *ppd_file,
3449                ppd_option_t        *ppd_option,
3450                ppd_group_t         *toplevel_group,
3451                GtkPrintSettings    *settings)
3452 {
3453   GtkPrinterOption *option;
3454   char *name;
3455   int i;
3456
3457   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
3458     return;
3459
3460   name = get_ppd_option_name (ppd_option->keyword);
3461
3462   option = NULL;
3463   if (ppd_option->ui == PPD_UI_PICKONE)
3464     {
3465       option = create_pickone_option (ppd_file, ppd_option, name);
3466     }
3467   else if (ppd_option->ui == PPD_UI_BOOLEAN)
3468     {
3469       option = create_boolean_option (ppd_file, ppd_option, name);
3470     }
3471 #ifdef PRINT_IGNORED_OPTIONS
3472   else
3473     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
3474 #endif  
3475   
3476   if (option)
3477     {
3478       char *name;
3479
3480       name = ppd_group_name (toplevel_group);
3481       if (STRING_IN_TABLE (name,
3482                            color_group_whitelist) ||
3483           STRING_IN_TABLE (ppd_option->keyword,
3484                            color_option_whitelist))
3485         {
3486           option->group = g_strdup ("ColorPage");
3487         }
3488       else if (STRING_IN_TABLE (name,
3489                                 image_quality_group_whitelist) ||
3490                STRING_IN_TABLE (ppd_option->keyword,
3491                                 image_quality_option_whitelist))
3492         {
3493           option->group = g_strdup ("ImageQualityPage");
3494         }
3495       else if (STRING_IN_TABLE (name,
3496                                 finishing_group_whitelist) ||
3497                STRING_IN_TABLE (ppd_option->keyword,
3498                                 finishing_option_whitelist))
3499         {
3500           option->group = g_strdup ("FinishingPage");
3501         }
3502       else
3503         {
3504           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
3505             {
3506               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
3507                 {
3508                   option->group = g_strdup (_(cups_group_translations[i].translation));
3509                   break;
3510                 }
3511             }
3512
3513           if (i == G_N_ELEMENTS (cups_group_translations))
3514             option->group = g_strdup (toplevel_group->text);
3515         }
3516
3517       set_option_from_settings (option, settings);
3518       
3519       gtk_printer_option_set_add (set, option);
3520     }
3521   
3522   g_free (name);
3523 }
3524
3525 static void
3526 handle_group (GtkPrinterOptionSet *set,
3527               ppd_file_t          *ppd_file,
3528               ppd_group_t         *group,
3529               ppd_group_t         *toplevel_group,
3530               GtkPrintSettings    *settings)
3531 {
3532   gint i;
3533   gchar *name;
3534   
3535   /* Ignore installable options */
3536   name = ppd_group_name (toplevel_group);
3537   if (strcmp (name, "InstallableOptions") == 0)
3538     return;
3539   
3540   for (i = 0; i < group->num_options; i++)
3541     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
3542
3543   for (i = 0; i < group->num_subgroups; i++)
3544     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
3545
3546 }
3547
3548 #ifdef HAVE_COLORD
3549
3550 typedef struct {
3551         GtkPrintSettings     *settings;
3552         GtkPrinter           *printer;
3553 } GtkPrintBackendCupsColordHelper;
3554
3555 static void
3556 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
3557                                       GtkPrintBackendCupsColordHelper *helper)
3558 {
3559   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
3560                                     helper->settings,
3561                                     set);
3562 }
3563 #endif
3564
3565 static GtkPrinterOptionSet *
3566 cups_printer_get_options (GtkPrinter           *printer,
3567                           GtkPrintSettings     *settings,
3568                           GtkPageSetup         *page_setup,
3569                           GtkPrintCapabilities  capabilities)
3570 {
3571   GtkPrinterOptionSet *set;
3572   GtkPrinterOption *option;
3573   ppd_file_t *ppd_file;
3574   int i;
3575   char *print_at[] = { "now", "at", "on-hold" };
3576   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
3577   char *prio[] = {"100", "80", "50", "30" };
3578   /* Translators: These strings name the possible values of the 
3579    * job priority option in the print dialog
3580    */
3581   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
3582   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
3583   /* Translators: These strings name the possible arrangements of
3584    * multiple pages on a sheet when printing
3585    */
3586   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"), 
3587                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"), 
3588                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"), 
3589                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
3590   char *name;
3591   int num_opts;
3592   cups_option_t *opts = NULL;
3593   GtkPrintBackendCups *backend;
3594   GtkTextDirection text_direction;
3595   GtkPrinterCups *cups_printer = NULL;
3596 #ifdef HAVE_COLORD
3597   GtkPrintBackendCupsColordHelper *helper;
3598 #endif
3599
3600   set = gtk_printer_option_set_new ();
3601
3602   /* Cups specific, non-ppd related settings */
3603
3604    /* Translators, this string is used to label the pages-per-sheet option 
3605     * in the print dialog 
3606     */
3607   option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3608   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
3609                                          n_up, n_up);
3610   gtk_printer_option_set (option, "1");
3611   set_option_from_settings (option, settings);
3612   gtk_printer_option_set_add (set, option);
3613   g_object_unref (option);
3614
3615   if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
3616     {
3617       for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
3618         n_up_layout_display[i] = _(n_up_layout_display[i]);
3619   
3620        /* Translators, this string is used to label the option in the print 
3621         * dialog that controls in what order multiple pages are arranged 
3622         */
3623       option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3624       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
3625                                              n_up_layout, n_up_layout_display);
3626
3627       text_direction = gtk_widget_get_default_direction ();
3628       if (text_direction == GTK_TEXT_DIR_LTR)
3629         gtk_printer_option_set (option, "lrtb");
3630       else
3631         gtk_printer_option_set (option, "rltb");
3632
3633       set_option_from_settings (option, settings);
3634       gtk_printer_option_set_add (set, option);
3635       g_object_unref (option);
3636     }
3637
3638   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
3639     prio_display[i] = _(prio_display[i]);
3640   
3641   /* Translators, this string is used to label the job priority option 
3642    * in the print dialog 
3643    */
3644   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3645   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
3646                                          prio, prio_display);
3647   gtk_printer_option_set (option, "50");
3648   set_option_from_settings (option, settings);
3649   gtk_printer_option_set_add (set, option);
3650   g_object_unref (option);
3651
3652   /* Translators, this string is used to label the billing info entry
3653    * in the print dialog 
3654    */
3655   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
3656   gtk_printer_option_set (option, "");
3657   set_option_from_settings (option, settings);
3658   gtk_printer_option_set_add (set, option);
3659   g_object_unref (option);
3660
3661   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
3662   cups_printer = GTK_PRINTER_CUPS (printer);
3663
3664   if (backend != NULL && printer != NULL)
3665     {
3666       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
3667       /* Translators, these strings are names for various 'standard' cover 
3668        * pages that the printing system may support.
3669        */
3670       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
3671       char **cover = NULL;
3672       char **cover_display = NULL;
3673       char **cover_display_translated = NULL;
3674       gint num_of_covers = 0;
3675       gpointer value;
3676       gint j;
3677
3678       num_of_covers = backend->number_of_covers;
3679       cover = g_new (char *, num_of_covers + 1);
3680       cover[num_of_covers] = NULL;
3681       cover_display = g_new (char *, num_of_covers + 1);
3682       cover_display[num_of_covers] = NULL;
3683       cover_display_translated = g_new (char *, num_of_covers + 1);
3684       cover_display_translated[num_of_covers] = NULL;
3685
3686       for (i = 0; i < num_of_covers; i++)
3687         {
3688           cover[i] = g_strdup (backend->covers[i]);
3689           value = NULL;
3690           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
3691             if (strcmp (cover_default[j], cover[i]) == 0)
3692               {
3693                 value = cover_display_default[j];
3694                 break;
3695               }
3696           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
3697         }
3698
3699       for (i = 0; i < num_of_covers; i++)
3700         cover_display_translated[i] = _(cover_display[i]);
3701   
3702       /* Translators, this is the label used for the option in the print 
3703        * dialog that controls the front cover page.
3704        */
3705       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3706       gtk_printer_option_choices_from_array (option, num_of_covers,
3707                                          cover, cover_display_translated);
3708
3709       if (cups_printer->default_cover_before != NULL)
3710         gtk_printer_option_set (option, cups_printer->default_cover_before);
3711       else
3712         gtk_printer_option_set (option, "none");
3713       set_option_from_settings (option, settings);
3714       gtk_printer_option_set_add (set, option);
3715       g_object_unref (option);
3716
3717       /* Translators, this is the label used for the option in the print 
3718        * dialog that controls the back cover page.
3719        */
3720       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3721       gtk_printer_option_choices_from_array (option, num_of_covers,
3722                                          cover, cover_display_translated);
3723       if (cups_printer->default_cover_after != NULL)
3724         gtk_printer_option_set (option, cups_printer->default_cover_after);
3725       else
3726         gtk_printer_option_set (option, "none");
3727       set_option_from_settings (option, settings);
3728       gtk_printer_option_set_add (set, option);
3729       g_object_unref (option);
3730
3731       g_strfreev (cover);
3732       g_strfreev (cover_display);
3733       g_free (cover_display_translated);
3734     }
3735
3736   /* Translators: this is the name of the option that controls when
3737    * a print job is printed. Possible values are 'now', a specified time,
3738    * or 'on hold'
3739    */
3740   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3741   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
3742                                          print_at, print_at);
3743   gtk_printer_option_set (option, "now");
3744   set_option_from_settings (option, settings);
3745   gtk_printer_option_set_add (set, option);
3746   g_object_unref (option);
3747   
3748   /* Translators: this is the name of the option that allows the user
3749    * to specify a time when a print job will be printed.
3750    */
3751   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
3752   gtk_printer_option_set (option, "");
3753   set_option_from_settings (option, settings);
3754   gtk_printer_option_set_add (set, option);
3755   g_object_unref (option);
3756   
3757   /* Printer (ppd) specific settings */
3758   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3759   if (ppd_file)
3760     {
3761       GtkPaperSize *paper_size;
3762       ppd_option_t *option;
3763       const gchar  *ppd_name;
3764
3765       ppdMarkDefaults (ppd_file);
3766
3767       paper_size = gtk_page_setup_get_paper_size (page_setup);
3768
3769       option = ppdFindOption (ppd_file, "PageSize");
3770       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
3771       
3772       if (ppd_name)
3773         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
3774       else
3775         {
3776           gchar *custom_name;
3777           char width[G_ASCII_DTOSTR_BUF_SIZE];
3778           char height[G_ASCII_DTOSTR_BUF_SIZE];
3779
3780           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3781           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3782           /* Translators: this format is used to display a custom paper
3783            * size. The two placeholders are replaced with the width and height
3784            * in points. E.g: "Custom 230.4x142.9"
3785            */
3786           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
3787           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
3788           g_free (custom_name);
3789         }
3790
3791       for (i = 0; i < ppd_file->num_groups; i++)
3792         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
3793     }
3794
3795   /* Now honor the user set defaults for this printer */
3796   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
3797
3798   for (i = 0; i < num_opts; i++)
3799     {
3800       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
3801         continue;
3802
3803       name = get_lpoption_name (opts[i].name);
3804       if (strcmp (name, "cups-job-sheets") == 0)
3805         {
3806           gchar **values;
3807           gint    num_values;
3808           
3809           values = g_strsplit (opts[i].value, ",", 2);
3810           num_values = g_strv_length (values);
3811
3812           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
3813           if (option && num_values > 0)
3814             gtk_printer_option_set (option, g_strstrip (values[0]));
3815
3816           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
3817           if (option && num_values > 1)
3818             gtk_printer_option_set (option, g_strstrip (values[1]));
3819
3820           g_strfreev (values);
3821         }
3822       else if (strcmp (name, "cups-job-hold-until") == 0)
3823         {
3824           GtkPrinterOption *option2 = NULL;
3825
3826           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
3827           if (option && opts[i].value)
3828             {
3829               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
3830               if (option2)
3831                 {
3832                   if (strcmp (opts[i].value, "indefinite") == 0)
3833                     gtk_printer_option_set (option2, "on-hold");
3834                   else
3835                     {
3836                       gtk_printer_option_set (option2, "at");
3837                       gtk_printer_option_set (option, opts[i].value);
3838                     }
3839                 }
3840             }
3841         }
3842       else if (strcmp (name, "cups-sides") == 0)
3843         {
3844           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
3845           if (option && opts[i].value)
3846             {
3847               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
3848                 gtk_printer_option_set (option, "DuplexTumble");
3849               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
3850                 gtk_printer_option_set (option, "DuplexNoTumble");
3851             }
3852         }
3853       else
3854         {
3855           option = gtk_printer_option_set_lookup (set, name);
3856           if (option)
3857             gtk_printer_option_set (option, opts[i].value);
3858         }
3859       g_free (name);
3860     }
3861
3862   cupsFreeOptions (num_opts, opts);
3863
3864 #ifdef HAVE_COLORD
3865   /* TRANSLATORS: this this the ICC color profile to use for this job */
3866   option = gtk_printer_option_new ("colord-profile",
3867                                    _("Printer Profile"),
3868                                    GTK_PRINTER_OPTION_TYPE_INFO);
3869
3870   /* assign it to the color page */
3871   option->group = g_strdup ("ColorPage");
3872
3873   /* TRANSLATORS: this is when color profile information is unavailable */
3874   gtk_printer_option_set (option, _("Unavailable"));
3875   gtk_printer_option_set_add (set, option);
3876
3877   /* watch to see if the user changed the options */
3878   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
3879   helper->printer = printer;
3880   helper->settings = settings;
3881   g_signal_connect_data (set, "changed",
3882                          G_CALLBACK (colord_printer_option_set_changed_cb),
3883                          helper,
3884                          (GClosureNotify) g_free,
3885                          0);
3886
3887   /* initial coldplug */
3888   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
3889                                     settings, set);
3890   g_object_bind_property (printer, "profile-title",
3891                           option, "value",
3892                           G_BINDING_DEFAULT);
3893
3894 #endif
3895
3896   return set;
3897 }
3898
3899
3900 static void
3901 mark_option_from_set (GtkPrinterOptionSet *set,
3902                       ppd_file_t          *ppd_file,
3903                       ppd_option_t        *ppd_option)
3904 {
3905   GtkPrinterOption *option;
3906   char *name = get_ppd_option_name (ppd_option->keyword);
3907
3908   option = gtk_printer_option_set_lookup (set, name);
3909
3910   if (option)
3911     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
3912   
3913   g_free (name);
3914 }
3915
3916
3917 static void
3918 mark_group_from_set (GtkPrinterOptionSet *set,
3919                      ppd_file_t          *ppd_file,
3920                      ppd_group_t         *group)
3921 {
3922   int i;
3923
3924   for (i = 0; i < group->num_options; i++)
3925     mark_option_from_set (set, ppd_file, &group->options[i]);
3926
3927   for (i = 0; i < group->num_subgroups; i++)
3928     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
3929 }
3930
3931 static void
3932 set_conflicts_from_option (GtkPrinterOptionSet *set,
3933                            ppd_file_t          *ppd_file,
3934                            ppd_option_t        *ppd_option)
3935 {
3936   GtkPrinterOption *option;
3937   char *name;
3938
3939   if (ppd_option->conflicted)
3940     {
3941       name = get_ppd_option_name (ppd_option->keyword);
3942       option = gtk_printer_option_set_lookup (set, name);
3943
3944       if (option)
3945         gtk_printer_option_set_has_conflict (option, TRUE);
3946 #ifdef PRINT_IGNORED_OPTIONS
3947       else
3948         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
3949 #endif
3950       
3951       g_free (name);
3952     }
3953 }
3954
3955 static void
3956 set_conflicts_from_group (GtkPrinterOptionSet *set,
3957                           ppd_file_t          *ppd_file,
3958                           ppd_group_t         *group)
3959 {
3960   int i;
3961
3962   for (i = 0; i < group->num_options; i++)
3963     set_conflicts_from_option (set, ppd_file, &group->options[i]);
3964
3965   for (i = 0; i < group->num_subgroups; i++)
3966     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
3967 }
3968
3969 static gboolean
3970 cups_printer_mark_conflicts (GtkPrinter          *printer,
3971                              GtkPrinterOptionSet *options)
3972 {
3973   ppd_file_t *ppd_file;
3974   int num_conflicts;
3975   int i;
3976  
3977   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3978
3979   if (ppd_file == NULL)
3980     return FALSE;
3981
3982   ppdMarkDefaults (ppd_file);
3983
3984   for (i = 0; i < ppd_file->num_groups; i++)
3985     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
3986
3987   num_conflicts = ppdConflicts (ppd_file);
3988
3989   if (num_conflicts > 0)
3990     {
3991       for (i = 0; i < ppd_file->num_groups; i++)
3992         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
3993     }
3994  
3995   return num_conflicts > 0;
3996 }
3997
3998 struct OptionData {
3999   GtkPrinter *printer;
4000   GtkPrinterOptionSet *options;
4001   GtkPrintSettings *settings;
4002   ppd_file_t *ppd_file;
4003 };
4004
4005 typedef struct {
4006   const char *cups;
4007   const char *standard;
4008 } NameMapping;
4009
4010 static void
4011 map_settings_to_option (GtkPrinterOption  *option,
4012                         const NameMapping  table[],
4013                         gint               n_elements,
4014                         GtkPrintSettings  *settings,
4015                         const gchar       *standard_name,
4016                         const gchar       *cups_name)
4017 {
4018   int i;
4019   char *name;
4020   const char *cups_value;
4021   const char *standard_value;
4022
4023   /* If the cups-specific setting is set, always use that */
4024   name = g_strdup_printf ("cups-%s", cups_name);
4025   cups_value = gtk_print_settings_get (settings, name);
4026   g_free (name);
4027   
4028   if (cups_value != NULL) 
4029     {
4030       gtk_printer_option_set (option, cups_value);
4031       return;
4032     }
4033
4034   /* Otherwise we try to convert from the general setting */
4035   standard_value = gtk_print_settings_get (settings, standard_name);
4036   if (standard_value == NULL)
4037     return;
4038
4039   for (i = 0; i < n_elements; i++)
4040     {
4041       if (table[i].cups == NULL && table[i].standard == NULL)
4042         {
4043           gtk_printer_option_set (option, standard_value);
4044           break;
4045         }
4046       else if (table[i].cups == NULL &&
4047                strcmp (table[i].standard, standard_value) == 0)
4048         {
4049           set_option_off (option);
4050           break;
4051         }
4052       else if (strcmp (table[i].standard, standard_value) == 0)
4053         {
4054           gtk_printer_option_set (option, table[i].cups);
4055           break;
4056         }
4057     }
4058 }
4059
4060 static void
4061 map_option_to_settings (const gchar       *value,
4062                         const NameMapping  table[],
4063                         gint               n_elements,
4064                         GtkPrintSettings  *settings,
4065                         const gchar       *standard_name,
4066                         const gchar       *cups_name)
4067 {
4068   int i;
4069   char *name;
4070
4071   for (i = 0; i < n_elements; i++)
4072     {
4073       if (table[i].cups == NULL && table[i].standard == NULL)
4074         {
4075           gtk_print_settings_set (settings,
4076                                   standard_name,
4077                                   value);
4078           break;
4079         }
4080       else if (table[i].cups == NULL && table[i].standard != NULL)
4081         {
4082           if (value_is_off (value))
4083             {
4084               gtk_print_settings_set (settings,
4085                                       standard_name,
4086                                       table[i].standard);
4087               break;
4088             }
4089         }
4090       else if (strcmp (table[i].cups, value) == 0)
4091         {
4092           gtk_print_settings_set (settings,
4093                                   standard_name,
4094                                   table[i].standard);
4095           break;
4096         }
4097     }
4098
4099   /* Always set the corresponding cups-specific setting */
4100   name = g_strdup_printf ("cups-%s", cups_name);
4101   gtk_print_settings_set (settings, name, value);
4102   g_free (name);
4103 }
4104
4105
4106 static const NameMapping paper_source_map[] = {
4107   { "Lower", "lower"},
4108   { "Middle", "middle"},
4109   { "Upper", "upper"},
4110   { "Rear", "rear"},
4111   { "Envelope", "envelope"},
4112   { "Cassette", "cassette"},
4113   { "LargeCapacity", "large-capacity"},
4114   { "AnySmallFormat", "small-format"},
4115   { "AnyLargeFormat", "large-format"},
4116   { NULL, NULL}
4117 };
4118
4119 static const NameMapping output_tray_map[] = {
4120   { "Upper", "upper"},
4121   { "Lower", "lower"},
4122   { "Rear", "rear"},
4123   { NULL, NULL}
4124 };
4125
4126 static const NameMapping duplex_map[] = {
4127   { "DuplexTumble", "vertical" },
4128   { "DuplexNoTumble", "horizontal" },
4129   { NULL, "simplex" }
4130 };
4131
4132 static const NameMapping output_mode_map[] = {
4133   { "Standard", "normal" },
4134   { "Normal", "normal" },
4135   { "Draft", "draft" },
4136   { "Fast", "draft" },
4137 };
4138
4139 static const NameMapping media_type_map[] = {
4140   { "Transparency", "transparency"},
4141   { "Standard", "stationery"},
4142   { NULL, NULL}
4143 };
4144
4145 static const NameMapping all_map[] = {
4146   { NULL, NULL}
4147 };
4148
4149
4150 static void
4151 set_option_from_settings (GtkPrinterOption *option,
4152                           GtkPrintSettings *settings)
4153 {
4154   const char *cups_value;
4155   char *value;
4156   
4157   if (settings == NULL)
4158     return;
4159
4160   if (strcmp (option->name, "gtk-paper-source") == 0)
4161     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
4162                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4163   else if (strcmp (option->name, "gtk-output-tray") == 0)
4164     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
4165                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4166   else if (strcmp (option->name, "gtk-duplex") == 0)
4167     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
4168                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4169   else if (strcmp (option->name, "cups-OutputMode") == 0)
4170     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
4171                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4172   else if (strcmp (option->name, "cups-Resolution") == 0)
4173     {
4174       cups_value = gtk_print_settings_get (settings, option->name);
4175       if (cups_value)
4176         gtk_printer_option_set (option, cups_value);
4177       else
4178         {
4179           if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
4180               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
4181               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
4182               option->value == NULL || option->value[0] == '\0')
4183             {
4184               int res = gtk_print_settings_get_resolution (settings);
4185               int res_x = gtk_print_settings_get_resolution_x (settings);
4186               int res_y = gtk_print_settings_get_resolution_y (settings);
4187
4188               if (res_x != res_y)
4189                 {
4190                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
4191                   gtk_printer_option_set (option, value);
4192                   g_free (value);
4193                 }
4194               else if (res != 0)
4195                 {
4196                   value = g_strdup_printf ("%ddpi", res);
4197                   gtk_printer_option_set (option, value);
4198                   g_free (value);
4199                 }
4200             }
4201         }
4202     }
4203   else if (strcmp (option->name, "gtk-paper-type") == 0)
4204     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
4205                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4206   else if (strcmp (option->name, "gtk-n-up") == 0)
4207     {
4208       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4209                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4210     }
4211   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4212     {
4213       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4214                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4215     }
4216   else if (strcmp (option->name, "gtk-billing-info") == 0)
4217     {
4218       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
4219       if (cups_value)
4220         gtk_printer_option_set (option, cups_value);
4221     } 
4222   else if (strcmp (option->name, "gtk-job-prio") == 0)
4223     {
4224       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
4225       if (cups_value)
4226         gtk_printer_option_set (option, cups_value);
4227     } 
4228   else if (strcmp (option->name, "gtk-cover-before") == 0)
4229     {
4230       cups_value = gtk_print_settings_get (settings, "cover-before");
4231       if (cups_value)
4232         gtk_printer_option_set (option, cups_value);
4233     } 
4234   else if (strcmp (option->name, "gtk-cover-after") == 0)
4235     {
4236       cups_value = gtk_print_settings_get (settings, "cover-after");
4237       if (cups_value)
4238         gtk_printer_option_set (option, cups_value);
4239     } 
4240   else if (strcmp (option->name, "gtk-print-time") == 0)
4241     {
4242       cups_value = gtk_print_settings_get (settings, "print-at");
4243       if (cups_value)
4244         gtk_printer_option_set (option, cups_value);
4245     } 
4246   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4247     {
4248       cups_value = gtk_print_settings_get (settings, "print-at-time");
4249       if (cups_value)
4250         gtk_printer_option_set (option, cups_value);
4251     } 
4252   else if (g_str_has_prefix (option->name, "cups-"))
4253     {
4254       cups_value = gtk_print_settings_get (settings, option->name);
4255       if (cups_value)
4256         gtk_printer_option_set (option, cups_value);
4257     } 
4258 }
4259
4260 static void
4261 foreach_option_get_settings (GtkPrinterOption *option,
4262                              gpointer          user_data)
4263 {
4264   struct OptionData *data = user_data;
4265   GtkPrintSettings *settings = data->settings;
4266   const char *value;
4267
4268   value = option->value;
4269
4270   if (strcmp (option->name, "gtk-paper-source") == 0)
4271     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
4272                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4273   else if (strcmp (option->name, "gtk-output-tray") == 0)
4274     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
4275                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4276   else if (strcmp (option->name, "gtk-duplex") == 0)
4277     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
4278                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4279   else if (strcmp (option->name, "cups-OutputMode") == 0)
4280     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
4281                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4282   else if (strcmp (option->name, "cups-Resolution") == 0)
4283     {
4284       int res, res_x, res_y;
4285
4286       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
4287         {
4288           if (res_x > 0 && res_y > 0)
4289             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
4290         }
4291       else if (sscanf (value, "%ddpi", &res) == 1)
4292         {
4293           if (res > 0)
4294             gtk_print_settings_set_resolution (settings, res);
4295         }
4296
4297       gtk_print_settings_set (settings, option->name, value);
4298     }
4299   else if (strcmp (option->name, "gtk-paper-type") == 0)
4300     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
4301                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4302   else if (strcmp (option->name, "gtk-n-up") == 0)
4303     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4304                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4305   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4306     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4307                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4308   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
4309     gtk_print_settings_set (settings, "cups-job-billing", value);
4310   else if (strcmp (option->name, "gtk-job-prio") == 0)
4311     gtk_print_settings_set (settings, "cups-job-priority", value);
4312   else if (strcmp (option->name, "gtk-cover-before") == 0)
4313     gtk_print_settings_set (settings, "cover-before", value);
4314   else if (strcmp (option->name, "gtk-cover-after") == 0)
4315     gtk_print_settings_set (settings, "cover-after", value);
4316   else if (strcmp (option->name, "gtk-print-time") == 0)
4317     gtk_print_settings_set (settings, "print-at", value);
4318   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4319     gtk_print_settings_set (settings, "print-at-time", value);
4320   else if (g_str_has_prefix (option->name, "cups-"))
4321     gtk_print_settings_set (settings, option->name, value);
4322 }
4323
4324 static gboolean
4325 supports_am_pm (void)
4326 {
4327   struct tm tmp_tm = { 0 };
4328   char   time[8];
4329   int    length;
4330
4331   length = strftime (time, sizeof (time), "%p", &tmp_tm);
4332
4333   return length != 0;
4334 }
4335
4336 /* Converts local time to UTC time. Local time has to be in one of these
4337  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
4338  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
4339  * Returns a newly allocated string holding UTC time in HH:MM:SS format
4340  * or NULL.
4341  */
4342 gchar *
4343 localtime_to_utctime (const char *local_time)
4344 {
4345   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
4346                              " %H : %M : %S ",
4347                              " %I : %M %p ", " %p %I : %M ",
4348                              " %H : %M ",
4349                              " %I %p ", " %p %I "};
4350   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
4351   const char *end = NULL;
4352   struct tm  *actual_local_time;
4353   struct tm  *actual_utc_time;
4354   struct tm   local_print_time;
4355   struct tm   utc_print_time;
4356   struct tm   diff_time;
4357   gchar      *utc_time = NULL;
4358   int         i, n;
4359
4360   if (local_time == NULL || local_time[0] == '\0')
4361     return NULL;
4362
4363   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
4364
4365   for (i = 0; i < n; i++)
4366     {
4367       local_print_time.tm_hour = 0;
4368       local_print_time.tm_min  = 0;
4369       local_print_time.tm_sec  = 0;
4370
4371       if (supports_am_pm ())
4372         end = strptime (local_time, formats_0[i], &local_print_time);
4373       else
4374         end = strptime (local_time, formats_1[i], &local_print_time);
4375
4376       if (end != NULL && end[0] == '\0')
4377         break;
4378     }
4379
4380   if (end != NULL && end[0] == '\0')
4381     {
4382       time_t rawtime;
4383       time (&rawtime);
4384
4385       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
4386       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
4387
4388       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
4389       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
4390       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
4391
4392       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
4393       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
4394       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
4395
4396       utc_time = g_strdup_printf ("%02d:%02d:%02d",
4397                                   utc_print_time.tm_hour,
4398                                   utc_print_time.tm_min,
4399                                   utc_print_time.tm_sec);
4400     }
4401
4402   return utc_time;
4403 }
4404
4405 static void
4406 cups_printer_get_settings_from_options (GtkPrinter          *printer,
4407                                         GtkPrinterOptionSet *options,
4408                                         GtkPrintSettings    *settings)
4409 {
4410   struct OptionData data;
4411   const char *print_at, *print_at_time;
4412
4413   data.printer = printer;
4414   data.options = options;
4415   data.settings = settings;
4416   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4417  
4418   if (data.ppd_file != NULL)
4419     {
4420       GtkPrinterOption *cover_before, *cover_after;
4421       
4422       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
4423
4424       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
4425       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
4426       if (cover_before && cover_after)
4427         {
4428           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
4429           gtk_print_settings_set (settings, "cups-job-sheets", value);
4430           g_free (value);
4431         }
4432
4433       print_at = gtk_print_settings_get (settings, "print-at");
4434       print_at_time = gtk_print_settings_get (settings, "print-at-time");
4435
4436       if (strcmp (print_at, "at") == 0)
4437         {
4438           gchar *utc_time = NULL;
4439           
4440           utc_time = localtime_to_utctime (print_at_time);
4441
4442           if (utc_time != NULL)
4443             {
4444               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
4445               g_free (utc_time);
4446             }
4447           else
4448             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
4449         }
4450       else if (strcmp (print_at, "on-hold") == 0)
4451         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
4452     }
4453 }
4454
4455 static void
4456 cups_printer_prepare_for_print (GtkPrinter       *printer,
4457                                 GtkPrintJob      *print_job,
4458                                 GtkPrintSettings *settings,
4459                                 GtkPageSetup     *page_setup)
4460 {
4461   GtkPrintPages pages;
4462   GtkPageRange *ranges;
4463   gint n_ranges;
4464   GtkPageSet page_set;
4465   GtkPaperSize *paper_size;
4466   const char *ppd_paper_name;
4467   double scale;
4468
4469   pages = gtk_print_settings_get_print_pages (settings);
4470   gtk_print_job_set_pages (print_job, pages);
4471
4472   if (pages == GTK_PRINT_PAGES_RANGES)
4473     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
4474   else
4475     {
4476       ranges = NULL;
4477       n_ranges = 0;
4478     }
4479
4480   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
4481   if (gtk_print_settings_get_collate (settings))
4482     gtk_print_settings_set (settings, "cups-Collate", "True");
4483   gtk_print_job_set_collate (print_job, FALSE);
4484
4485   if (gtk_print_settings_get_reverse (settings))
4486     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
4487   gtk_print_job_set_reverse (print_job, FALSE);
4488
4489   if (gtk_print_settings_get_n_copies (settings) > 1)
4490     gtk_print_settings_set_int (settings, "cups-copies",
4491                                 gtk_print_settings_get_n_copies (settings));
4492   gtk_print_job_set_num_copies (print_job, 1);
4493
4494   scale = gtk_print_settings_get_scale (settings);
4495   if (scale != 100.0)
4496     gtk_print_job_set_scale (print_job, scale / 100.0);
4497
4498   page_set = gtk_print_settings_get_page_set (settings);
4499   if (page_set == GTK_PAGE_SET_EVEN)
4500     gtk_print_settings_set (settings, "cups-page-set", "even");
4501   else if (page_set == GTK_PAGE_SET_ODD)
4502     gtk_print_settings_set (settings, "cups-page-set", "odd");
4503   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
4504
4505   paper_size = gtk_page_setup_get_paper_size (page_setup);
4506   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
4507   if (ppd_paper_name != NULL)
4508     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
4509   else
4510     {
4511       char width[G_ASCII_DTOSTR_BUF_SIZE];
4512       char height[G_ASCII_DTOSTR_BUF_SIZE];
4513       char *custom_name;
4514
4515       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4516       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4517       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
4518       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
4519       g_free (custom_name);
4520     }
4521
4522   if (gtk_print_settings_get_number_up (settings) > 1)
4523     {
4524       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
4525       GEnumClass        *enum_class;
4526       GEnumValue        *enum_value;
4527
4528       switch (gtk_page_setup_get_orientation (page_setup))
4529         {
4530           case GTK_PAGE_ORIENTATION_PORTRAIT:
4531             break;
4532           case GTK_PAGE_ORIENTATION_LANDSCAPE:
4533             if (layout < 4)
4534               layout = layout + 2 + 4 * (1 - layout / 2);
4535             else
4536               layout = layout - 3 - 2 * (layout % 2);
4537             break;
4538           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4539             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
4540             break;
4541           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4542             if (layout < 4)
4543               layout = layout + 5 - 2 * (layout % 2);
4544             else
4545               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
4546             break;
4547         }
4548
4549       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
4550       enum_value = g_enum_get_value (enum_class, layout);
4551       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
4552       g_type_class_unref (enum_class);
4553     }
4554
4555   gtk_print_job_set_rotate (print_job, TRUE);
4556 }
4557
4558 static GtkPageSetup *
4559 create_page_setup (ppd_file_t *ppd_file,
4560                    ppd_size_t *size)
4561  {
4562    char *display_name;
4563    GtkPageSetup *page_setup;
4564    GtkPaperSize *paper_size;
4565    ppd_option_t *option;
4566    ppd_choice_t *choice;
4567
4568   display_name = NULL;
4569   option = ppdFindOption (ppd_file, "PageSize");
4570   if (option)
4571     {
4572       choice = ppdFindChoice (option, size->name);
4573       if (choice)
4574         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
4575     }
4576
4577   if (display_name == NULL)
4578     display_name = g_strdup (size->name);
4579   
4580   page_setup = gtk_page_setup_new ();
4581   paper_size = gtk_paper_size_new_from_ppd (size->name,
4582                                             display_name,
4583                                             size->width,
4584                                             size->length);
4585   gtk_page_setup_set_paper_size (page_setup, paper_size);
4586   gtk_paper_size_free (paper_size);
4587   
4588   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
4589   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
4590   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
4591   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
4592   
4593   g_free (display_name);
4594
4595   return page_setup;
4596 }
4597
4598 static GList *
4599 cups_printer_list_papers (GtkPrinter *printer)
4600 {
4601   ppd_file_t *ppd_file;
4602   ppd_size_t *size;
4603   GtkPageSetup *page_setup;
4604   GList *l;
4605   int i;
4606
4607   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4608   if (ppd_file == NULL)
4609     return NULL;
4610
4611   l = NULL;
4612   
4613   for (i = 0; i < ppd_file->num_sizes; i++)
4614     {
4615       size = &ppd_file->sizes[i];      
4616
4617       page_setup = create_page_setup (ppd_file, size);
4618
4619       l = g_list_prepend (l, page_setup);
4620     }
4621
4622   return g_list_reverse (l);
4623 }
4624
4625 static GtkPageSetup *
4626 cups_printer_get_default_page_size (GtkPrinter *printer)
4627 {
4628   ppd_file_t *ppd_file;
4629   ppd_size_t *size;
4630   ppd_option_t *option;
4631
4632
4633   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4634   if (ppd_file == NULL)
4635     return NULL;
4636
4637   option = ppdFindOption (ppd_file, "PageSize");
4638   size = ppdPageSize (ppd_file, option->defchoice); 
4639
4640   return create_page_setup (ppd_file, size);
4641 }
4642
4643 static gboolean
4644 cups_printer_get_hard_margins (GtkPrinter *printer,
4645                                gdouble    *top,
4646                                gdouble    *bottom,
4647                                gdouble    *left,
4648                                gdouble    *right)
4649 {
4650   ppd_file_t *ppd_file;
4651
4652   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4653   if (ppd_file == NULL)
4654     return FALSE;
4655
4656   *left = ppd_file->custom_margins[0];
4657   *bottom = ppd_file->custom_margins[1];
4658   *right = ppd_file->custom_margins[2];
4659   *top = ppd_file->custom_margins[3];
4660
4661   return TRUE;
4662 }
4663
4664 static GtkPrintCapabilities
4665 cups_printer_get_capabilities (GtkPrinter *printer)
4666 {
4667   return
4668     GTK_PRINT_CAPABILITY_COPIES |
4669     GTK_PRINT_CAPABILITY_COLLATE |
4670     GTK_PRINT_CAPABILITY_REVERSE |
4671 #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
4672     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
4673 #endif
4674     GTK_PRINT_CAPABILITY_NUMBER_UP;
4675 }