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