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