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