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