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