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