]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
printing: Use DBus calls instead of Avahi API
[~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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef __linux__
21 #define _GNU_SOURCE
22 #endif
23
24 #include "config.h"
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <stdlib.h>
30 #include <time.h>
31 /* Cups 1.6 deprecates ppdFindAttr(), ppdFindCustomOption(),
32  * ppdFirstCustomParam(), and ppdNextCustomParam() among others. This
33  * turns off the warning so that it will compile.
34  */
35 #ifdef HAVE_CUPS_API_1_6
36 # define _PPD_DEPRECATED
37 #endif
38
39 #include <cups/cups.h>
40 #include <cups/language.h>
41 #include <cups/http.h>
42 #include <cups/ipp.h>
43 #include <errno.h>
44 #include <cairo.h>
45 #include <cairo-pdf.h>
46 #include <cairo-ps.h>
47
48 #include <glib/gstdio.h>
49 #include <glib/gi18n-lib.h>
50 #include <gmodule.h>
51
52 #include <gtk/gtk.h>
53 #include <gtk/gtkprintbackend.h>
54 #include <gtk/gtkunixprint.h>
55 #include <gtk/gtkprinter-private.h>
56
57 #include "gtkprintbackendcups.h"
58 #include "gtkprintercups.h"
59
60 #include "gtkcupsutils.h"
61
62 #ifdef HAVE_COLORD
63 #include <colord.h>
64 #endif
65
66 typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
67
68 #define GTK_PRINT_BACKEND_CUPS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
69 #define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
70 #define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
71
72 #define _CUPS_MAX_ATTEMPTS 10
73 #define _CUPS_MAX_CHUNK_SIZE 8192
74
75 #ifdef HAVE_CUPS_API_1_6
76 #define AVAHI_IF_UNSPEC -1
77 #define AVAHI_PROTO_INET 0
78 #define AVAHI_PROTO_INET6 1
79 #define AVAHI_PROTO_UNSPEC -1
80
81 #define AVAHI_BUS "org.freedesktop.Avahi"
82 #define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server"
83 #define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
84 #define AVAHI_SERVICE_RESOLVER_IFACE "org.freedesktop.Avahi.ServiceResolver"
85 #endif
86
87 /* define this to see warnings about ignored ppd options */
88 #undef PRINT_IGNORED_OPTIONS
89
90 #define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
91 #define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
92
93 static GType print_backend_cups_type = 0;
94
95 typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
96                                                    GtkCupsResult   *result,
97                                                    gpointer         user_data);
98
99 typedef enum
100 {
101   DISPATCH_SETUP,
102   DISPATCH_REQUEST,
103   DISPATCH_SEND,
104   DISPATCH_CHECK,
105   DISPATCH_READ,
106   DISPATCH_ERROR
107 } GtkPrintCupsDispatchState;
108
109 typedef struct
110 {
111   GSource source;
112
113   http_t *http;
114   GtkCupsRequest *request;
115   GtkCupsPollState poll_state;
116   GPollFD *data_poll;
117   GtkPrintBackendCups *backend;
118   GtkPrintCupsResponseCallbackFunc callback;
119   gpointer                         callback_data;
120
121 } GtkPrintCupsDispatchWatch;
122
123 struct _GtkPrintBackendCupsClass
124 {
125   GtkPrintBackendClass parent_class;
126 };
127
128 struct _GtkPrintBackendCups
129 {
130   GtkPrintBackend parent_instance;
131
132   char *default_printer;
133
134   guint list_printers_poll;
135   guint list_printers_pending : 1;
136   gint  list_printers_attempts;
137   guint got_default_printer   : 1;
138   guint default_printer_poll;
139   GtkCupsConnectionTest *cups_connection_test;
140   gint  reading_ppds;
141
142   char **covers;
143   int    number_of_covers;
144
145   GList      *requests;
146   GHashTable *auth;
147   gchar      *username;
148   gboolean    authentication_lock;
149 #ifdef HAVE_COLORD
150   CdClient   *colord_client;
151 #endif
152 #ifdef HAVE_CUPS_API_1_6
153   GDBusConnection *dbus_connection;
154   gchar           *avahi_default_printer;
155   guint            avahi_service_browser_subscription_id;
156   guint            avahi_service_browser_subscription_ids[2];
157   gchar           *avahi_service_browser_paths[2];
158   GCancellable    *avahi_cancellable;
159 #endif
160 };
161
162 static GObjectClass *backend_parent_class;
163
164 static void                 gtk_print_backend_cups_class_init      (GtkPrintBackendCupsClass          *class);
165 static void                 gtk_print_backend_cups_init            (GtkPrintBackendCups               *impl);
166 static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
167 static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
168 static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
169 static void                 cups_get_default_printer               (GtkPrintBackendCups               *print_backend);
170 static void                 cups_get_local_default_printer         (GtkPrintBackendCups               *print_backend);
171 static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
172                                                                     GtkCupsRequest                    *request,
173                                                                     GtkPrintCupsResponseCallbackFunc   callback,
174                                                                     gpointer                           user_data,
175                                                                     GDestroyNotify                     notify);
176 static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
177                                                                     GtkPrinterOptionSet               *options,
178                                                                     GtkPrintSettings                  *settings);
179 static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
180                                                                     GtkPrinterOptionSet               *options);
181 static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
182                                                                     GtkPrintSettings                  *settings,
183                                                                     GtkPageSetup                      *page_setup,
184                                                                     GtkPrintCapabilities               capabilities);
185 static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
186                                                                     GtkPrintJob                       *print_job,
187                                                                     GtkPrintSettings                  *settings,
188                                                                     GtkPageSetup                      *page_setup);
189 static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
190 static GtkPageSetup *       cups_printer_get_default_page_size     (GtkPrinter                        *printer);
191 static void                 cups_printer_request_details           (GtkPrinter                        *printer);
192 static gboolean             cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
193 static gboolean             cups_request_ppd                       (GtkPrinter                        *printer);
194 static gboolean             cups_printer_get_hard_margins          (GtkPrinter                        *printer,
195                                                                     gdouble                           *top,
196                                                                     gdouble                           *bottom,
197                                                                     gdouble                           *left,
198                                                                     gdouble                           *right);
199 static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
200 static void                 set_option_from_settings               (GtkPrinterOption                  *option,
201                                                                     GtkPrintSettings                  *setting);
202 static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
203                                                                     GtkPrintJob                       *job,
204                                                                     int                                job_id);
205 static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
206 static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
207                                                                     GtkPrintJob                       *job,
208                                                                     GIOChannel                        *data_io,
209                                                                     GtkPrintJobCompleteFunc            callback,
210                                                                     gpointer                           user_data,
211                                                                     GDestroyNotify                     dnotify);
212 static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
213                                                                     GtkPrintSettings                  *settings,
214                                                                     gdouble                            width,
215                                                                     gdouble                            height,
216                                                                     GIOChannel                        *cache_io);
217
218 static void                 gtk_print_backend_cups_set_password    (GtkPrintBackend                   *backend,
219                                                                     gchar                            **auth_info_required,
220                                                                     gchar                            **auth_info);
221
222 void                        overwrite_and_free                      (gpointer                          data);
223 static gboolean             is_address_local                        (const gchar                      *address);
224 static gboolean             request_auth_info                       (gpointer                          data);
225
226 #ifdef HAVE_CUPS_API_1_6
227 static void                 avahi_request_printer_list              (GtkPrintBackendCups              *cups_backend);
228 #endif
229
230 static void
231 gtk_print_backend_cups_register_type (GTypeModule *module)
232 {
233   const GTypeInfo print_backend_cups_info =
234   {
235     sizeof (GtkPrintBackendCupsClass),
236     NULL,               /* base_init */
237     NULL,               /* base_finalize */
238     (GClassInitFunc) gtk_print_backend_cups_class_init,
239     NULL,               /* class_finalize */
240     NULL,               /* class_data */
241     sizeof (GtkPrintBackendCups),
242     0,                  /* n_preallocs */
243     (GInstanceInitFunc) gtk_print_backend_cups_init
244   };
245
246   print_backend_cups_type = g_type_module_register_type (module,
247                                                          GTK_TYPE_PRINT_BACKEND,
248                                                          "GtkPrintBackendCups",
249                                                          &print_backend_cups_info, 0);
250 }
251
252 G_MODULE_EXPORT void
253 pb_module_init (GTypeModule *module)
254 {
255   GTK_NOTE (PRINTING,
256             g_print ("CUPS Backend: Initializing the CUPS print backend module\n"));
257
258   gtk_print_backend_cups_register_type (module);
259   gtk_printer_cups_register_type (module);
260 }
261
262 G_MODULE_EXPORT void
263 pb_module_exit (void)
264 {
265
266 }
267
268 G_MODULE_EXPORT GtkPrintBackend *
269 pb_module_create (void)
270 {
271   return gtk_print_backend_cups_new ();
272 }
273 /* CUPS 1.6 Getter/Setter Functions CUPS 1.6 makes private most of the
274  * IPP structures and enforces access via new getter functions, which
275  * are unfortunately not available in earlier versions. We define
276  * below those getter functions as macros for use when building
277  * against earlier CUPS versions.
278  */
279 #ifndef HAVE_CUPS_API_1_6
280 #define ippGetOperation(ipp_request) ipp_request->request.op.operation_id
281 #define ippGetInteger(attr, index) attr->values[index].integer
282 #define ippGetBoolean(attr, index) attr->values[index].boolean
283 #define ippGetString(attr, index, foo) attr->values[index].string.text
284 #define ippGetValueTag(attr) attr->value_tag
285 #define ippGetName(attr) attr->name
286 #define ippGetCount(attr) attr->num_values
287 #define ippGetGroupTag(attr) attr->group_tag
288
289 static int
290 ippGetRange (ipp_attribute_t *attr,
291              int element,
292              int *upper)
293 {
294   *upper = attr->values[element].range.upper;
295   return (attr->values[element].range.lower);
296 }
297 #endif
298 /*
299  * GtkPrintBackendCups
300  */
301 GType
302 gtk_print_backend_cups_get_type (void)
303 {
304   return print_backend_cups_type;
305 }
306
307 /**
308  * gtk_print_backend_cups_new:
309  *
310  * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
311  * implements the #GtkPrintBackend interface with direct access to
312  * the filesystem using Unix/Linux API calls
313  *
314  * Return value: the new #GtkPrintBackendCups object
315  */
316 GtkPrintBackend *
317 gtk_print_backend_cups_new (void)
318 {
319   GTK_NOTE (PRINTING,
320             g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
321
322   return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
323 }
324
325 static void
326 gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
327 {
328   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
329   GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
330
331   backend_parent_class = g_type_class_peek_parent (class);
332
333   gobject_class->finalize = gtk_print_backend_cups_finalize;
334   gobject_class->dispose = gtk_print_backend_cups_dispose;
335
336   backend_class->request_printer_list = cups_get_printer_list;
337   backend_class->print_stream = gtk_print_backend_cups_print_stream;
338   backend_class->printer_request_details = cups_printer_request_details;
339   backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
340   backend_class->printer_get_options = cups_printer_get_options;
341   backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
342   backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
343   backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
344   backend_class->printer_list_papers = cups_printer_list_papers;
345   backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
346   backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
347   backend_class->printer_get_capabilities = cups_printer_get_capabilities;
348   backend_class->set_password = gtk_print_backend_cups_set_password;
349 }
350
351 static cairo_status_t
352 _cairo_write_to_cups (void                *closure,
353                       const unsigned char *data,
354                       unsigned int         length)
355 {
356   GIOChannel *io = (GIOChannel *)closure;
357   gsize written;
358   GError *error;
359
360   error = NULL;
361
362   GTK_NOTE (PRINTING,
363             g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
364
365   while (length > 0)
366     {
367       g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
368
369       if (error != NULL)
370         {
371           GTK_NOTE (PRINTING,
372                     g_print ("CUPS Backend: Error writing to temp file, %s\n",
373                              error->message));
374
375           g_error_free (error);
376           return CAIRO_STATUS_WRITE_ERROR;
377         }
378
379       GTK_NOTE (PRINTING,
380                 g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written));
381
382       data += written;
383       length -= written;
384     }
385
386   return CAIRO_STATUS_SUCCESS;
387 }
388
389 static cairo_surface_t *
390 cups_printer_create_cairo_surface (GtkPrinter       *printer,
391                                    GtkPrintSettings *settings,
392                                    gdouble           width,
393                                    gdouble           height,
394                                    GIOChannel       *cache_io)
395 {
396   cairo_surface_t *surface;
397   ppd_file_t      *ppd_file = NULL;
398   ppd_attr_t      *ppd_attr = NULL;
399   ppd_attr_t      *ppd_attr_res = NULL;
400   ppd_attr_t      *ppd_attr_screen_freq = NULL;
401   ppd_attr_t      *ppd_attr_res_screen_freq = NULL;
402   gchar           *res_string = NULL;
403   gint             level = 2;
404
405   if (gtk_printer_accepts_pdf (printer))
406     surface = cairo_pdf_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height);
407   else
408     surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
409
410   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
411
412   if (ppd_file != NULL)
413     {
414       ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
415
416       if (ppd_attr != NULL)
417         level = atoi (ppd_attr->value);
418
419       if (gtk_print_settings_get_resolution (settings) == 0)
420         {
421           ppd_attr_res = ppdFindAttr (ppd_file, "DefaultResolution", NULL);
422
423           if (ppd_attr_res != NULL)
424             {
425               int res, res_x, res_y;
426
427               if (sscanf (ppd_attr_res->value, "%dx%ddpi", &res_x, &res_y) == 2)
428                 {
429                   if (res_x > 0 && res_y > 0)
430                     gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
431                 }
432               else if (sscanf (ppd_attr_res->value, "%ddpi", &res) == 1)
433                 {
434                   if (res > 0)
435                     gtk_print_settings_set_resolution (settings, res);
436                 }
437             }
438         }
439
440       res_string = g_strdup_printf ("%ddpi",
441                                     gtk_print_settings_get_resolution (settings));
442       ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
443       g_free (res_string);
444
445       if (ppd_attr_res_screen_freq == NULL)
446         {
447           res_string = g_strdup_printf ("%dx%ddpi",
448                                         gtk_print_settings_get_resolution_x (settings),
449                                         gtk_print_settings_get_resolution_y (settings));
450           ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
451           g_free (res_string);
452         }
453
454       ppd_attr_screen_freq = ppdFindAttr (ppd_file, "ScreenFreq", NULL);
455
456       if (ppd_attr_res_screen_freq != NULL && atof (ppd_attr_res_screen_freq->value) > 0.0)
457         gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_res_screen_freq->value));
458       else if (ppd_attr_screen_freq != NULL && atof (ppd_attr_screen_freq->value) > 0.0)
459         gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_screen_freq->value));
460     }
461
462   if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PS)
463     {
464       if (level == 2)
465         cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
466
467       if (level == 3)
468         cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
469     }
470
471   cairo_surface_set_fallback_resolution (surface,
472                                          2.0 * gtk_print_settings_get_printer_lpi (settings),
473                                          2.0 * gtk_print_settings_get_printer_lpi (settings));
474
475   return surface;
476 }
477
478 typedef struct {
479   GtkPrintJobCompleteFunc callback;
480   GtkPrintJob *job;
481   gpointer user_data;
482   GDestroyNotify dnotify;
483 } CupsPrintStreamData;
484
485 static void
486 cups_free_print_stream_data (CupsPrintStreamData *data)
487 {
488   GTK_NOTE (PRINTING,
489             g_print ("CUPS Backend: %s\n", G_STRFUNC));
490
491   if (data->dnotify)
492     data->dnotify (data->user_data);
493   g_object_unref (data->job);
494   g_free (data);
495 }
496
497 static void
498 cups_print_cb (GtkPrintBackendCups *print_backend,
499                GtkCupsResult       *result,
500                gpointer             user_data)
501 {
502   GError *error = NULL;
503   CupsPrintStreamData *ps = user_data;
504
505   gdk_threads_enter ();
506
507   GTK_NOTE (PRINTING,
508             g_print ("CUPS Backend: %s\n", G_STRFUNC));
509
510   if (gtk_cups_result_is_error (result))
511     error = g_error_new_literal (gtk_print_error_quark (),
512                                  GTK_PRINT_ERROR_INTERNAL_ERROR,
513                                  gtk_cups_result_get_error_string (result));
514
515   if (ps->callback)
516     ps->callback (ps->job, ps->user_data, error);
517
518   if (error == NULL)
519     {
520       int job_id = 0;
521       ipp_attribute_t *attr;            /* IPP job-id attribute */
522       ipp_t *response = gtk_cups_result_get_response (result);
523
524       if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
525         job_id = ippGetInteger (attr, 0);
526
527       if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
528         gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
529       else
530         {
531           gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
532           cups_begin_polling_info (print_backend, ps->job, job_id);
533         }
534     }
535   else
536     gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
537
538
539   if (error)
540     g_error_free (error);
541
542   gdk_threads_leave ();
543 }
544
545 typedef struct {
546   GtkCupsRequest *request;
547   GtkPrinterCups *printer;
548 } CupsOptionsData;
549
550 static void
551 add_cups_options (const gchar *key,
552                   const gchar *value,
553                   gpointer     user_data)
554 {
555   CupsOptionsData *data = (CupsOptionsData *) user_data;
556   GtkCupsRequest *request = data->request;
557   GtkPrinterCups *printer = data->printer;
558   gboolean custom_value = FALSE;
559   gchar *new_value = NULL;
560   gint i;
561
562   if (!key || !value)
563     return;
564
565   if (!g_str_has_prefix (key, "cups-"))
566     return;
567
568   if (strcmp (value, "gtk-ignore-value") == 0)
569     return;
570
571   key = key + strlen ("cups-");
572
573   if (printer && printer->ppd_file)
574     {
575       ppd_coption_t *coption;
576       gboolean       found = FALSE;
577       gboolean       custom_values_enabled = FALSE;
578
579       coption = ppdFindCustomOption (printer->ppd_file, key);
580       if (coption && coption->option)
581         {
582           for (i = 0; i < coption->option->num_choices; i++)
583             {
584               /* Are custom values enabled ? */
585               if (g_str_equal (coption->option->choices[i].choice, "Custom"))
586                 custom_values_enabled = TRUE;
587
588               /* Is the value among available choices ? */
589               if (g_str_equal (coption->option->choices[i].choice, value))
590                 found = TRUE;
591             }
592
593           if (custom_values_enabled && !found)
594             custom_value = TRUE;
595         }
596     }
597
598   /* Add "Custom." prefix to custom values if not already added. */
599   if (custom_value && !g_str_has_prefix (value, "Custom."))
600     {
601       new_value = g_strdup_printf ("Custom.%s", value);
602       gtk_cups_request_encode_option (request, key, new_value);
603       g_free (new_value);
604     }
605   else
606     gtk_cups_request_encode_option (request, key, value);
607 }
608
609 static void
610 gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
611                                      GtkPrintJob             *job,
612                                      GIOChannel              *data_io,
613                                      GtkPrintJobCompleteFunc  callback,
614                                      gpointer                 user_data,
615                                      GDestroyNotify           dnotify)
616 {
617   GtkPrinterCups *cups_printer;
618   CupsPrintStreamData *ps;
619   CupsOptionsData *options_data;
620   GtkCupsRequest *request = NULL;
621   GtkPrintSettings *settings;
622   const gchar *title;
623   char  printer_absolute_uri[HTTP_MAX_URI];
624
625   GTK_NOTE (PRINTING,
626             g_print ("CUPS Backend: %s\n", G_STRFUNC));
627
628   cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
629   settings = gtk_print_job_get_settings (job);
630
631 #ifdef HAVE_CUPS_API_1_6
632   if (cups_printer->avahi_browsed)
633     {
634       http_t *http;
635
636       http = httpConnect (cups_printer->hostname, cups_printer->port);
637       if (http)
638         {
639           request = gtk_cups_request_new_with_username (http,
640                                                         GTK_CUPS_POST,
641                                                         IPP_PRINT_JOB,
642                                                         data_io,
643                                                         cups_printer->hostname,
644                                                         cups_printer->device_uri,
645                                                         GTK_PRINT_BACKEND_CUPS (print_backend)->username);
646           g_snprintf (printer_absolute_uri, HTTP_MAX_URI, "%s", cups_printer->printer_uri);
647         }
648       else
649         {
650           GError *error = NULL;
651
652           GTK_NOTE (PRINTING,
653                     g_warning ("CUPS Backend: Error connecting to %s:%d",
654                                cups_printer->hostname,
655                                cups_printer->port));
656
657           error = g_error_new (gtk_print_error_quark (),
658                                GTK_CUPS_ERROR_GENERAL,
659                                "Error connecting to %s",
660                                cups_printer->hostname);
661
662           gtk_print_job_set_status (job, GTK_PRINT_STATUS_FINISHED_ABORTED);
663
664           if (callback)
665             {
666               callback (job, user_data, error);
667             }
668
669           g_clear_error (&error);
670
671           return;
672         }
673     }
674   else
675 #endif
676     {
677       request = gtk_cups_request_new_with_username (NULL,
678                                                     GTK_CUPS_POST,
679                                                     IPP_PRINT_JOB,
680                                                     data_io,
681                                                     NULL,
682                                                     cups_printer->device_uri,
683                                                     GTK_PRINT_BACKEND_CUPS (print_backend)->username);
684
685       httpAssembleURIf (HTTP_URI_CODING_ALL,
686                         printer_absolute_uri,
687                         sizeof (printer_absolute_uri),
688                         "ipp",
689                         NULL,
690                         "localhost",
691                         ippPort (),
692                         "/printers/%s",
693                         gtk_printer_get_name (gtk_print_job_get_printer (job)));
694     }
695
696   gtk_cups_request_set_ipp_version (request,
697                                     cups_printer->ipp_version_major,
698                                     cups_printer->ipp_version_minor);
699
700   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
701                                    IPP_TAG_URI, "printer-uri",
702                                    NULL, printer_absolute_uri);
703
704   title = gtk_print_job_get_title (job);
705   if (title)
706     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
707                                      IPP_TAG_NAME, "job-name",
708                                      NULL, title);
709
710   options_data = g_new0 (CupsOptionsData, 1);
711   options_data->request = request;
712   options_data->printer = cups_printer;
713   gtk_print_settings_foreach (settings, add_cups_options, options_data);
714   g_free (options_data);
715
716   ps = g_new0 (CupsPrintStreamData, 1);
717   ps->callback = callback;
718   ps->user_data = user_data;
719   ps->dnotify = dnotify;
720   ps->job = g_object_ref (job);
721
722   request->need_auth_info = cups_printer->auth_info_required != NULL;
723   request->auth_info_required = g_strdupv (cups_printer->auth_info_required);
724
725   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
726                         request,
727                         (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
728                         ps,
729                         (GDestroyNotify)cups_free_print_stream_data);
730 }
731
732 void overwrite_and_free (gpointer data)
733 {
734   gchar *password = (gchar *) data;
735
736   if (password != NULL)
737     {
738       memset (password, 0, strlen (password));
739       g_free (password);
740     }
741 }
742
743 static void
744 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
745 {
746 #ifdef HAVE_CUPS_API_1_6
747   gint i;
748 #endif
749
750   backend_cups->list_printers_poll = FALSE;
751   backend_cups->got_default_printer = FALSE;
752   backend_cups->list_printers_pending = FALSE;
753   backend_cups->list_printers_attempts = 0;
754   backend_cups->reading_ppds = 0;
755
756   backend_cups->requests = NULL;
757   backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
758   backend_cups->authentication_lock = FALSE;
759
760   backend_cups->covers = NULL;
761   backend_cups->number_of_covers = 0;
762
763   backend_cups->default_printer_poll = 0;
764   backend_cups->cups_connection_test = NULL;
765
766   backend_cups->username = NULL;
767
768 #ifdef HAVE_COLORD
769   backend_cups->colord_client = cd_client_new ();
770 #endif
771
772 #ifdef HAVE_CUPS_API_1_6
773   backend_cups->dbus_connection = NULL;
774   backend_cups->avahi_default_printer = NULL;
775   backend_cups->avahi_service_browser_subscription_id = 0;
776   for (i = 0; i < 2; i++)
777     {
778       backend_cups->avahi_service_browser_paths[i] = NULL;
779       backend_cups->avahi_service_browser_subscription_ids[i] = 0;
780     }
781 #endif
782
783   cups_get_local_default_printer (backend_cups);
784 }
785
786 static void
787 gtk_print_backend_cups_finalize (GObject *object)
788 {
789   GtkPrintBackendCups *backend_cups;
790 #ifdef HAVE_CUPS_API_1_6
791   gint                 i;
792 #endif
793
794   GTK_NOTE (PRINTING,
795             g_print ("CUPS Backend: finalizing CUPS backend module\n"));
796
797   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
798
799   g_free (backend_cups->default_printer);
800   backend_cups->default_printer = NULL;
801
802   g_strfreev (backend_cups->covers);
803   backend_cups->number_of_covers = 0;
804
805   gtk_cups_connection_test_free (backend_cups->cups_connection_test);
806   backend_cups->cups_connection_test = NULL;
807
808   g_hash_table_destroy (backend_cups->auth);
809
810   g_free (backend_cups->username);
811
812 #ifdef HAVE_COLORD
813   g_object_unref (backend_cups->colord_client);
814 #endif
815
816 #ifdef HAVE_CUPS_API_1_6
817   g_cancellable_cancel (backend_cups->avahi_cancellable);
818
819   for (i = 0; i < 2; i++)
820     {
821       if (backend_cups->avahi_service_browser_subscription_ids[i] > 0)
822         {
823           g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
824                                                 backend_cups->avahi_service_browser_subscription_ids[i]);
825           backend_cups->avahi_service_browser_subscription_ids[i] = 0;
826         }
827
828       if (backend_cups->avahi_service_browser_paths[i])
829         {
830           g_dbus_connection_call (backend_cups->dbus_connection,
831                                   AVAHI_BUS,
832                                   backend_cups->avahi_service_browser_paths[i],
833                                   AVAHI_SERVICE_BROWSER_IFACE,
834                                   "Free",
835                                   NULL,
836                                   NULL,
837                                   G_DBUS_CALL_FLAGS_NONE,
838                                   -1,
839                                   NULL,
840                                   NULL,
841                                   NULL);
842           g_clear_pointer (&backend_cups->avahi_service_browser_paths[i], g_free);
843         }
844     }
845
846   if (backend_cups->avahi_service_browser_subscription_id > 0)
847     {
848       g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
849                                             backend_cups->avahi_service_browser_subscription_id);
850       backend_cups->avahi_service_browser_subscription_id = 0;
851     }
852
853   g_clear_object (&backend_cups->avahi_cancellable);
854   g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
855   g_object_unref (backend_cups->dbus_connection);
856 #endif
857
858   backend_parent_class->finalize (object);
859 }
860
861 static void
862 gtk_print_backend_cups_dispose (GObject *object)
863 {
864   GtkPrintBackendCups *backend_cups;
865
866   GTK_NOTE (PRINTING,
867             g_print ("CUPS Backend: %s\n", G_STRFUNC));
868
869   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
870
871   if (backend_cups->list_printers_poll > 0)
872     g_source_remove (backend_cups->list_printers_poll);
873   backend_cups->list_printers_poll = 0;
874   backend_cups->list_printers_attempts = 0;
875
876   if (backend_cups->default_printer_poll > 0)
877     g_source_remove (backend_cups->default_printer_poll);
878   backend_cups->default_printer_poll = 0;
879
880   backend_parent_class->dispose (object);
881 }
882
883 static gboolean
884 is_address_local (const gchar *address)
885 {
886   if (address[0] == '/' ||
887       strcmp (address, "127.0.0.1") == 0 ||
888       strcmp (address, "[::1]") == 0)
889     return TRUE;
890   else
891     return FALSE;
892 }
893
894 static void
895 gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
896                                      gchar           **auth_info_required,
897                                      gchar           **auth_info)
898 {
899   GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
900   GList *l;
901   char   dispatch_hostname[HTTP_MAX_URI];
902   gchar *username = NULL;
903   gchar *hostname = NULL;
904   gchar *password = NULL;
905   gint   length;
906   gint   i;
907
908   length = g_strv_length (auth_info_required);
909
910   if (auth_info != NULL)
911     for (i = 0; i < length; i++)
912       {
913         if (g_strcmp0 (auth_info_required[i], "username") == 0)
914           username = g_strdup (auth_info[i]);
915         else if (g_strcmp0 (auth_info_required[i], "hostname") == 0)
916           hostname = g_strdup (auth_info[i]);
917         else if (g_strcmp0 (auth_info_required[i], "password") == 0)
918           password = g_strdup (auth_info[i]);
919       }
920
921   if (hostname != NULL && username != NULL && password != NULL)
922     {
923       gchar *key = g_strconcat (username, "@", hostname, NULL);
924       g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
925       GTK_NOTE (PRINTING,
926                 g_print ("CUPS backend: storing password for %s\n", key));
927     }
928
929   g_free (cups_backend->username);
930   cups_backend->username = g_strdup (username);
931
932
933   for (l = cups_backend->requests; l; l = l->next)
934     {
935       GtkPrintCupsDispatchWatch *dispatch = l->data;
936
937       httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
938       if (is_address_local (dispatch_hostname))
939         strcpy (dispatch_hostname, "localhost");
940
941       if (dispatch->request->need_auth_info)
942         {
943           if (auth_info != NULL)
944             {
945               dispatch->request->auth_info = g_new0 (gchar *, length + 1);
946               for (i = 0; i < length; i++)
947                 dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
948             }
949           dispatch->backend->authentication_lock = FALSE;
950           dispatch->request->need_auth_info = FALSE;
951         }
952       else if (dispatch->request->password_state == GTK_CUPS_PASSWORD_REQUESTED || auth_info == NULL)
953         {
954           overwrite_and_free (dispatch->request->password);
955           dispatch->request->password = g_strdup (password);
956           g_free (dispatch->request->username);
957           dispatch->request->username = g_strdup (username);
958           dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
959           dispatch->backend->authentication_lock = FALSE;
960         }
961     }
962 }
963
964 static gboolean
965 request_password (gpointer data)
966 {
967   GtkPrintCupsDispatchWatch *dispatch = data;
968   const gchar               *username;
969   gchar                     *password;
970   gchar                     *prompt = NULL;
971   gchar                     *key = NULL;
972   char                       hostname[HTTP_MAX_URI];
973   gchar                    **auth_info_required;
974   gchar                    **auth_info_default;
975   gchar                    **auth_info_display;
976   gboolean                  *auth_info_visible;
977   gint                       length = 3;
978   gint                       i;
979
980   if (dispatch->backend->authentication_lock)
981     return G_SOURCE_REMOVE;
982
983   httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
984   if (is_address_local (hostname))
985     strcpy (hostname, "localhost");
986
987   if (dispatch->backend->username != NULL)
988     username = dispatch->backend->username;
989   else
990     username = cupsUser ();
991
992   auth_info_required = g_new0 (gchar*, length + 1);
993   auth_info_required[0] = g_strdup ("hostname");
994   auth_info_required[1] = g_strdup ("username");
995   auth_info_required[2] = g_strdup ("password");
996
997   auth_info_default = g_new0 (gchar*, length + 1);
998   auth_info_default[0] = g_strdup (hostname);
999   auth_info_default[1] = g_strdup (username);
1000
1001   auth_info_display = g_new0 (gchar*, length + 1);
1002   auth_info_display[1] = g_strdup (_("Username:"));
1003   auth_info_display[2] = g_strdup (_("Password:"));
1004
1005   auth_info_visible = g_new0 (gboolean, length + 1);
1006   auth_info_visible[1] = TRUE;
1007
1008   key = g_strconcat (username, "@", hostname, NULL);
1009   password = g_hash_table_lookup (dispatch->backend->auth, key);
1010
1011   if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
1012     {
1013       GTK_NOTE (PRINTING,
1014                 g_print ("CUPS backend: using stored password for %s\n", key));
1015
1016       overwrite_and_free (dispatch->request->password);
1017       dispatch->request->password = g_strdup (password);
1018       g_free (dispatch->request->username);
1019       dispatch->request->username = g_strdup (username);
1020       dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
1021     }
1022   else
1023     {
1024       const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
1025       const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
1026       char *printer_name = NULL;
1027
1028       if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
1029         printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
1030
1031       if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
1032         g_hash_table_remove (dispatch->backend->auth, key);
1033
1034       dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
1035
1036       dispatch->backend->authentication_lock = TRUE;
1037
1038       switch (ippGetOperation (dispatch->request->ipp_request))
1039         {
1040           case IPP_PRINT_JOB:
1041             if (job_title != NULL && 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 a document on %s"), hostname);
1045             break;
1046           case IPP_GET_JOB_ATTRIBUTES:
1047             if (job_title != NULL)
1048               prompt = g_strdup_printf ( _("Authentication is required to get attributes of job '%s'"), job_title);
1049             else
1050               prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
1051             break;
1052           case IPP_GET_PRINTER_ATTRIBUTES:
1053             if (printer_name != NULL)
1054               prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
1055             else
1056               prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
1057             break;
1058           case CUPS_GET_DEFAULT:
1059             prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
1060             break;
1061           case CUPS_GET_PRINTERS:
1062             prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
1063             break;
1064           default:
1065             /* work around gcc warning about 0 not being a value for this enum */
1066             if (ippGetOperation (dispatch->request->ipp_request) == 0)
1067               prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
1068             else
1069               prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
1070             break;
1071         }
1072
1073       g_free (printer_name);
1074
1075       g_signal_emit_by_name (dispatch->backend, "request-password",
1076                              auth_info_required, auth_info_default, auth_info_display, auth_info_visible, prompt);
1077
1078       g_free (prompt);
1079     }
1080
1081   for (i = 0; i < length; i++)
1082     {
1083       g_free (auth_info_required[i]);
1084       g_free (auth_info_default[i]);
1085       g_free (auth_info_display[i]);
1086     }
1087
1088   g_free (auth_info_required);
1089   g_free (auth_info_default);
1090   g_free (auth_info_display);
1091   g_free (auth_info_visible);
1092   g_free (key);
1093
1094   return G_SOURCE_REMOVE;
1095 }
1096
1097 static void
1098 cups_dispatch_add_poll (GSource *source)
1099 {
1100   GtkPrintCupsDispatchWatch *dispatch;
1101   GtkCupsPollState poll_state;
1102
1103   dispatch = (GtkPrintCupsDispatchWatch *) source;
1104
1105   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
1106
1107   /* Remove the old source if the poll state changed. */
1108   if (poll_state != dispatch->poll_state && dispatch->data_poll != NULL)
1109     {
1110       g_source_remove_poll (source, dispatch->data_poll);
1111       g_free (dispatch->data_poll);
1112       dispatch->data_poll = NULL;
1113     }
1114
1115   if (dispatch->request->http != NULL)
1116     {
1117       if (dispatch->data_poll == NULL)
1118         {
1119           dispatch->data_poll = g_new0 (GPollFD, 1);
1120           dispatch->poll_state = poll_state;
1121
1122           if (poll_state == GTK_CUPS_HTTP_READ)
1123             dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
1124           else if (poll_state == GTK_CUPS_HTTP_WRITE)
1125             dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
1126           else
1127             dispatch->data_poll->events = 0;
1128
1129           dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
1130           g_source_add_poll (source, dispatch->data_poll);
1131         }
1132     }
1133 }
1134
1135 static gboolean
1136 check_auth_info (gpointer user_data)
1137 {
1138   GtkPrintCupsDispatchWatch *dispatch;
1139   dispatch = (GtkPrintCupsDispatchWatch *) user_data;
1140
1141   if (!dispatch->request->need_auth_info)
1142     {
1143       if (dispatch->request->auth_info == NULL)
1144         {
1145           dispatch->callback (GTK_PRINT_BACKEND (dispatch->backend),
1146                               gtk_cups_request_get_result (dispatch->request),
1147                               dispatch->callback_data);
1148           g_source_destroy ((GSource *) dispatch);
1149         }
1150       else
1151         {
1152           gint length;
1153           gint i;
1154
1155           length = g_strv_length (dispatch->request->auth_info_required);
1156
1157           gtk_cups_request_ipp_add_strings (dispatch->request,
1158                                             IPP_TAG_JOB,
1159                                             IPP_TAG_TEXT,
1160                                             "auth-info",
1161                                             length,
1162                                             NULL,
1163                                             (const char * const *) dispatch->request->auth_info);
1164
1165           g_source_attach ((GSource *) dispatch, NULL);
1166           g_source_unref ((GSource *) dispatch);
1167
1168           for (i = 0; i < length; i++)
1169             overwrite_and_free (dispatch->request->auth_info[i]);
1170           g_free (dispatch->request->auth_info);
1171           dispatch->request->auth_info = NULL;
1172         }
1173
1174       return G_SOURCE_REMOVE;
1175     }
1176
1177   return G_SOURCE_CONTINUE;
1178 }
1179
1180 static gboolean
1181 request_auth_info (gpointer user_data)
1182 {
1183   GtkPrintCupsDispatchWatch  *dispatch;
1184   const char                 *job_title;
1185   const char                 *printer_uri;
1186   gchar                      *prompt = NULL;
1187   char                       *printer_name = NULL;
1188   gint                        length;
1189   gint                        i;
1190   gboolean                   *auth_info_visible = NULL;
1191   gchar                     **auth_info_default = NULL;
1192   gchar                     **auth_info_display = NULL;
1193
1194   dispatch = (GtkPrintCupsDispatchWatch *) user_data;
1195
1196   if (dispatch->backend->authentication_lock)
1197     return FALSE;
1198
1199   job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
1200   printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
1201   length = g_strv_length (dispatch->request->auth_info_required);
1202
1203   auth_info_visible = g_new0 (gboolean, length);
1204   auth_info_default = g_new0 (gchar *, length + 1);
1205   auth_info_display = g_new0 (gchar *, length + 1);
1206
1207   for (i = 0; i < length; i++)
1208     {
1209       if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0)
1210         {
1211           auth_info_display[i] = g_strdup (_("Domain:"));
1212           auth_info_default[i] = g_strdup ("WORKGROUP");
1213           auth_info_visible[i] = TRUE;
1214         }
1215       else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0)
1216         {
1217           auth_info_display[i] = g_strdup (_("Username:"));
1218           if (dispatch->backend->username != NULL)
1219             auth_info_default[i] = g_strdup (dispatch->backend->username);
1220           else
1221             auth_info_default[i] = g_strdup (cupsUser ());
1222           auth_info_visible[i] = TRUE;
1223         }
1224       else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
1225         {
1226           auth_info_display[i] = g_strdup (_("Password:"));
1227           auth_info_visible[i] = FALSE;
1228         }
1229     }
1230
1231   if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
1232     printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
1233
1234   dispatch->backend->authentication_lock = TRUE;
1235
1236   if (job_title != NULL)
1237     {
1238       if (printer_name != NULL)
1239         prompt = g_strdup_printf ( _("Authentication is required to print document '%s' on printer %s"), job_title, printer_name);
1240       else
1241         prompt = g_strdup_printf ( _("Authentication is required to print document '%s'"), job_title);
1242     }
1243   else
1244     {
1245       if (printer_name != NULL)
1246         prompt = g_strdup_printf ( _("Authentication is required to print this document on printer %s"), printer_name);
1247       else
1248         prompt = g_strdup ( _("Authentication is required to print this document"));
1249     }
1250
1251   g_signal_emit_by_name (dispatch->backend, "request-password",
1252                          dispatch->request->auth_info_required,
1253                          auth_info_default,
1254                          auth_info_display,
1255                          auth_info_visible,
1256                          prompt);
1257
1258   for (i = 0; i < length; i++)
1259     {
1260       g_free (auth_info_default[i]);
1261       g_free (auth_info_display[i]);
1262     }
1263
1264   g_free (auth_info_default);
1265   g_free (auth_info_display);
1266   g_free (printer_name);
1267   g_free (prompt);
1268
1269   g_idle_add (check_auth_info, user_data);
1270
1271   return FALSE;
1272 }
1273
1274 static gboolean
1275 cups_dispatch_watch_check (GSource *source)
1276 {
1277   GtkPrintCupsDispatchWatch *dispatch;
1278   GtkCupsPollState poll_state;
1279   gboolean result;
1280
1281   GTK_NOTE (PRINTING,
1282             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1283
1284   dispatch = (GtkPrintCupsDispatchWatch *) source;
1285
1286   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
1287
1288   if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
1289     if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
1290        return FALSE;
1291
1292   result = gtk_cups_request_read_write (dispatch->request, FALSE);
1293   if (result && dispatch->data_poll != NULL)
1294     {
1295       g_source_remove_poll (source, dispatch->data_poll);
1296       g_free (dispatch->data_poll);
1297       dispatch->data_poll = NULL;
1298     }
1299
1300   if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
1301     {
1302       dispatch->request->need_password = FALSE;
1303       g_idle_add (request_password, dispatch);
1304       result = FALSE;
1305     }
1306
1307   return result;
1308 }
1309
1310 static gboolean
1311 cups_dispatch_watch_prepare (GSource *source,
1312                              gint    *timeout_)
1313 {
1314   GtkPrintCupsDispatchWatch *dispatch;
1315   gboolean result;
1316
1317   dispatch = (GtkPrintCupsDispatchWatch *) source;
1318
1319   GTK_NOTE (PRINTING,
1320             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1321
1322   *timeout_ = -1;
1323
1324   result = gtk_cups_request_read_write (dispatch->request, TRUE);
1325
1326   cups_dispatch_add_poll (source);
1327
1328   return result;
1329 }
1330
1331 static gboolean
1332 cups_dispatch_watch_dispatch (GSource     *source,
1333                               GSourceFunc  callback,
1334                               gpointer     user_data)
1335 {
1336   GtkPrintCupsDispatchWatch *dispatch;
1337   GtkPrintCupsResponseCallbackFunc ep_callback;
1338   GtkCupsResult *result;
1339
1340   g_assert (callback != NULL);
1341
1342   ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
1343
1344   dispatch = (GtkPrintCupsDispatchWatch *) source;
1345
1346   result = gtk_cups_request_get_result (dispatch->request);
1347
1348   GTK_NOTE (PRINTING,
1349             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1350
1351   if (gtk_cups_result_is_error (result))
1352     {
1353       GTK_NOTE (PRINTING,
1354                 g_print("Error result: %s (type %i, status %i, code %i)\n",
1355                         gtk_cups_result_get_error_string (result),
1356                         gtk_cups_result_get_error_type (result),
1357                         gtk_cups_result_get_error_status (result),
1358                         gtk_cups_result_get_error_code (result)));
1359      }
1360
1361   ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
1362
1363   return FALSE;
1364 }
1365
1366 static void
1367 cups_dispatch_watch_finalize (GSource *source)
1368 {
1369   GtkPrintCupsDispatchWatch *dispatch;
1370   GtkCupsResult *result;
1371
1372   GTK_NOTE (PRINTING,
1373             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1374
1375   dispatch = (GtkPrintCupsDispatchWatch *) source;
1376
1377   result = gtk_cups_request_get_result (dispatch->request);
1378   if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
1379     {
1380       const gchar *username;
1381       gchar        hostname[HTTP_MAX_URI];
1382       gchar       *key;
1383
1384       httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
1385       if (is_address_local (hostname))
1386         strcpy (hostname, "localhost");
1387
1388       if (dispatch->backend->username != NULL)
1389         username = dispatch->backend->username;
1390       else
1391         username = cupsUser ();
1392
1393       key = g_strconcat (username, "@", hostname, NULL);
1394       GTK_NOTE (PRINTING,
1395                 g_print ("CUPS backend: removing stored password for %s\n", key));
1396       g_hash_table_remove (dispatch->backend->auth, key);
1397       g_free (key);
1398
1399       if (dispatch->backend)
1400         dispatch->backend->authentication_lock = FALSE;
1401     }
1402
1403   gtk_cups_request_free (dispatch->request);
1404
1405   if (dispatch->backend)
1406     {
1407       /* We need to unref this at idle time, because it might be the
1408        * last reference to this module causing the code to be
1409        * unloaded (including this particular function!)
1410        * Update: Doing this at idle caused a deadlock taking the
1411        * mainloop context lock while being in a GSource callout for
1412        * multithreaded apps. So, for now we just disable unloading
1413        * of print backends. See _gtk_print_backend_create for the
1414        * disabling.
1415        */
1416
1417       dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
1418
1419
1420       g_object_unref (dispatch->backend);
1421       dispatch->backend = NULL;
1422     }
1423
1424   if (dispatch->data_poll)
1425     {
1426       g_source_remove_poll (source, dispatch->data_poll);
1427       g_free (dispatch->data_poll);
1428       dispatch->data_poll = NULL;
1429     }
1430 }
1431
1432 static GSourceFuncs _cups_dispatch_watch_funcs = {
1433   cups_dispatch_watch_prepare,
1434   cups_dispatch_watch_check,
1435   cups_dispatch_watch_dispatch,
1436   cups_dispatch_watch_finalize
1437 };
1438
1439
1440 static void
1441 cups_request_execute (GtkPrintBackendCups              *print_backend,
1442                       GtkCupsRequest                   *request,
1443                       GtkPrintCupsResponseCallbackFunc  callback,
1444                       gpointer                          user_data,
1445                       GDestroyNotify                    notify)
1446 {
1447   GtkPrintCupsDispatchWatch *dispatch;
1448
1449   dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
1450                                                          sizeof (GtkPrintCupsDispatchWatch));
1451   g_source_set_name (&dispatch->source, "GTK+ CUPS backend");
1452
1453   GTK_NOTE (PRINTING,
1454             g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
1455
1456   dispatch->request = request;
1457   dispatch->backend = g_object_ref (print_backend);
1458   dispatch->poll_state = GTK_CUPS_HTTP_IDLE;
1459   dispatch->data_poll = NULL;
1460   dispatch->callback = NULL;
1461   dispatch->callback_data = NULL;
1462
1463   print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
1464
1465   g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
1466
1467   if (request->need_auth_info)
1468     {
1469       dispatch->callback = callback;
1470       dispatch->callback_data = user_data;
1471       request_auth_info (dispatch);
1472     }
1473   else
1474     {
1475       g_source_attach ((GSource *) dispatch, NULL);
1476       g_source_unref ((GSource *) dispatch);
1477     }
1478 }
1479
1480 #if 0
1481 static void
1482 cups_request_printer_info_cb (GtkPrintBackendCups *backend,
1483                               GtkCupsResult       *result,
1484                               gpointer             user_data)
1485 {
1486   ipp_attribute_t *attr;
1487   ipp_t *response;
1488   gchar *printer_name;
1489   GtkPrinterCups *cups_printer;
1490   GtkPrinter *printer;
1491   gchar *loc;
1492   gchar *desc;
1493   gchar *state_msg;
1494   int job_count;
1495   gboolean status_changed;
1496
1497   g_assert (GTK_IS_PRINT_BACKEND_CUPS (backend));
1498
1499   printer_name = (gchar *)user_data;
1500   printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (backend),
1501                                             printer_name);
1502
1503   GTK_NOTE (PRINTING,
1504             g_print ("CUPS Backend: %s - Got printer info for printer '%s'\n", G_STRFUNC, printer_name));
1505
1506   if (!printer)
1507     {
1508       GTK_NOTE (PRINTING,
1509             g_print ("CUPS Backend: Could not find printer called '%s'\n", printer_name));
1510       return;
1511     }
1512
1513   cups_printer = GTK_PRINTER_CUPS (printer);
1514
1515   if (gtk_cups_result_is_error (result))
1516     {
1517       if (gtk_printer_is_new (printer))
1518         {
1519           gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
1520                                             printer);
1521           return;
1522         }
1523       else
1524         return; /* TODO: mark as inactive printer */
1525     }
1526
1527   response = gtk_cups_result_get_response (result);
1528
1529   /* TODO: determine printer type and use correct icon */
1530   gtk_printer_set_icon_name (printer, "printer");
1531
1532   state_msg = "";
1533   loc = "";
1534   desc = "";
1535   job_count = 0;
1536   for (attr = response->attrs; attr != NULL; attr = attr->next)
1537     {
1538       if (!attr->name)
1539         continue;
1540
1541       _CUPS_MAP_ATTR_STR (attr, loc, "printer-location");
1542       _CUPS_MAP_ATTR_STR (attr, desc, "printer-info");
1543       _CUPS_MAP_ATTR_STR (attr, state_msg, "printer-state-message");
1544       _CUPS_MAP_ATTR_INT (attr, cups_printer->state, "printer-state");
1545       _CUPS_MAP_ATTR_INT (attr, job_count, "queued-job-count");
1546     }
1547
1548   status_changed = gtk_printer_set_job_count (printer, job_count);
1549
1550   status_changed |= gtk_printer_set_location (printer, loc);
1551   status_changed |= gtk_printer_set_description (printer, desc);
1552   status_changed |= gtk_printer_set_state_message (printer, state_msg);
1553
1554   if (status_changed)
1555     g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
1556                            "printer-status-changed", printer);
1557 }
1558
1559 static void
1560 cups_request_printer_info (GtkPrintBackendCups *print_backend,
1561                            const gchar         *printer_name)
1562 {
1563   GtkCupsRequest *request;
1564   gchar *printer_uri;
1565   static const char * const pattrs[] =  /* Attributes we're interested in */
1566     {
1567       "printer-location",
1568       "printer-info",
1569       "printer-state-message",
1570       "printer-state",
1571       "queued-job-count",
1572       "job-sheets-supported",
1573       "job-sheets-default"
1574     };
1575
1576   request = gtk_cups_request_new_with_username (NULL,
1577                                                 GTK_CUPS_POST,
1578                                                 IPP_GET_PRINTER_ATTRIBUTES,
1579                                                 NULL,
1580                                                 NULL,
1581                                                 NULL,
1582                                                 print_backend->username);
1583
1584   printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
1585                                   printer_name);
1586   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
1587                                    "printer-uri", NULL, printer_uri);
1588
1589   GTK_NOTE (PRINTING,
1590             g_print ("CUPS Backend: %s - Requesting printer info for URI '%s'\n", G_STRFUNC, printer_uri));
1591
1592   g_free (printer_uri);
1593
1594   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1595                                     "requested-attributes", G_N_ELEMENTS (pattrs),
1596                                     NULL, pattrs);
1597
1598   cups_request_execute (print_backend,
1599                         request,
1600                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
1601                         g_strdup (printer_name),
1602                         (GDestroyNotify) g_free);
1603 }
1604 #endif
1605
1606 typedef struct {
1607   GtkPrintBackendCups *print_backend;
1608   GtkPrintJob *job;
1609   int job_id;
1610   int counter;
1611 } CupsJobPollData;
1612
1613 static void
1614 job_object_died (gpointer  user_data,
1615                  GObject  *where_the_object_was)
1616 {
1617   CupsJobPollData *data = user_data;
1618   data->job = NULL;
1619 }
1620
1621 static void
1622 cups_job_poll_data_free (CupsJobPollData *data)
1623 {
1624   if (data->job)
1625     g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
1626
1627   g_free (data);
1628 }
1629
1630 static void
1631 cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
1632                           GtkCupsResult       *result,
1633                           gpointer             user_data)
1634 {
1635   CupsJobPollData *data = user_data;
1636   ipp_attribute_t *attr;
1637   ipp_t *response;
1638   int state;
1639   gboolean done;
1640
1641   gdk_threads_enter ();
1642
1643   if (data->job == NULL)
1644     {
1645       cups_job_poll_data_free (data);
1646       goto done;
1647     }
1648
1649   data->counter++;
1650
1651   response = gtk_cups_result_get_response (result);
1652
1653   state = 0;
1654
1655 #ifdef HAVE_CUPS_API_1_6
1656   attr = ippFindAttribute (response, "job-state", IPP_TAG_INTEGER);
1657   state = ippGetInteger (attr, 0);
1658 #else
1659   for (attr = response->attrs; attr != NULL; attr = attr->next)
1660     {
1661       if (!attr->name)
1662         continue;
1663
1664       _CUPS_MAP_ATTR_INT (attr, state, "job-state");
1665     }
1666 #endif
1667
1668   done = FALSE;
1669   switch (state)
1670     {
1671     case IPP_JOB_PENDING:
1672     case IPP_JOB_HELD:
1673     case IPP_JOB_STOPPED:
1674       gtk_print_job_set_status (data->job,
1675                                 GTK_PRINT_STATUS_PENDING);
1676       break;
1677     case IPP_JOB_PROCESSING:
1678       gtk_print_job_set_status (data->job,
1679                                 GTK_PRINT_STATUS_PRINTING);
1680       break;
1681     default:
1682     case IPP_JOB_CANCELLED:
1683     case IPP_JOB_ABORTED:
1684       gtk_print_job_set_status (data->job,
1685                                 GTK_PRINT_STATUS_FINISHED_ABORTED);
1686       done = TRUE;
1687       break;
1688     case 0:
1689     case IPP_JOB_COMPLETED:
1690       gtk_print_job_set_status (data->job,
1691                                 GTK_PRINT_STATUS_FINISHED);
1692       done = TRUE;
1693       break;
1694     }
1695
1696   if (!done && data->job != NULL)
1697     {
1698       guint32 timeout;
1699
1700       if (data->counter < 5)
1701         timeout = 100;
1702       else if (data->counter < 10)
1703         timeout = 500;
1704       else
1705         timeout = 1000;
1706
1707       g_timeout_add (timeout, cups_job_info_poll_timeout, data);
1708     }
1709   else
1710     cups_job_poll_data_free (data);
1711
1712 done:
1713   gdk_threads_leave ();
1714 }
1715
1716 static void
1717 cups_request_job_info (CupsJobPollData *data)
1718 {
1719   GtkCupsRequest *request;
1720   gchar *job_uri;
1721
1722   request = gtk_cups_request_new_with_username (NULL,
1723                                                 GTK_CUPS_POST,
1724                                                 IPP_GET_JOB_ATTRIBUTES,
1725                                                 NULL,
1726                                                 NULL,
1727                                                 NULL,
1728                                                 data->print_backend->username);
1729
1730   job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
1731   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
1732                                    "job-uri", NULL, job_uri);
1733   g_free (job_uri);
1734
1735   cups_request_execute (data->print_backend,
1736                         request,
1737                         (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
1738                         data,
1739                         NULL);
1740 }
1741
1742 static gboolean
1743 cups_job_info_poll_timeout (gpointer user_data)
1744 {
1745   CupsJobPollData *data = user_data;
1746
1747   if (data->job == NULL)
1748     cups_job_poll_data_free (data);
1749   else
1750     cups_request_job_info (data);
1751
1752   return G_SOURCE_REMOVE;
1753 }
1754
1755 static void
1756 cups_begin_polling_info (GtkPrintBackendCups *print_backend,
1757                          GtkPrintJob         *job,
1758                          gint                 job_id)
1759 {
1760   CupsJobPollData *data;
1761
1762   data = g_new0 (CupsJobPollData, 1);
1763
1764   data->print_backend = print_backend;
1765   data->job = job;
1766   data->job_id = job_id;
1767   data->counter = 0;
1768
1769   g_object_weak_ref (G_OBJECT (job), job_object_died, data);
1770
1771   cups_request_job_info (data);
1772 }
1773
1774 static void
1775 mark_printer_inactive (GtkPrinter      *printer,
1776                        GtkPrintBackend *backend)
1777 {
1778   gtk_printer_set_is_active (printer, FALSE);
1779   g_signal_emit_by_name (backend, "printer-removed", printer);
1780 }
1781
1782 static gint
1783 find_printer (GtkPrinter  *printer,
1784               const gchar *find_name)
1785 {
1786   const gchar *printer_name;
1787
1788   printer_name = gtk_printer_get_name (printer);
1789   return g_ascii_strcasecmp (printer_name, find_name);
1790 }
1791 /* Printer messages we're interested in */
1792 static const char * const printer_messages[] =
1793   {
1794     "toner-low",
1795     "toner-empty",
1796     "developer-low",
1797     "developer-empty",
1798     "marker-supply-low",
1799     "marker-supply-empty",
1800     "cover-open",
1801     "door-open",
1802     "media-low",
1803     "media-empty",
1804     "offline",
1805     "other"
1806   };
1807 /* Our translatable versions of the printer messages */
1808 static const char * printer_strings[] =
1809   {
1810     N_("Printer '%s' is low on toner."),
1811     N_("Printer '%s' has no toner left."),
1812     /* Translators: "Developer" like on photo development context */
1813     N_("Printer '%s' is low on developer."),
1814     /* Translators: "Developer" like on photo development context */
1815     N_("Printer '%s' is out of developer."),
1816     /* Translators: "marker" is one color bin of the printer */
1817     N_("Printer '%s' is low on at least one marker supply."),
1818     /* Translators: "marker" is one color bin of the printer */
1819     N_("Printer '%s' is out of at least one marker supply."),
1820     N_("The cover is open on printer '%s'."),
1821     N_("The door is open on printer '%s'."),
1822     N_("Printer '%s' is low on paper."),
1823     N_("Printer '%s' is out of paper."),
1824     N_("Printer '%s' is currently offline."),
1825     N_("There is a problem on printer '%s'.")
1826   };
1827
1828 /* Attributes we're interested in for printers */
1829 static const char * const printer_attrs[] =
1830   {
1831     "printer-name",
1832     "printer-uri-supported",
1833     "member-uris",
1834     "printer-location",
1835     "printer-info",
1836     "printer-state-message",
1837     "printer-state-reasons",
1838     "printer-state",
1839     "queued-job-count",
1840     "printer-is-accepting-jobs",
1841     "job-sheets-supported",
1842     "job-sheets-default",
1843     "printer-type",
1844     "auth-info-required",
1845     "number-up-default",
1846     "ipp-versions-supported",
1847     "multiple-document-handling-supported",
1848     "copies-supported",
1849     "number-up-supported"
1850   };
1851
1852 typedef enum
1853   {
1854     GTK_PRINTER_STATE_LEVEL_NONE = 0,
1855     GTK_PRINTER_STATE_LEVEL_INFO = 1,
1856     GTK_PRINTER_STATE_LEVEL_WARNING = 2,
1857     GTK_PRINTER_STATE_LEVEL_ERROR = 3
1858   } PrinterStateLevel;
1859
1860 typedef struct
1861 {
1862   const gchar *printer_name;
1863   const gchar *printer_uri;
1864   const gchar *member_uris;
1865   const gchar *location;
1866   const gchar *description;
1867   gchar *state_msg;
1868   const gchar *reason_msg;
1869   PrinterStateLevel reason_level;
1870   gint state;
1871   gint job_count;
1872   gboolean is_paused;
1873   gboolean is_accepting_jobs;
1874   const gchar *default_cover_before;
1875   const gchar *default_cover_after;
1876   gboolean default_printer;
1877   gboolean got_printer_type;
1878   gboolean remote_printer;
1879 #ifdef HAVE_CUPS_API_1_6
1880   gboolean avahi_printer;
1881 #endif
1882   gchar  **auth_info_required;
1883   gint     default_number_up;
1884   guchar   ipp_version_major;
1885   guchar   ipp_version_minor;
1886   gboolean supports_copies;
1887   gboolean supports_collate;
1888   gboolean supports_number_up;
1889 } PrinterSetupInfo;
1890
1891 static void
1892 get_ipp_version (const char *ipp_version_string,
1893                  guchar     *ipp_version_major,
1894                  guchar     *ipp_version_minor)
1895 {
1896   gchar **ipp_version_strv;
1897   gchar  *endptr;
1898
1899   *ipp_version_major = 1;
1900   *ipp_version_minor = 1;
1901
1902   if (ipp_version_string)
1903     {
1904       ipp_version_strv = g_strsplit (ipp_version_string, ".", 0);
1905
1906       if (ipp_version_strv)
1907         {
1908           if (g_strv_length (ipp_version_strv) == 2)
1909             {
1910               *ipp_version_major = (guchar) g_ascii_strtoull (ipp_version_strv[0], &endptr, 10);
1911               if (endptr == ipp_version_strv[0])
1912                 *ipp_version_major = 1;
1913
1914               *ipp_version_minor = (guchar) g_ascii_strtoull (ipp_version_strv[1], &endptr, 10);
1915               if (endptr == ipp_version_strv[1])
1916                 *ipp_version_minor = 1;
1917             }
1918
1919           g_strfreev (ipp_version_strv);
1920         }
1921     }
1922 }
1923
1924 static void
1925 get_server_ipp_version (guchar *ipp_version_major,
1926                         guchar *ipp_version_minor)
1927 {
1928   *ipp_version_major = 1;
1929   *ipp_version_minor = 1;
1930
1931   if (IPP_VERSION && strlen (IPP_VERSION) == 2)
1932     {
1933       *ipp_version_major = (unsigned char) IPP_VERSION[0];
1934       *ipp_version_minor = (unsigned char) IPP_VERSION[1];
1935     }
1936 }
1937
1938 static gint
1939 ipp_version_cmp (guchar ipp_version_major1,
1940                  guchar ipp_version_minor1,
1941                  guchar ipp_version_major2,
1942                  guchar ipp_version_minor2)
1943 {
1944   if (ipp_version_major1 == ipp_version_major2 &&
1945       ipp_version_minor1 == ipp_version_minor2)
1946     {
1947       return 0;
1948     }
1949   else if (ipp_version_major1 < ipp_version_major2 ||
1950            (ipp_version_major1 == ipp_version_major2 &&
1951             ipp_version_minor1 < ipp_version_minor2))
1952     {
1953       return -1;
1954     }
1955   else
1956     {
1957       return 1;
1958     }
1959 }
1960
1961 static void
1962 cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
1963                                ipp_attribute_t *attr,
1964                                PrinterSetupInfo *info)
1965 {
1966   gint i, j;
1967   if (strcmp (ippGetName (attr), "printer-name") == 0 &&
1968       ippGetValueTag (attr) == IPP_TAG_NAME)
1969     info->printer_name = ippGetString (attr, 0, NULL);
1970   else if (strcmp (ippGetName (attr), "printer-uri-supported") == 0 &&
1971            ippGetValueTag (attr) == IPP_TAG_URI)
1972     info->printer_uri = ippGetString (attr, 0, NULL);
1973   else if (strcmp (ippGetName (attr), "member-uris") == 0 &&
1974            ippGetValueTag (attr) == IPP_TAG_URI)
1975     info->member_uris = ippGetString (attr, 0, NULL);
1976   else if (strcmp (ippGetName (attr), "printer-location") == 0)
1977     info->location = ippGetString (attr, 0, NULL);
1978   else if (strcmp (ippGetName (attr), "printer-info") == 0)
1979     info->description = ippGetString (attr, 0, NULL);
1980   else if (strcmp (ippGetName (attr), "printer-state-message") == 0)
1981     info->state_msg = g_strdup (ippGetString (attr, 0, NULL));
1982   else if (strcmp (ippGetName (attr), "printer-state-reasons") == 0)
1983     /* Store most important reason to reason_msg and set
1984        its importance at printer_state_reason_level */
1985     {
1986       for (i = 0; i < ippGetCount (attr); i++)
1987         {
1988           if (strcmp (ippGetString (attr, i, NULL), "none") != 0)
1989             {
1990               gboolean interested_in = FALSE;
1991               /* Sets is_paused flag for paused printer. */
1992               if (strcmp (ippGetString (attr, i, NULL), "paused") == 0)
1993                 {
1994                   info->is_paused = TRUE;
1995                 }
1996
1997               for (j = 0; j < G_N_ELEMENTS (printer_messages); j++)
1998                 if (strncmp (ippGetString (attr, i, NULL), printer_messages[j],
1999                              strlen (printer_messages[j])) == 0)
2000                   {
2001                     interested_in = TRUE;
2002                     break;
2003                   }
2004
2005               if (interested_in)
2006                 {
2007                   if (g_str_has_suffix (ippGetString (attr, i, NULL), "-report"))
2008                     {
2009                       if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_INFO)
2010                         {
2011                           info->reason_msg = ippGetString (attr, i, NULL);
2012                           info->reason_level = GTK_PRINTER_STATE_LEVEL_INFO;
2013                         }
2014                     }
2015                   else if (g_str_has_suffix (ippGetString (attr, i, NULL), "-warning"))
2016                     {
2017                       if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_WARNING)
2018                         {
2019                           info->reason_msg = ippGetString (attr, i, NULL);
2020                           info->reason_level = GTK_PRINTER_STATE_LEVEL_WARNING;
2021                         }
2022                     }
2023                   else  /* It is error in the case of no suffix. */
2024                     {
2025                       info->reason_msg = ippGetString (attr, i, NULL);
2026                       info->reason_level = GTK_PRINTER_STATE_LEVEL_ERROR;
2027                     }
2028                 }
2029             }
2030         }
2031     }
2032   else if (strcmp (ippGetName (attr), "printer-state") == 0)
2033     info->state = ippGetInteger (attr, 0);
2034   else if (strcmp (ippGetName (attr), "queued-job-count") == 0)
2035     info->job_count = ippGetInteger (attr, 0);
2036   else if (strcmp (ippGetName (attr), "printer-is-accepting-jobs") == 0)
2037     {
2038       if (ippGetBoolean (attr, 0) == 1)
2039         info->is_accepting_jobs = TRUE;
2040       else
2041         info->is_accepting_jobs = FALSE;
2042     }
2043   else if (strcmp (ippGetName (attr), "job-sheets-supported") == 0)
2044     {
2045       if (cups_backend->covers == NULL)
2046         {
2047           cups_backend->number_of_covers = ippGetCount (attr);
2048           cups_backend->covers = g_new (char *, cups_backend->number_of_covers + 1);
2049           for (i = 0; i < cups_backend->number_of_covers; i++)
2050             cups_backend->covers[i] = g_strdup (ippGetString (attr, i, NULL));
2051           cups_backend->covers[cups_backend->number_of_covers] = NULL;
2052         }
2053     }
2054   else if (strcmp (ippGetName (attr), "job-sheets-default") == 0)
2055     {
2056       if (ippGetCount (attr) == 2)
2057         {
2058           info->default_cover_before = ippGetString (attr, 0, NULL);
2059           info->default_cover_after = ippGetString (attr, 1, NULL);
2060         }
2061     }
2062   else if (strcmp (ippGetName (attr), "printer-type") == 0)
2063     {
2064       info->got_printer_type = TRUE;
2065       if (ippGetInteger (attr, 0) & 0x00020000)
2066         info->default_printer = TRUE;
2067       else
2068         info->default_printer = FALSE;
2069
2070       if (ippGetInteger (attr, 0) & 0x00000002)
2071         info->remote_printer = TRUE;
2072       else
2073         info->remote_printer = FALSE;
2074     }
2075   else if (strcmp (ippGetName (attr), "auth-info-required") == 0)
2076     {
2077       if (strcmp (ippGetString (attr, 0, NULL), "none") != 0)
2078         {
2079           info->auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
2080           for (i = 0; i < ippGetCount (attr); i++)
2081             info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
2082         }
2083     }
2084   else if (strcmp (ippGetName (attr), "number-up-default") == 0)
2085     {
2086       info->default_number_up = ippGetInteger (attr, 0);
2087     }
2088   else if (g_strcmp0 (ippGetName (attr), "ipp-versions-supported") == 0)
2089     {
2090       guchar server_ipp_version_major;
2091       guchar server_ipp_version_minor;
2092       guchar ipp_version_major;
2093       guchar ipp_version_minor;
2094
2095       get_server_ipp_version (&server_ipp_version_major,
2096                               &server_ipp_version_minor);
2097
2098       for (i = 0; i < ippGetCount (attr); i++)
2099         {
2100           get_ipp_version (ippGetString (attr, i, NULL),
2101                            &ipp_version_major,
2102                            &ipp_version_minor);
2103
2104           if (ipp_version_cmp (ipp_version_major,
2105                                ipp_version_minor,
2106                                info->ipp_version_major,
2107                                info->ipp_version_minor) > 0 &&
2108               ipp_version_cmp (ipp_version_major,
2109                                ipp_version_minor,
2110                                server_ipp_version_major,
2111                                server_ipp_version_minor) <= 0)
2112             {
2113               info->ipp_version_major = ipp_version_major;
2114               info->ipp_version_minor = ipp_version_minor;
2115             }
2116         }
2117     }
2118   else if (g_strcmp0 (ippGetName (attr), "number-up-supported") == 0)
2119     {
2120       if (ippGetCount (attr) == 6)
2121         {
2122           info->supports_number_up = TRUE;
2123         }
2124     }
2125   else if (g_strcmp0 (ippGetName (attr), "copies-supported") == 0)
2126     {
2127       int upper = 1;
2128
2129       ippGetRange (attr, 0, &upper);
2130       if (upper > 1)
2131         {
2132           info->supports_copies = TRUE;
2133         }
2134     }
2135   else if (g_strcmp0 (ippGetName (attr), "multiple-document-handling-supported") == 0)
2136     {
2137       for (i = 0; i < ippGetCount (attr); i++)
2138         {
2139           if (g_strcmp0 (ippGetString (attr, i, NULL), "separate-documents-collated-copies") == 0)
2140             {
2141               info->supports_collate = TRUE;
2142             }
2143         }
2144     }
2145   else
2146     {
2147       GTK_NOTE (PRINTING,
2148                 g_print ("CUPS Backend: Attribute %s ignored", ippGetName (attr)));
2149     }
2150 }
2151
2152 static GtkPrinter*
2153 cups_create_printer (GtkPrintBackendCups *cups_backend,
2154                      PrinterSetupInfo *info)
2155 {
2156   GtkPrinterCups *cups_printer;
2157   GtkPrinter *printer;
2158   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
2159   char uri[HTTP_MAX_URI];       /* Printer URI */
2160   char method[HTTP_MAX_URI];    /* Method/scheme name */
2161   char username[HTTP_MAX_URI];  /* Username:password */
2162   char hostname[HTTP_MAX_URI];  /* Hostname */
2163   char resource[HTTP_MAX_URI];  /* Resource name */
2164   int  port;                    /* Port number */
2165   char *cups_server;            /* CUPS server */
2166
2167 #ifdef HAVE_COLORD
2168 #ifdef HAVE_CUPS_API_1_6
2169   if (info->avahi_printer)
2170     cups_printer = gtk_printer_cups_new (info->printer_name,
2171                                          backend,
2172                                          NULL);
2173   else
2174 #endif
2175     cups_printer = gtk_printer_cups_new (info->printer_name,
2176                                          backend,
2177                                          cups_backend->colord_client);
2178 #else
2179   cups_printer = gtk_printer_cups_new (info->printer_name, backend, NULL);
2180 #endif
2181
2182   cups_printer->device_uri = g_strdup_printf ("/printers/%s",
2183                                               info->printer_name);
2184
2185   /* Check to see if we are looking at a class */
2186   if (info->member_uris)
2187     {
2188       cups_printer->printer_uri = g_strdup (info->member_uris);
2189       /* TODO if member_uris is a class we need to recursivly find a printer */
2190       GTK_NOTE (PRINTING,
2191                 g_print ("CUPS Backend: Found class with printer %s\n",
2192                          info->member_uris));
2193     }
2194   else
2195     {
2196       cups_printer->printer_uri = g_strdup (info->printer_uri);
2197       GTK_NOTE (PRINTING,
2198                 g_print ("CUPS Backend: Found printer %s\n",
2199                          info->printer_uri));
2200     }
2201
2202   httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
2203                    method, sizeof (method),
2204                    username, sizeof (username),
2205                    hostname, sizeof (hostname),
2206                    &port,
2207                    resource, sizeof (resource));
2208
2209   if (strncmp (resource, "/printers/", 10) == 0)
2210     {
2211       cups_printer->ppd_name = g_strdup (resource + 10);
2212       GTK_NOTE (PRINTING,
2213                 g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, info->printer_name));
2214     }
2215
2216   gethostname (uri, sizeof (uri));
2217   cups_server = g_strdup (cupsServer());
2218
2219   if (strcasecmp (uri, hostname) == 0)
2220     strcpy (hostname, "localhost");
2221
2222   /* if the cups server is local and listening at a unix domain socket
2223    * then use the socket connection
2224    */
2225   if ((strstr (hostname, "localhost") != NULL) &&
2226       (cups_server[0] == '/'))
2227     strcpy (hostname, cups_server);
2228
2229   g_free (cups_server);
2230
2231   cups_printer->default_cover_before = g_strdup (info->default_cover_before);
2232   cups_printer->default_cover_after = g_strdup (info->default_cover_after);
2233
2234   if (info->default_number_up > 0)
2235     cups_printer->default_number_up = info->default_number_up;
2236
2237   cups_printer->hostname = g_strdup (hostname);
2238   cups_printer->port = port;
2239
2240   cups_printer->auth_info_required = g_strdupv (info->auth_info_required);
2241   g_strfreev (info->auth_info_required);
2242
2243   printer = GTK_PRINTER (cups_printer);
2244
2245   if (cups_backend->default_printer != NULL &&
2246       strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
2247     gtk_printer_set_is_default (printer, TRUE);
2248
2249 #ifdef HAVE_CUPS_API_1_6
2250   cups_printer->avahi_browsed = info->avahi_printer;
2251 #endif
2252
2253   gtk_print_backend_add_printer (backend, printer);
2254   return printer;
2255 }
2256
2257 static void
2258 set_printer_icon_name_from_info (GtkPrinter       *printer,
2259                                  PrinterSetupInfo *info)
2260 {
2261   /* Set printer icon according to importance
2262      (none, report, warning, error - report is omitted). */
2263   if (info->reason_level == GTK_PRINTER_STATE_LEVEL_ERROR)
2264     gtk_printer_set_icon_name (printer, "printer-error");
2265   else if (info->reason_level == GTK_PRINTER_STATE_LEVEL_WARNING)
2266     gtk_printer_set_icon_name (printer, "printer-warning");
2267   else if (gtk_printer_is_paused (printer))
2268     gtk_printer_set_icon_name (printer, "printer-paused");
2269   else
2270     gtk_printer_set_icon_name (printer, "printer");
2271 }
2272
2273 static void
2274 set_info_state_message (PrinterSetupInfo *info)
2275 {
2276   gint i;
2277
2278   if (info->state_msg && strlen (info->state_msg) == 0)
2279     {
2280       gchar *tmp_msg2 = NULL;
2281       if (info->is_paused && !info->is_accepting_jobs)
2282         /* Translators: this is a printer status. */
2283         tmp_msg2 = g_strdup ( _("Paused; Rejecting Jobs"));
2284       if (info->is_paused && info->is_accepting_jobs)
2285         /* Translators: this is a printer status. */
2286         tmp_msg2 = g_strdup ( _("Paused"));
2287       if (!info->is_paused && !info->is_accepting_jobs)
2288         /* Translators: this is a printer status. */
2289         tmp_msg2 = g_strdup ( _("Rejecting Jobs"));
2290
2291       if (tmp_msg2 != NULL)
2292         {
2293           g_free (info->state_msg);
2294           info->state_msg = tmp_msg2;
2295         }
2296     }
2297
2298   /* Set description of the reason and combine it with printer-state-message. */
2299   if (info->reason_msg)
2300     {
2301       gchar *reason_msg_desc = NULL;
2302       gboolean found = FALSE;
2303
2304       for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
2305         {
2306           if (strncmp (info->reason_msg, printer_messages[i],
2307                        strlen (printer_messages[i])) == 0)
2308             {
2309               reason_msg_desc = g_strdup_printf (printer_strings[i],
2310                                                  info->printer_name);
2311               found = TRUE;
2312               break;
2313             }
2314         }
2315
2316       if (!found)
2317         info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
2318
2319       if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
2320         {
2321           if (strlen (info->state_msg) == 0)
2322             {
2323               g_free (info->state_msg);
2324               info->state_msg = reason_msg_desc;
2325               reason_msg_desc = NULL;
2326             }
2327           else
2328             {
2329               gchar *tmp_msg = NULL;
2330               /* Translators: this string connects multiple printer states together. */
2331               tmp_msg = g_strjoin ( _("; "), info->state_msg,
2332                                    reason_msg_desc, NULL);
2333               g_free (info->state_msg);
2334               info->state_msg = tmp_msg;
2335             }
2336         }
2337
2338       g_free (reason_msg_desc);
2339     }
2340 }
2341
2342 static void
2343 set_default_printer (GtkPrintBackendCups *cups_backend,
2344                      const gchar         *default_printer_name)
2345 {
2346   cups_backend->default_printer = g_strdup (default_printer_name);
2347   cups_backend->got_default_printer = TRUE;
2348
2349   if (cups_backend->default_printer != NULL)
2350     {
2351       GtkPrinter *default_printer = NULL;
2352       default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
2353                                                         cups_backend->default_printer);
2354       if (default_printer != NULL)
2355         {
2356           gtk_printer_set_is_default (default_printer, TRUE);
2357           g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
2358                                  "printer-status-changed", default_printer);
2359         }
2360     }
2361 }
2362
2363 #ifdef HAVE_CUPS_API_1_6
2364 typedef struct
2365 {
2366   gchar *name;
2367   gchar *type;
2368   gchar *domain;
2369   gchar *host;
2370   gint   port;
2371 } AvahiService;
2372
2373 void
2374 avahi_service_free (AvahiService *service)
2375 {
2376   if (service)
2377     {
2378       g_free (service->name);
2379       g_free (service->type);
2380       g_free (service->domain);
2381       g_free (service->host);
2382       g_free (service);
2383     }
2384 }
2385
2386 static void
2387 cups_request_avahi_printer_info_cb (GtkPrintBackendCups *cups_backend,
2388                                     GtkCupsResult       *result,
2389                                     gpointer             user_data)
2390 {
2391   PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
2392   GtkPrintBackend  *backend = GTK_PRINT_BACKEND (cups_backend);
2393   ipp_attribute_t  *attr;
2394   AvahiService     *service = (AvahiService *) user_data;
2395   GtkPrinter       *printer;
2396   gboolean          list_has_changed = FALSE;
2397   gboolean          status_changed = FALSE;
2398   ipp_t            *response;
2399
2400   gdk_threads_enter ();
2401
2402   GTK_NOTE (PRINTING,
2403             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2404
2405   if (gtk_cups_result_is_error (result))
2406     {
2407       GTK_NOTE (PRINTING,
2408                 g_warning ("CUPS Backend: Error getting printer info: %s %d %d",
2409                            gtk_cups_result_get_error_string (result),
2410                            gtk_cups_result_get_error_type (result),
2411                            gtk_cups_result_get_error_code (result)));
2412
2413       goto done;
2414     }
2415
2416   response = gtk_cups_result_get_response (result);
2417   attr = ippFirstAttribute (response);
2418   while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
2419     attr = ippNextAttribute (response);
2420
2421   if (attr)
2422     {
2423       while (attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
2424         {
2425           cups_printer_handle_attribute (cups_backend, attr, info);
2426           attr = ippNextAttribute (response);
2427         }
2428
2429       if (info->printer_name && info->printer_uri)
2430         {
2431           info->avahi_printer = TRUE;
2432
2433           if (info->got_printer_type &&
2434               info->default_printer &&
2435               cups_backend->avahi_default_printer == NULL)
2436             cups_backend->avahi_default_printer = g_strdup (info->printer_name);
2437
2438           set_info_state_message (info);
2439
2440           printer = gtk_print_backend_find_printer (backend, info->printer_name);
2441           if (!printer)
2442             {
2443               printer = cups_create_printer (cups_backend, info);
2444               list_has_changed = TRUE;
2445             }
2446           else
2447             {
2448               g_object_ref (printer);
2449             }
2450
2451           gtk_printer_set_is_paused (printer, info->is_paused);
2452           gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
2453
2454           if (!gtk_printer_is_active (printer))
2455             {
2456               gtk_printer_set_is_active (printer, TRUE);
2457               gtk_printer_set_is_new (printer, TRUE);
2458               list_has_changed = TRUE;
2459             }
2460
2461           GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
2462           GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (service->name);
2463           GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (service->type);
2464           GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (service->domain);
2465           GTK_PRINTER_CUPS (printer)->hostname = g_strdup (service->host);
2466           GTK_PRINTER_CUPS (printer)->port = service->port;
2467           GTK_PRINTER_CUPS (printer)->state = info->state;
2468           GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
2469           GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
2470           GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
2471           GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
2472           GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
2473           status_changed = gtk_printer_set_job_count (printer, info->job_count);
2474           status_changed |= gtk_printer_set_location (printer, info->location);
2475           status_changed |= gtk_printer_set_description (printer, info->description);
2476           status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
2477           status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
2478
2479           set_printer_icon_name_from_info (printer, info);
2480
2481           if (gtk_printer_is_new (printer))
2482             {
2483               g_signal_emit_by_name (backend, "printer-added", printer);
2484               gtk_printer_set_is_new (printer, FALSE);
2485             }
2486
2487           if (status_changed)
2488             g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
2489                                    "printer-status-changed", printer);
2490
2491           /* The ref is held by GtkPrintBackend, in add_printer() */
2492           g_object_unref (printer);
2493         }
2494     }
2495
2496 done:
2497   if (list_has_changed)
2498     g_signal_emit_by_name (backend, "printer-list-changed");
2499
2500   if (!cups_backend->got_default_printer &&
2501       gtk_print_backend_printer_list_is_done (backend) &&
2502       cups_backend->avahi_default_printer != NULL)
2503     {
2504       set_default_printer (cups_backend, cups_backend->avahi_default_printer);
2505     }
2506
2507   g_slice_free (PrinterSetupInfo, info);
2508
2509   gdk_threads_leave ();
2510 }
2511
2512 static void
2513 cups_request_avahi_printer_info (const gchar         *printer_uri,
2514                                  const gchar         *host,
2515                                  gint                 port,
2516                                  const gchar         *name,
2517                                  const gchar         *type,
2518                                  const gchar         *domain,
2519                                  GtkPrintBackendCups *backend)
2520 {
2521   GtkCupsRequest *request;
2522   AvahiService   *service;
2523   http_t         *http;
2524
2525   http = httpConnect (host, port);
2526   if (http)
2527     {
2528       service = (AvahiService *) g_new0 (AvahiService, 1);
2529       service->name = g_strdup (name);
2530       service->type = g_strdup (type);
2531       service->domain = g_strdup (domain);
2532       service->host = g_strdup (host);
2533       service->port = port;
2534
2535       request = gtk_cups_request_new_with_username (http,
2536                                                     GTK_CUPS_POST,
2537                                                     IPP_GET_PRINTER_ATTRIBUTES,
2538                                                     NULL,
2539                                                     NULL,
2540                                                     NULL,
2541                                                     backend->username);
2542
2543       gtk_cups_request_set_ipp_version (request, 1, 1);
2544
2545       gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
2546                                        "printer-uri", NULL, printer_uri);
2547
2548       gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2549                                         "requested-attributes", G_N_ELEMENTS (printer_attrs),
2550                                         NULL, printer_attrs);
2551
2552       cups_request_execute (backend,
2553                             request,
2554                             (GtkPrintCupsResponseCallbackFunc) cups_request_avahi_printer_info_cb,
2555                             service,
2556                             (GDestroyNotify) avahi_service_free);
2557     }
2558 }
2559
2560 typedef struct
2561 {
2562   gchar               *printer_uri;
2563   gchar               *host;
2564   gint                 port;
2565   gchar               *name;
2566   gchar               *type;
2567   gchar               *domain;
2568   GtkPrintBackendCups *backend;
2569 } AvahiConnectionTestData;
2570
2571 static void
2572 avahi_connection_test_cb (GObject      *source_object,
2573                           GAsyncResult *res,
2574                           gpointer      user_data)
2575 {
2576   AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
2577   GSocketConnection       *connection;
2578
2579   connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
2580                                                        res,
2581                                                        NULL);
2582   g_object_unref (source_object);
2583
2584   if (connection != NULL)
2585     {
2586       g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
2587       g_object_unref (connection);
2588
2589       cups_request_avahi_printer_info (data->printer_uri,
2590                                        data->host,
2591                                        data->port,
2592                                        data->name,
2593                                        data->type,
2594                                        data->domain,
2595                                        data->backend);
2596     }
2597
2598   g_free (data->printer_uri);
2599   g_free (data->host);
2600   g_free (data->name);
2601   g_free (data->type);
2602   g_free (data->domain);
2603   g_free (data);
2604 }
2605
2606 static void
2607 avahi_service_resolver_cb (GObject      *source_object,
2608                            GAsyncResult *res,
2609                            gpointer      user_data)
2610 {
2611   AvahiConnectionTestData *data;
2612   GtkPrintBackendCups     *backend = GTK_PRINT_BACKEND_CUPS (user_data);
2613   const gchar             *name;
2614   const gchar             *host;
2615   const gchar             *type;
2616   const gchar             *domain;
2617   const gchar             *address;
2618   const gchar             *protocol_string;
2619   GVariant                *output;
2620   GVariant                *txt;
2621   GVariant                *child;
2622   guint32                  flags;
2623   guint16                  port;
2624   GError                  *error = NULL;
2625   gchar                   *suffix = NULL;
2626   gchar                   *tmp;
2627   gint                     interface;
2628   gint                     protocol;
2629   gint                     aprotocol;
2630   gint                     i, j;
2631
2632   output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
2633                                           res,
2634                                           &error);
2635   if (output)
2636     {
2637       g_variant_get (output, "(ii&s&s&s&si&sq@aayu)",
2638                      &interface,
2639                      &protocol,
2640                      &name,
2641                      &type,
2642                      &domain,
2643                      &host,
2644                      &aprotocol,
2645                      &address,
2646                      &port,
2647                      &txt,
2648                      &flags);
2649
2650       for (i = 0; i < g_variant_n_children (txt); i++)
2651         {
2652           child = g_variant_get_child_value (txt, i);
2653
2654           tmp = g_new0 (gchar, g_variant_n_children (child) + 1);
2655           for (j = 0; j < g_variant_n_children (child); j++)
2656             {
2657               tmp[j] = g_variant_get_byte (g_variant_get_child_value (child, j));
2658             }
2659
2660           if (g_str_has_prefix (tmp, "rp="))
2661             {
2662               suffix = g_strdup (tmp + 3);
2663               g_free (tmp);
2664               break;
2665             }
2666
2667           g_free (tmp);
2668         }
2669
2670       if (suffix)
2671         {
2672           if (g_strcmp0 (type, "_ipp._tcp") == 0)
2673             protocol_string = "ipp";
2674           else
2675             protocol_string = "ipps";
2676
2677           data = g_new0 (AvahiConnectionTestData, 1);
2678
2679           if (aprotocol == AVAHI_PROTO_INET6)
2680             data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, suffix);
2681           else
2682             data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, suffix);
2683
2684           data->host = g_strdup (address);
2685           data->port = port;
2686           data->name = g_strdup (name);
2687           data->type = g_strdup (type);
2688           data->domain = g_strdup (domain);
2689           data->backend = backend;
2690
2691           /* It can happen that the address is not reachable */
2692           g_socket_client_connect_to_host_async (g_socket_client_new (),
2693                                                  address,
2694                                                  port,
2695                                                  backend->avahi_cancellable,
2696                                                  avahi_connection_test_cb,
2697                                                  data);
2698           g_free (suffix);
2699         }
2700
2701       g_variant_unref (output);
2702     }
2703   else
2704     {
2705       g_warning ("%s", error->message);
2706       g_error_free (error);
2707     }
2708 }
2709
2710 static void
2711 avahi_service_browser_signal_handler (GDBusConnection *connection,
2712                                       const gchar     *sender_name,
2713                                       const gchar     *object_path,
2714                                       const gchar     *interface_name,
2715                                       const gchar     *signal_name,
2716                                       GVariant        *parameters,
2717                                       gpointer         user_data)
2718 {
2719   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
2720   gchar               *name;
2721   gchar               *type;
2722   gchar               *domain;
2723   guint                flags;
2724   gint                 interface;
2725   gint                 protocol;
2726
2727   if (g_strcmp0 (signal_name, "ItemNew") == 0)
2728     {
2729       g_variant_get (parameters, "(ii&s&s&su)",
2730                      &interface,
2731                      &protocol,
2732                      &name,
2733                      &type,
2734                      &domain,
2735                      &flags);
2736
2737       if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
2738           g_strcmp0 (type, "_ipps._tcp") == 0)
2739         {
2740           g_dbus_connection_call (backend->dbus_connection,
2741                                   AVAHI_BUS,
2742                                   "/",
2743                                   AVAHI_SERVER_IFACE,
2744                                   "ResolveService",
2745                                   g_variant_new ("(iisssiu)",
2746                                                  interface,
2747                                                  protocol,
2748                                                  name,
2749                                                  type,
2750                                                  domain,
2751                                                  AVAHI_PROTO_UNSPEC,
2752                                                  0),
2753                                   G_VARIANT_TYPE ("(iissssisqaayu)"),
2754                                   G_DBUS_CALL_FLAGS_NONE,
2755                                   -1,
2756                                   backend->avahi_cancellable,
2757                                   avahi_service_resolver_cb,
2758                                   user_data);
2759         }
2760     }
2761   else if (g_strcmp0 (signal_name, "ItemRemove") == 0)
2762     {
2763       GtkPrinterCups *printer;
2764       GList          *list;
2765       GList          *iter;
2766
2767       g_variant_get (parameters, "(ii&s&s&su)",
2768                      &interface,
2769                      &protocol,
2770                      &name,
2771                      &type,
2772                      &domain,
2773                      &flags);
2774
2775       if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
2776           g_strcmp0 (type, "_ipps._tcp") == 0)
2777         {
2778           list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
2779           for (iter = list; iter; iter = iter->next)
2780             {
2781               printer = GTK_PRINTER_CUPS (iter->data);
2782               if (g_strcmp0 (printer->avahi_name, name) == 0 &&
2783                   g_strcmp0 (printer->avahi_type, type) == 0 &&
2784                   g_strcmp0 (printer->avahi_domain, domain) == 0)
2785                 {
2786                   if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
2787                                  backend->avahi_default_printer) == 0)
2788                     g_clear_pointer (&backend->avahi_default_printer, g_free);
2789
2790                   g_signal_emit_by_name (backend, "printer-removed", printer);
2791                   gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
2792                                                     GTK_PRINTER (printer));
2793                   g_signal_emit_by_name (backend, "printer-list-changed");
2794                   break;
2795                 }
2796             }
2797         }
2798
2799       g_list_free (list);
2800     }
2801 }
2802
2803 static void
2804 avahi_service_browser_new_cb (GObject      *source_object,
2805                               GAsyncResult *res,
2806                               gpointer      user_data)
2807 {
2808   GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
2809   GVariant            *output;
2810   GError              *error = NULL;
2811   gint                 i;
2812
2813   output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
2814                                           res,
2815                                           &error);
2816   if (output)
2817     {
2818       i = cups_backend->avahi_service_browser_paths[0] ? 1 : 0;
2819
2820       g_variant_get (output, "(o)", &cups_backend->avahi_service_browser_paths[i]);
2821
2822       cups_backend->avahi_service_browser_subscription_ids[i] =
2823         g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
2824                                             NULL,
2825                                             AVAHI_SERVICE_BROWSER_IFACE,
2826                                             NULL,
2827                                             cups_backend->avahi_service_browser_paths[i],
2828                                             NULL,
2829                                             G_DBUS_SIGNAL_FLAGS_NONE,
2830                                             avahi_service_browser_signal_handler,
2831                                             user_data,
2832                                             NULL);
2833
2834       /*
2835        * The general subscription for all service browsers is not needed
2836        * now because we are already subscribed to service browsers
2837        * specific to _ipp._tcp and _ipps._tcp services.
2838        */
2839       if (cups_backend->avahi_service_browser_paths[0] &&
2840           cups_backend->avahi_service_browser_paths[1] &&
2841           cups_backend->avahi_service_browser_subscription_id > 0)
2842         {
2843           g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
2844                                                 cups_backend->avahi_service_browser_subscription_id);
2845           cups_backend->avahi_service_browser_subscription_id = 0;
2846         }
2847
2848       g_variant_unref (output);
2849     }
2850   else
2851     {
2852       /*
2853        * The creation of ServiceBrowser fails with G_IO_ERROR_DBUS_ERROR
2854        * if Avahi is disabled.
2855        */
2856       if (error->domain != G_IO_ERROR ||
2857           error->code != G_IO_ERROR_DBUS_ERROR)
2858         g_warning ("%s", error->message);
2859       g_error_free (error);
2860     }
2861 }
2862
2863 static void
2864 avahi_create_browsers (GObject      *source_object,
2865                        GAsyncResult *res,
2866                        gpointer      user_data)
2867 {
2868   GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
2869   GError              *error = NULL;
2870
2871   cups_backend->dbus_connection = g_bus_get_finish (res, &error);
2872   if (!cups_backend->dbus_connection)
2873     {
2874       g_debug ("Couldn't connect to D-Bus system bus, %s", error->message);
2875       g_error_free (error);
2876       return;
2877     }
2878
2879   /*
2880    * We need to subscribe to signals of service browser before
2881    * we actually create it because it starts to emit them right
2882    * after its creation.
2883    */
2884   cups_backend->avahi_service_browser_subscription_id =
2885     g_dbus_connection_signal_subscribe  (cups_backend->dbus_connection,
2886                                          NULL,
2887                                          AVAHI_SERVICE_BROWSER_IFACE,
2888                                          NULL,
2889                                          NULL,
2890                                          NULL,
2891                                          G_DBUS_SIGNAL_FLAGS_NONE,
2892                                          avahi_service_browser_signal_handler,
2893                                          cups_backend,
2894                                          NULL);
2895
2896   /*
2897    * Create service browsers for _ipp._tcp and _ipps._tcp services.
2898    */
2899   g_dbus_connection_call (cups_backend->dbus_connection,
2900                           AVAHI_BUS,
2901                           "/",
2902                           AVAHI_SERVER_IFACE,
2903                           "ServiceBrowserNew",
2904                           g_variant_new ("(iissu)",
2905                                          AVAHI_IF_UNSPEC,
2906                                          AVAHI_PROTO_UNSPEC,
2907                                          "_ipp._tcp",
2908                                          "",
2909                                          0),
2910                           G_VARIANT_TYPE ("(o)"),
2911                           G_DBUS_CALL_FLAGS_NONE,
2912                           -1,
2913                           cups_backend->avahi_cancellable,
2914                           avahi_service_browser_new_cb,
2915                           cups_backend);
2916
2917   g_dbus_connection_call (cups_backend->dbus_connection,
2918                           AVAHI_BUS,
2919                           "/",
2920                           AVAHI_SERVER_IFACE,
2921                           "ServiceBrowserNew",
2922                           g_variant_new ("(iissu)",
2923                                          AVAHI_IF_UNSPEC,
2924                                          AVAHI_PROTO_UNSPEC,
2925                                          "_ipps._tcp",
2926                                          "",
2927                                          0),
2928                           G_VARIANT_TYPE ("(o)"),
2929                           G_DBUS_CALL_FLAGS_NONE,
2930                           -1,
2931                           cups_backend->avahi_cancellable,
2932                           avahi_service_browser_new_cb,
2933                           cups_backend);
2934 }
2935
2936 static void
2937 avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
2938 {
2939   cups_backend->avahi_cancellable = g_cancellable_new ();
2940   g_bus_get (G_BUS_TYPE_SYSTEM, NULL, avahi_create_browsers, cups_backend);
2941 }
2942 #endif
2943
2944 static void
2945 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
2946                               GtkCupsResult       *result,
2947                               gpointer             user_data)
2948 {
2949   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
2950   ipp_attribute_t *attr;
2951   ipp_t *response;
2952   gboolean list_has_changed;
2953   GList *removed_printer_checklist;
2954   gchar *remote_default_printer = NULL;
2955   GList *iter;
2956
2957   gdk_threads_enter ();
2958
2959   list_has_changed = FALSE;
2960
2961   GTK_NOTE (PRINTING,
2962             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2963
2964   cups_backend->list_printers_pending = FALSE;
2965
2966   if (gtk_cups_result_is_error (result))
2967     {
2968       GTK_NOTE (PRINTING,
2969                 g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
2970                            gtk_cups_result_get_error_string (result),
2971                            gtk_cups_result_get_error_type (result),
2972                            gtk_cups_result_get_error_code (result)));
2973
2974       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2975           gtk_cups_result_get_error_code (result) == 1)
2976         {
2977           /* Canceled by user, stop popping up more password dialogs */
2978           if (cups_backend->list_printers_poll > 0)
2979             g_source_remove (cups_backend->list_printers_poll);
2980           cups_backend->list_printers_poll = 0;
2981           cups_backend->list_printers_attempts = 0;
2982         }
2983
2984       goto done;
2985     }
2986
2987   /* Gather the names of the printers in the current queue
2988    * so we may check to see if they were removed
2989    */
2990   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
2991
2992   response = gtk_cups_result_get_response (result);
2993 #ifdef HAVE_CUPS_API_1_6
2994   for (attr = ippFirstAttribute (response); attr != NULL;
2995        attr = ippNextAttribute (response))
2996     {
2997       GtkPrinter *printer;
2998       gboolean status_changed = FALSE;
2999       GList *node;
3000       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
3001
3002       /* Skip leading attributes until we hit a printer...
3003        */
3004       while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
3005         attr = ippNextAttribute (response);
3006
3007       if (attr == NULL)
3008         break;
3009       while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
3010       {
3011         cups_printer_handle_attribute (cups_backend, attr, info);
3012         attr = ippNextAttribute (response);
3013       }
3014 #else
3015   for (attr = response->attrs; attr != NULL; attr = attr->next)
3016     {
3017       GtkPrinter *printer;
3018       gboolean status_changed = FALSE;
3019       GList *node;
3020       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
3021       info->default_number_up = 1;
3022
3023       /* Skip leading attributes until we hit a printer...
3024        */
3025       while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
3026         attr = attr->next;
3027
3028       if (attr == NULL)
3029         break;
3030       while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
3031       {
3032         cups_printer_handle_attribute (cups_backend, attr, info);
3033         attr = attr->next;
3034       }
3035 #endif
3036
3037       if (info->printer_name == NULL ||
3038           (info->printer_uri == NULL && info->member_uris == NULL))
3039       {
3040         if (attr == NULL)
3041           break;
3042         else
3043           continue;
3044       }
3045
3046       if (info->got_printer_type)
3047         {
3048           if (info->default_printer && !cups_backend->got_default_printer)
3049             {
3050               if (!info->remote_printer)
3051                 {
3052                   cups_backend->got_default_printer = TRUE;
3053                   cups_backend->default_printer = g_strdup (info->printer_name);
3054                 }
3055               else
3056                 {
3057                   if (remote_default_printer == NULL)
3058                     remote_default_printer = g_strdup (info->printer_name);
3059                 }
3060             }
3061         }
3062       else
3063         {
3064           if (!cups_backend->got_default_printer)
3065             cups_get_default_printer (cups_backend);
3066         }
3067
3068       /* remove name from checklist if it was found */
3069       node = g_list_find_custom (removed_printer_checklist,
3070                                  info->printer_name,
3071                                  (GCompareFunc) find_printer);
3072       removed_printer_checklist = g_list_delete_link (removed_printer_checklist,
3073                                                       node);
3074
3075       printer = gtk_print_backend_find_printer (backend, info->printer_name);
3076       if (!printer)
3077         {
3078           printer = cups_create_printer (cups_backend, info);
3079           list_has_changed = TRUE;
3080         }
3081
3082       else
3083         g_object_ref (printer);
3084
3085       GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
3086
3087       gtk_printer_set_is_paused (printer, info->is_paused);
3088       gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
3089
3090       if (!gtk_printer_is_active (printer))
3091         {
3092           gtk_printer_set_is_active (printer, TRUE);
3093           gtk_printer_set_is_new (printer, TRUE);
3094           list_has_changed = TRUE;
3095         }
3096
3097       if (gtk_printer_is_new (printer))
3098         {
3099           g_signal_emit_by_name (backend, "printer-added", printer);
3100
3101           gtk_printer_set_is_new (printer, FALSE);
3102         }
3103
3104 #if 0
3105       /* Getting printer info with separate requests overwhelms cups
3106        * when the printer list has more than a handful of printers.
3107        */
3108       cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
3109 #endif
3110
3111       GTK_PRINTER_CUPS (printer)->state = info->state;
3112       GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
3113       GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
3114       GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
3115       GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
3116       GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
3117       status_changed = gtk_printer_set_job_count (printer, info->job_count);
3118       status_changed |= gtk_printer_set_location (printer, info->location);
3119       status_changed |= gtk_printer_set_description (printer,
3120                                                      info->description);
3121
3122       set_info_state_message (info);
3123
3124       status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
3125       status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
3126
3127       set_printer_icon_name_from_info (printer, info);
3128
3129       if (status_changed)
3130         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
3131                                "printer-status-changed", printer);
3132
3133       /* The ref is held by GtkPrintBackend, in add_printer() */
3134       g_object_unref (printer);
3135       g_free (info->state_msg);
3136       g_slice_free (PrinterSetupInfo, info);
3137
3138       if (attr == NULL)
3139         break;
3140     }
3141
3142   /* look at the removed printers checklist and mark any printer
3143      as inactive if it is in the list, emitting a printer_removed signal */
3144   if (removed_printer_checklist != NULL)
3145     {
3146       for (iter = removed_printer_checklist; iter; iter = iter->next)
3147         {
3148 #ifdef HAVE_CUPS_API_1_6
3149           if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
3150 #endif
3151             {
3152               mark_printer_inactive (GTK_PRINTER (iter->data), backend);
3153               list_has_changed = TRUE;
3154             }
3155         }
3156
3157       g_list_free (removed_printer_checklist);
3158     }
3159
3160 done:
3161   if (list_has_changed)
3162     g_signal_emit_by_name (backend, "printer-list-changed");
3163
3164   gtk_print_backend_set_list_done (backend);
3165
3166   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
3167     {
3168       set_default_printer (cups_backend, remote_default_printer);
3169       g_free (remote_default_printer);
3170     }
3171
3172 #ifdef HAVE_CUPS_API_1_6
3173   if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
3174     {
3175       set_default_printer (cups_backend, cups_backend->avahi_default_printer);
3176     }
3177 #endif
3178
3179   gdk_threads_leave ();
3180 }
3181
3182 static void
3183 update_backend_status (GtkPrintBackendCups    *cups_backend,
3184                        GtkCupsConnectionState  state)
3185 {
3186   switch (state)
3187     {
3188     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
3189       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
3190       break;
3191     case GTK_CUPS_CONNECTION_AVAILABLE:
3192       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
3193       break;
3194     default: ;
3195     }
3196 }
3197
3198 static gboolean
3199 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
3200 {
3201   GtkCupsConnectionState state;
3202   GtkCupsRequest *request;
3203
3204   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
3205     return TRUE;
3206
3207   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
3208   update_backend_status (cups_backend, state);
3209
3210   if (cups_backend->list_printers_attempts == 60)
3211     {
3212       cups_backend->list_printers_attempts = -1;
3213       if (cups_backend->list_printers_poll > 0)
3214         g_source_remove (cups_backend->list_printers_poll);
3215       cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
3216                                            (GSourceFunc) cups_request_printer_list,
3217                                            cups_backend);
3218     }
3219   else if (cups_backend->list_printers_attempts != -1)
3220     cups_backend->list_printers_attempts++;
3221
3222   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3223     return TRUE;
3224   else
3225     if (cups_backend->list_printers_attempts > 0)
3226       cups_backend->list_printers_attempts = 60;
3227
3228   cups_backend->list_printers_pending = TRUE;
3229
3230   request = gtk_cups_request_new_with_username (NULL,
3231                                                 GTK_CUPS_POST,
3232                                                 CUPS_GET_PRINTERS,
3233                                                 NULL,
3234                                                 NULL,
3235                                                 NULL,
3236                                                 cups_backend->username);
3237
3238   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3239                                     "requested-attributes", G_N_ELEMENTS (printer_attrs),
3240                                     NULL, printer_attrs);
3241
3242   cups_request_execute (cups_backend,
3243                         request,
3244                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
3245                         request,
3246                         NULL);
3247
3248   return TRUE;
3249 }
3250
3251 static void
3252 cups_get_printer_list (GtkPrintBackend *backend)
3253 {
3254   GtkPrintBackendCups *cups_backend;
3255
3256   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
3257
3258   if (cups_backend->cups_connection_test == NULL)
3259     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
3260
3261   if (cups_backend->list_printers_poll == 0)
3262     {
3263       if (cups_request_printer_list (cups_backend))
3264         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
3265                                              (GSourceFunc) cups_request_printer_list,
3266                                              backend);
3267
3268 #ifdef HAVE_CUPS_API_1_6
3269       avahi_request_printer_list (cups_backend);
3270 #endif
3271     }
3272 }
3273
3274 typedef struct {
3275   GtkPrinterCups *printer;
3276   GIOChannel *ppd_io;
3277   http_t *http;
3278 } GetPPDData;
3279
3280 static void
3281 get_ppd_data_free (GetPPDData *data)
3282 {
3283   GTK_NOTE (PRINTING,
3284             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3285   httpClose (data->http);
3286   g_io_channel_unref (data->ppd_io);
3287   g_object_unref (data->printer);
3288   g_free (data);
3289 }
3290
3291 static void
3292 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
3293                      GtkCupsResult       *result,
3294                      GetPPDData          *data)
3295 {
3296   GtkPrinter *printer;
3297
3298   gdk_threads_enter ();
3299
3300   GTK_NOTE (PRINTING,
3301             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3302
3303   printer = GTK_PRINTER (data->printer);
3304   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
3305   print_backend->reading_ppds--;
3306
3307   if (gtk_cups_result_is_error (result))
3308     {
3309       gboolean success = FALSE;
3310
3311       /* If we get a 404 then it is just a raw printer without a ppd
3312          and not an error. Standalone Avahi printers also don't have
3313          PPD files. */
3314       if (((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
3315            (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
3316 #ifdef HAVE_CUPS_API_1_6
3317            || GTK_PRINTER_CUPS (printer)->avahi_browsed
3318 #endif
3319            )
3320         {
3321           gtk_printer_set_has_details (printer, TRUE);
3322           success = TRUE;
3323         }
3324
3325       g_signal_emit_by_name (printer, "details-acquired", success);
3326       goto done;
3327     }
3328
3329   /* let ppdOpenFd take over the ownership of the open file */
3330   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
3331   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
3332   ppdLocalize (data->printer->ppd_file);
3333   ppdMarkDefaults (data->printer->ppd_file);
3334
3335   gtk_printer_set_has_details (printer, TRUE);
3336   g_signal_emit_by_name (printer, "details-acquired", TRUE);
3337
3338 done:
3339   gdk_threads_leave ();
3340 }
3341
3342 static gboolean
3343 cups_request_ppd (GtkPrinter *printer)
3344 {
3345   GError *error;
3346   GtkPrintBackend *print_backend;
3347   GtkPrinterCups *cups_printer;
3348   GtkCupsRequest *request;
3349   char *ppd_filename;
3350   gchar *resource;
3351   http_t *http;
3352   GetPPDData *data;
3353   int fd;
3354
3355   cups_printer = GTK_PRINTER_CUPS (printer);
3356
3357   error = NULL;
3358
3359   GTK_NOTE (PRINTING,
3360             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3361
3362   if (cups_printer->remote)
3363     {
3364       GtkCupsConnectionState state;
3365
3366       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
3367
3368       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
3369         {
3370           if (cups_printer->get_remote_ppd_attempts == 60)
3371             {
3372               cups_printer->get_remote_ppd_attempts = -1;
3373               if (cups_printer->get_remote_ppd_poll > 0)
3374                 g_source_remove (cups_printer->get_remote_ppd_poll);
3375               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
3376                                                     (GSourceFunc) cups_request_ppd,
3377                                                     printer);
3378             }
3379           else if (cups_printer->get_remote_ppd_attempts != -1)
3380             cups_printer->get_remote_ppd_attempts++;
3381
3382           return TRUE;
3383         }
3384
3385       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
3386       cups_printer->remote_cups_connection_test = NULL;
3387       cups_printer->get_remote_ppd_poll = 0;
3388       cups_printer->get_remote_ppd_attempts = 0;
3389
3390       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3391         {
3392           g_signal_emit_by_name (printer, "details-acquired", FALSE);
3393           return FALSE;
3394         }
3395     }
3396
3397   http = httpConnectEncrypt (cups_printer->hostname,
3398                              cups_printer->port,
3399                              cupsEncryption ());
3400
3401   data = g_new0 (GetPPDData, 1);
3402
3403   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
3404                         &ppd_filename,
3405                         &error);
3406
3407 #ifdef G_ENABLE_DEBUG
3408   /* If we are debugging printing don't delete the tmp files */
3409   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
3410     unlink (ppd_filename);
3411 #else
3412   unlink (ppd_filename);
3413 #endif /* G_ENABLE_DEBUG */
3414
3415   if (error != NULL)
3416     {
3417       GTK_NOTE (PRINTING,
3418                 g_warning ("CUPS Backend: Failed to create temp file, %s\n",
3419                            error->message));
3420       g_error_free (error);
3421       httpClose (http);
3422       g_free (ppd_filename);
3423       g_free (data);
3424
3425       g_signal_emit_by_name (printer, "details-acquired", FALSE);
3426       return FALSE;
3427     }
3428
3429   data->http = http;
3430   fchmod (fd, S_IRUSR | S_IWUSR);
3431   data->ppd_io = g_io_channel_unix_new (fd);
3432   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
3433   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
3434
3435   data->printer = g_object_ref (printer);
3436
3437   resource = g_strdup_printf ("/printers/%s.ppd",
3438                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
3439
3440   print_backend = gtk_printer_get_backend (printer);
3441
3442   request = gtk_cups_request_new_with_username (data->http,
3443                                                 GTK_CUPS_GET,
3444                                                 0,
3445                                                 data->ppd_io,
3446                                                 cups_printer->hostname,
3447                                                 resource,
3448                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
3449
3450   gtk_cups_request_set_ipp_version (request,
3451                                     cups_printer->ipp_version_major,
3452                                     cups_printer->ipp_version_minor);
3453
3454   GTK_NOTE (PRINTING,
3455             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
3456
3457
3458   cups_printer->reading_ppd = TRUE;
3459   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
3460
3461   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
3462                         request,
3463                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
3464                         data,
3465                         (GDestroyNotify)get_ppd_data_free);
3466
3467   g_free (resource);
3468   g_free (ppd_filename);
3469
3470   return FALSE;
3471 }
3472
3473 /* Ordering matters for default preference */
3474 static const char *lpoptions_locations[] = {
3475   "/etc/cups/lpoptions",
3476   ".lpoptions",
3477   ".cups/lpoptions"
3478 };
3479
3480 static void
3481 cups_parse_user_default_printer (const char  *filename,
3482                                  char       **printer_name)
3483 {
3484   FILE *fp;
3485   char line[1024], *lineptr, *defname = NULL;
3486
3487   if ((fp = g_fopen (filename, "r")) == NULL)
3488     return;
3489
3490   while (fgets (line, sizeof (line), fp) != NULL)
3491     {
3492       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
3493         continue;
3494
3495       lineptr = line + 8;
3496       while (isspace (*lineptr))
3497         lineptr++;
3498
3499       if (!*lineptr)
3500         continue;
3501
3502       defname = lineptr;
3503       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
3504         lineptr++;
3505
3506       *lineptr = '\0';
3507
3508       if (*printer_name != NULL)
3509         g_free (*printer_name);
3510
3511       *printer_name = g_strdup (defname);
3512     }
3513
3514   fclose (fp);
3515 }
3516
3517 static void
3518 cups_get_user_default_printer (char **printer_name)
3519 {
3520   int i;
3521
3522   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
3523     {
3524       if (g_path_is_absolute (lpoptions_locations[i]))
3525         {
3526           cups_parse_user_default_printer (lpoptions_locations[i],
3527                                            printer_name);
3528         }
3529       else
3530         {
3531           char *filename;
3532
3533           filename = g_build_filename (g_get_home_dir (),
3534                                        lpoptions_locations[i], NULL);
3535           cups_parse_user_default_printer (filename, printer_name);
3536           g_free (filename);
3537         }
3538     }
3539 }
3540
3541 static int
3542 cups_parse_user_options (const char     *filename,
3543                          const char     *printer_name,
3544                          int             num_options,
3545                          cups_option_t **options)
3546 {
3547   FILE *fp;
3548   gchar line[1024], *lineptr, *name;
3549
3550   if ((fp = g_fopen (filename, "r")) == NULL)
3551     return num_options;
3552
3553   while (fgets (line, sizeof (line), fp) != NULL)
3554     {
3555       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
3556         lineptr = line + 4;
3557       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
3558         lineptr = line + 7;
3559       else
3560         continue;
3561
3562       /* Skip leading whitespace */
3563       while (isspace (*lineptr))
3564         lineptr++;
3565
3566       if (!*lineptr)
3567         continue;
3568
3569       /* NUL-terminate the name, stripping the instance name */
3570       name = lineptr;
3571       while (!isspace (*lineptr) && *lineptr)
3572         {
3573           if (*lineptr == '/')
3574             *lineptr = '\0';
3575           lineptr++;
3576         }
3577
3578       if (!*lineptr)
3579         continue;
3580
3581       *lineptr++ = '\0';
3582
3583       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
3584           continue;
3585
3586       /* We found our printer, parse the options */
3587       num_options = cupsParseOptions (lineptr, num_options, options);
3588     }
3589
3590   fclose (fp);
3591
3592   return num_options;
3593 }
3594
3595 static int
3596 cups_get_user_options (const char     *printer_name,
3597                        int             num_options,
3598                        cups_option_t **options)
3599 {
3600   int i;
3601
3602   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
3603     {
3604       if (g_path_is_absolute (lpoptions_locations[i]))
3605         {
3606            num_options = cups_parse_user_options (lpoptions_locations[i],
3607                                                   printer_name,
3608                                                   num_options,
3609                                                   options);
3610         }
3611       else
3612         {
3613           char *filename;
3614
3615           filename = g_build_filename (g_get_home_dir (),
3616                                        lpoptions_locations[i], NULL);
3617           num_options = cups_parse_user_options (filename, printer_name,
3618                                                  num_options, options);
3619           g_free (filename);
3620         }
3621     }
3622
3623   return num_options;
3624 }
3625
3626 /* This function requests default printer from a CUPS server in regular intervals.
3627  * In the case of unreachable CUPS server the request is repeated later.
3628  * The default printer is not requested in the case of previous success.
3629  */
3630 static void
3631 cups_get_default_printer (GtkPrintBackendCups *backend)
3632 {
3633   GtkPrintBackendCups *cups_backend;
3634
3635   cups_backend = backend;
3636
3637   if (cups_backend->cups_connection_test == NULL)
3638     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
3639
3640   if (cups_backend->default_printer_poll == 0)
3641     {
3642       if (cups_request_default_printer (cups_backend))
3643         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
3644                                                (GSourceFunc) cups_request_default_printer,
3645                                                backend);
3646     }
3647 }
3648
3649 /* This function gets default printer from local settings.*/
3650 static void
3651 cups_get_local_default_printer (GtkPrintBackendCups *backend)
3652 {
3653   const char *str;
3654   char *name = NULL;
3655
3656   if ((str = g_getenv ("LPDEST")) != NULL)
3657     {
3658       backend->default_printer = g_strdup (str);
3659       backend->got_default_printer = TRUE;
3660       return;
3661     }
3662   else if ((str = g_getenv ("PRINTER")) != NULL &&
3663            strcmp (str, "lp") != 0)
3664     {
3665       backend->default_printer = g_strdup (str);
3666       backend->got_default_printer = TRUE;
3667       return;
3668     }
3669
3670   /* Figure out user setting for default printer */
3671   cups_get_user_default_printer (&name);
3672   if (name != NULL)
3673     {
3674       backend->default_printer = name;
3675       backend->got_default_printer = TRUE;
3676       return;
3677     }
3678 }
3679
3680 static void
3681 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
3682                                  GtkCupsResult       *result,
3683                                  gpointer             user_data)
3684 {
3685   ipp_t *response;
3686   ipp_attribute_t *attr;
3687   GtkPrinter *printer;
3688
3689   gdk_threads_enter ();
3690
3691   if (gtk_cups_result_is_error (result))
3692     {
3693       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
3694           gtk_cups_result_get_error_code (result) == 1)
3695         {
3696           /* Canceled by user, stop popping up more password dialogs */
3697           if (print_backend->list_printers_poll > 0)
3698             g_source_remove (print_backend->list_printers_poll);
3699           print_backend->list_printers_poll = 0;
3700         }
3701
3702       return;
3703     }
3704
3705   response = gtk_cups_result_get_response (result);
3706
3707   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
3708       print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
3709
3710   print_backend->got_default_printer = TRUE;
3711
3712   if (print_backend->default_printer != NULL)
3713     {
3714       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
3715       if (printer != NULL)
3716         {
3717           gtk_printer_set_is_default (printer, TRUE);
3718           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
3719         }
3720     }
3721
3722   /* Make sure to kick off get_printers if we are polling it,
3723    * as we could have blocked this reading the default printer
3724    */
3725   if (print_backend->list_printers_poll != 0)
3726     cups_request_printer_list (print_backend);
3727
3728   gdk_threads_leave ();
3729 }
3730
3731 static gboolean
3732 cups_request_default_printer (GtkPrintBackendCups *print_backend)
3733 {
3734   GtkCupsConnectionState state;
3735   GtkCupsRequest *request;
3736
3737   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
3738   update_backend_status (print_backend, state);
3739
3740   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3741     return TRUE;
3742
3743   request = gtk_cups_request_new_with_username (NULL,
3744                                                 GTK_CUPS_POST,
3745                                                 CUPS_GET_DEFAULT,
3746                                                 NULL,
3747                                                 NULL,
3748                                                 NULL,
3749                                                 print_backend->username);
3750
3751   cups_request_execute (print_backend,
3752                         request,
3753                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
3754                         g_object_ref (print_backend),
3755                         g_object_unref);
3756
3757   return FALSE;
3758 }
3759
3760 static void
3761 cups_printer_request_details (GtkPrinter *printer)
3762 {
3763   GtkPrinterCups *cups_printer;
3764
3765   cups_printer = GTK_PRINTER_CUPS (printer);
3766   if (!cups_printer->reading_ppd &&
3767       gtk_printer_cups_get_ppd (cups_printer) == NULL)
3768     {
3769       if (cups_printer->remote)
3770         {
3771           if (cups_printer->get_remote_ppd_poll == 0)
3772             {
3773               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
3774
3775               if (cups_request_ppd (printer))
3776                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
3777                                                     (GSourceFunc) cups_request_ppd,
3778                                                     printer);
3779             }
3780         }
3781       else
3782         cups_request_ppd (printer);
3783     }
3784 }
3785
3786 static char *
3787 ppd_text_to_utf8 (ppd_file_t *ppd_file,
3788                   const char *text)
3789 {
3790   const char *encoding = NULL;
3791   char *res;
3792
3793   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
3794     {
3795       return g_strdup (text);
3796     }
3797   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
3798     {
3799       encoding = "ISO-8859-1";
3800     }
3801   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
3802     {
3803       encoding = "ISO-8859-2";
3804     }
3805   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
3806     {
3807       encoding = "ISO-8859-5";
3808     }
3809   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
3810     {
3811       encoding = "SHIFT-JIS";
3812     }
3813   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
3814     {
3815       encoding = "MACINTOSH";
3816     }
3817   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
3818     {
3819       encoding = "WINDOWS-1252";
3820     }
3821   else
3822     {
3823       /* Fallback, try iso-8859-1... */
3824       encoding = "ISO-8859-1";
3825     }
3826
3827   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
3828
3829   if (res == NULL)
3830     {
3831       GTK_NOTE (PRINTING,
3832                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
3833       res = g_strdup ("???");
3834     }
3835
3836   return res;
3837 }
3838
3839 /* TODO: Add more translations for common settings here */
3840
3841 static const struct {
3842   const char *keyword;
3843   const char *translation;
3844 } cups_option_translations[] = {
3845   { "Duplex", N_("Two Sided") },
3846   { "MediaType", N_("Paper Type") },
3847   { "InputSlot", N_("Paper Source") },
3848   { "OutputBin", N_("Output Tray") },
3849   { "Resolution", N_("Resolution") },
3850   { "PreFilter", N_("GhostScript pre-filtering") },
3851 };
3852
3853
3854 static const struct {
3855   const char *keyword;
3856   const char *choice;
3857   const char *translation;
3858 } cups_choice_translations[] = {
3859   { "Duplex", "None", N_("One Sided") },
3860   /* Translators: this is an option of "Two Sided" */
3861   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
3862   /* Translators: this is an option of "Two Sided" */
3863   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
3864   /* Translators: this is an option of "Paper Source" */
3865   { "InputSlot", "Auto", N_("Auto Select") },
3866   /* Translators: this is an option of "Paper Source" */
3867   { "InputSlot", "AutoSelect", N_("Auto Select") },
3868   /* Translators: this is an option of "Paper Source" */
3869   { "InputSlot", "Default", N_("Printer Default") },
3870   /* Translators: this is an option of "Paper Source" */
3871   { "InputSlot", "None", N_("Printer Default") },
3872   /* Translators: this is an option of "Paper Source" */
3873   { "InputSlot", "PrinterDefault", N_("Printer Default") },
3874   /* Translators: this is an option of "Paper Source" */
3875   { "InputSlot", "Unspecified", N_("Auto Select") },
3876   /* Translators: this is an option of "Resolution" */
3877   { "Resolution", "default", N_("Printer Default") },
3878   /* Translators: this is an option of "GhostScript" */
3879   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
3880   /* Translators: this is an option of "GhostScript" */
3881   { "PreFilter", "Level1", N_("Convert to PS level 1") },
3882   /* Translators: this is an option of "GhostScript" */
3883   { "PreFilter", "Level2", N_("Convert to PS level 2") },
3884   /* Translators: this is an option of "GhostScript" */
3885   { "PreFilter", "No", N_("No pre-filtering") },
3886 };
3887
3888 static const struct {
3889   const char *name;
3890   const char *translation;
3891 } cups_group_translations[] = {
3892 /* Translators: "Miscellaneous" is the label for a button, that opens
3893    up an extra panel of settings in a print dialog. */
3894   { "Miscellaneous", N_("Miscellaneous") },
3895 };
3896
3897 static const struct {
3898   const char *ppd_keyword;
3899   const char *name;
3900 } ppd_option_names[] = {
3901   {"Duplex", "gtk-duplex" },
3902   {"MediaType", "gtk-paper-type"},
3903   {"InputSlot", "gtk-paper-source"},
3904   {"OutputBin", "gtk-output-tray"},
3905 };
3906
3907 static const struct {
3908   const char *lpoption;
3909   const char *name;
3910 } lpoption_names[] = {
3911   {"number-up", "gtk-n-up" },
3912   {"number-up-layout", "gtk-n-up-layout"},
3913   {"job-billing", "gtk-billing-info"},
3914   {"job-priority", "gtk-job-prio"},
3915 };
3916
3917 /* keep sorted when changing */
3918 static const char *color_option_whitelist[] = {
3919   "BRColorEnhancement",
3920   "BRColorMatching",
3921   "BRColorMatching",
3922   "BRColorMode",
3923   "BRGammaValue",
3924   "BRImprovedGray",
3925   "BlackSubstitution",
3926   "ColorModel",
3927   "HPCMYKInks",
3928   "HPCSGraphics",
3929   "HPCSImages",
3930   "HPCSText",
3931   "HPColorSmart",
3932   "RPSBlackMode",
3933   "RPSBlackOverPrint",
3934   "Rcmyksimulation",
3935 };
3936
3937 /* keep sorted when changing */
3938 static const char *color_group_whitelist[] = {
3939   "ColorPage",
3940   "FPColorWise1",
3941   "FPColorWise2",
3942   "FPColorWise3",
3943   "FPColorWise4",
3944   "FPColorWise5",
3945   "HPColorOptionsPanel",
3946 };
3947
3948 /* keep sorted when changing */
3949 static const char *image_quality_option_whitelist[] = {
3950   "BRDocument",
3951   "BRHalfTonePattern",
3952   "BRNormalPrt",
3953   "BRPrintQuality",
3954   "BitsPerPixel",
3955   "Darkness",
3956   "Dithering",
3957   "EconoMode",
3958   "Economode",
3959   "HPEconoMode",
3960   "HPEdgeControl",
3961   "HPGraphicsHalftone",
3962   "HPHalftone",
3963   "HPLJDensity",
3964   "HPPhotoHalftone",
3965   "OutputMode",
3966   "REt",
3967   "RPSBitsPerPixel",
3968   "RPSDitherType",
3969   "Resolution",
3970   "ScreenLock",
3971   "Smoothing",
3972   "TonerSaveMode",
3973   "UCRGCRForImage",
3974 };
3975
3976 /* keep sorted when changing */
3977 static const char *image_quality_group_whitelist[] = {
3978   "FPImageQuality1",
3979   "FPImageQuality2",
3980   "FPImageQuality3",
3981   "ImageQualityPage",
3982 };
3983
3984 /* keep sorted when changing */
3985 static const char * finishing_option_whitelist[] = {
3986   "BindColor",
3987   "BindEdge",
3988   "BindType",
3989   "BindWhen",
3990   "Booklet",
3991   "FoldType",
3992   "FoldWhen",
3993   "HPStaplerOptions",
3994   "Jog",
3995   "Slipsheet",
3996   "Sorter",
3997   "StapleLocation",
3998   "StapleOrientation",
3999   "StapleWhen",
4000   "StapleX",
4001   "StapleY",
4002 };
4003
4004 /* keep sorted when changing */
4005 static const char *finishing_group_whitelist[] = {
4006   "FPFinishing1",
4007   "FPFinishing2",
4008   "FPFinishing3",
4009   "FPFinishing4",
4010   "FinishingPage",
4011   "HPFinishingPanel",
4012 };
4013
4014 /* keep sorted when changing */
4015 static const char *cups_option_blacklist[] = {
4016   "Collate",
4017   "Copies",
4018   "OutputOrder",
4019   "PageRegion",
4020   "PageSize",
4021 };
4022
4023 static char *
4024 get_option_text (ppd_file_t   *ppd_file,
4025                  ppd_option_t *option)
4026 {
4027   int i;
4028   char *utf8;
4029
4030   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
4031     {
4032       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
4033         return g_strdup (_(cups_option_translations[i].translation));
4034     }
4035
4036   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
4037
4038   /* Some ppd files have spaces in the text before the colon */
4039   g_strchomp (utf8);
4040
4041   return utf8;
4042 }
4043
4044 static char *
4045 get_choice_text (ppd_file_t   *ppd_file,
4046                  ppd_choice_t *choice)
4047 {
4048   int i;
4049   ppd_option_t *option = choice->option;
4050   const char *keyword = option->keyword;
4051
4052   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
4053     {
4054       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
4055           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
4056         return g_strdup (_(cups_choice_translations[i].translation));
4057     }
4058   return ppd_text_to_utf8 (ppd_file, choice->text);
4059 }
4060
4061 static gboolean
4062 group_has_option (ppd_group_t  *group,
4063                   ppd_option_t *option)
4064 {
4065   int i;
4066
4067   if (group == NULL)
4068     return FALSE;
4069
4070   if (group->num_options > 0 &&
4071       option >= group->options && option < group->options + group->num_options)
4072     return TRUE;
4073
4074   for (i = 0; i < group->num_subgroups; i++)
4075     {
4076       if (group_has_option (&group->subgroups[i],option))
4077         return TRUE;
4078     }
4079   return FALSE;
4080 }
4081
4082 static void
4083 set_option_off (GtkPrinterOption *option)
4084 {
4085   /* Any of these will do, _set only applies the value
4086    * if its allowed of the option */
4087   gtk_printer_option_set (option, "False");
4088   gtk_printer_option_set (option, "Off");
4089   gtk_printer_option_set (option, "None");
4090 }
4091
4092 static gboolean
4093 value_is_off (const char *value)
4094 {
4095   return  (strcasecmp (value, "None") == 0 ||
4096            strcasecmp (value, "Off") == 0 ||
4097            strcasecmp (value, "False") == 0);
4098 }
4099
4100 static char *
4101 ppd_group_name (ppd_group_t *group)
4102 {
4103   return group->name;
4104 }
4105
4106 static int
4107 available_choices (ppd_file_t     *ppd,
4108                    ppd_option_t   *option,
4109                    ppd_choice_t ***available,
4110                    gboolean        keep_if_only_one_option)
4111 {
4112   ppd_option_t *other_option;
4113   int i, j;
4114   gchar *conflicts;
4115   ppd_const_t *constraint;
4116   const char *choice, *other_choice;
4117   ppd_option_t *option1, *option2;
4118   ppd_group_t *installed_options;
4119   int num_conflicts;
4120   gboolean all_default;
4121   int add_auto;
4122
4123   if (available)
4124     *available = NULL;
4125
4126   conflicts = g_new0 (char, option->num_choices);
4127
4128   installed_options = NULL;
4129   for (i = 0; i < ppd->num_groups; i++)
4130     {
4131       char *name;
4132
4133       name = ppd_group_name (&ppd->groups[i]);
4134       if (strcmp (name, "InstallableOptions") == 0)
4135         {
4136           installed_options = &ppd->groups[i];
4137           break;
4138         }
4139     }
4140
4141   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
4142     {
4143       option1 = ppdFindOption (ppd, constraint->option1);
4144       if (option1 == NULL)
4145         continue;
4146
4147       option2 = ppdFindOption (ppd, constraint->option2);
4148       if (option2 == NULL)
4149         continue;
4150
4151       if (option == option1)
4152         {
4153           choice = constraint->choice1;
4154           other_option = option2;
4155           other_choice = constraint->choice2;
4156         }
4157       else if (option == option2)
4158         {
4159           choice = constraint->choice2;
4160           other_option = option1;
4161           other_choice = constraint->choice1;
4162         }
4163       else
4164         continue;
4165
4166       /* We only care of conflicts with installed_options and
4167          PageSize */
4168       if (!group_has_option (installed_options, other_option) &&
4169           (strcmp (other_option->keyword, "PageSize") != 0))
4170         continue;
4171
4172       if (*other_choice == 0)
4173         {
4174           /* Conflict only if the installed option is not off */
4175           if (value_is_off (other_option->defchoice))
4176             continue;
4177         }
4178       /* Conflict if the installed option has the specified default */
4179       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
4180         continue;
4181
4182       if (*choice == 0)
4183         {
4184           /* Conflict with all non-off choices */
4185           for (j = 0; j < option->num_choices; j++)
4186             {
4187               if (!value_is_off (option->choices[j].choice))
4188                 conflicts[j] = 1;
4189             }
4190         }
4191       else
4192         {
4193           for (j = 0; j < option->num_choices; j++)
4194             {
4195               if (strcasecmp (option->choices[j].choice, choice) == 0)
4196                 conflicts[j] = 1;
4197             }
4198         }
4199     }
4200
4201   num_conflicts = 0;
4202   all_default = TRUE;
4203   for (j = 0; j < option->num_choices; j++)
4204     {
4205       if (conflicts[j])
4206         num_conflicts++;
4207       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
4208         all_default = FALSE;
4209     }
4210
4211   if ((all_default && !keep_if_only_one_option) ||
4212       (num_conflicts == option->num_choices))
4213     {
4214       g_free (conflicts);
4215
4216       return 0;
4217     }
4218
4219   /* Some ppds don't have a "use printer default" option for
4220    * InputSlot. This means you always have to select a particular slot,
4221    * and you can't auto-pick source based on the paper size. To support
4222    * this we always add an auto option if there isn't one already. If
4223    * the user chooses the generated option we don't send any InputSlot
4224    * value when printing. The way we detect existing auto-cases is based
4225    * on feedback from Michael Sweet of cups fame.
4226    */
4227   add_auto = 0;
4228   if (strcmp (option->keyword, "InputSlot") == 0)
4229     {
4230       gboolean found_auto = FALSE;
4231       for (j = 0; j < option->num_choices; j++)
4232         {
4233           if (!conflicts[j])
4234             {
4235               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
4236                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
4237                   strcmp (option->choices[j].choice, "Default") == 0 ||
4238                   strcmp (option->choices[j].choice, "None") == 0 ||
4239                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
4240                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
4241                   option->choices[j].code == NULL ||
4242                   option->choices[j].code[0] == 0)
4243                 {
4244                   found_auto = TRUE;
4245                   break;
4246                 }
4247             }
4248         }
4249
4250       if (!found_auto)
4251         add_auto = 1;
4252     }
4253
4254   if (available)
4255     {
4256       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
4257
4258       i = 0;
4259       for (j = 0; j < option->num_choices; j++)
4260         {
4261           if (!conflicts[j])
4262             (*available)[i++] = &option->choices[j];
4263         }
4264
4265       if (add_auto)
4266         (*available)[i++] = NULL;
4267     }
4268
4269   g_free (conflicts);
4270
4271   return option->num_choices - num_conflicts + add_auto;
4272 }
4273
4274 static GtkPrinterOption *
4275 create_pickone_option (ppd_file_t   *ppd_file,
4276                        ppd_option_t *ppd_option,
4277                        const gchar  *gtk_name)
4278 {
4279   GtkPrinterOption *option;
4280   ppd_choice_t **available;
4281   char *label;
4282   int n_choices;
4283   int i;
4284   ppd_coption_t *coption;
4285
4286   g_assert (ppd_option->ui == PPD_UI_PICKONE);
4287
4288   option = NULL;
4289
4290   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
4291   if (n_choices > 0)
4292     {
4293
4294       /* right now only support one parameter per custom option
4295        * if more than one print warning and only offer the default choices
4296        */
4297
4298       label = get_option_text (ppd_file, ppd_option);
4299
4300       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
4301
4302       if (coption)
4303         {
4304           ppd_cparam_t *cparam;
4305
4306           cparam = ppdFirstCustomParam (coption);
4307
4308           if (ppdNextCustomParam (coption) == NULL)
4309             {
4310               switch (cparam->type)
4311                 {
4312                 case PPD_CUSTOM_INT:
4313                   option = gtk_printer_option_new (gtk_name, label,
4314                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
4315                   break;
4316                 case PPD_CUSTOM_PASSCODE:
4317                   option = gtk_printer_option_new (gtk_name, label,
4318                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
4319                   break;
4320                 case PPD_CUSTOM_PASSWORD:
4321                     option = gtk_printer_option_new (gtk_name, label,
4322                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
4323                   break;
4324                case PPD_CUSTOM_REAL:
4325                     option = gtk_printer_option_new (gtk_name, label,
4326                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
4327                   break;
4328                 case PPD_CUSTOM_STRING:
4329                   option = gtk_printer_option_new (gtk_name, label,
4330                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
4331                   break;
4332 #ifdef PRINT_IGNORED_OPTIONS
4333                 case PPD_CUSTOM_POINTS:
4334                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
4335                   break;
4336                 case PPD_CUSTOM_CURVE:
4337                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
4338                   break;
4339                 case PPD_CUSTOM_INVCURVE:
4340                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
4341                   break;
4342 #endif
4343                 default:
4344                   break;
4345                 }
4346             }
4347 #ifdef PRINT_IGNORED_OPTIONS
4348           else
4349             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
4350 #endif
4351         }
4352
4353       if (!option)
4354         option = gtk_printer_option_new (gtk_name, label,
4355                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
4356       g_free (label);
4357
4358       gtk_printer_option_allocate_choices (option, n_choices);
4359       for (i = 0; i < n_choices; i++)
4360         {
4361           if (available[i] == NULL)
4362             {
4363               /* This was auto-added */
4364               option->choices[i] = g_strdup ("gtk-ignore-value");
4365               option->choices_display[i] = g_strdup (_("Printer Default"));
4366             }
4367           else
4368             {
4369               option->choices[i] = g_strdup (available[i]->choice);
4370               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
4371             }
4372         }
4373
4374       if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
4375         {
4376           if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
4377             gtk_printer_option_set (option, ppd_option->defchoice + 7);
4378           else
4379             gtk_printer_option_set (option, ppd_option->defchoice);
4380         }
4381       else
4382         {
4383           gtk_printer_option_set (option, ppd_option->defchoice);
4384         }
4385     }
4386 #ifdef PRINT_IGNORED_OPTIONS
4387   else
4388     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
4389 #endif
4390   g_free (available);
4391
4392   return option;
4393 }
4394
4395 static GtkPrinterOption *
4396 create_boolean_option (ppd_file_t   *ppd_file,
4397                        ppd_option_t *ppd_option,
4398                        const gchar  *gtk_name)
4399 {
4400   GtkPrinterOption *option;
4401   ppd_choice_t **available;
4402   char *label;
4403   int n_choices;
4404
4405   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
4406
4407   option = NULL;
4408
4409   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
4410   if (n_choices == 2)
4411     {
4412       label = get_option_text (ppd_file, ppd_option);
4413       option = gtk_printer_option_new (gtk_name, label,
4414                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
4415       g_free (label);
4416
4417       gtk_printer_option_allocate_choices (option, 2);
4418       option->choices[0] = g_strdup ("True");
4419       option->choices_display[0] = g_strdup ("True");
4420       option->choices[1] = g_strdup ("False");
4421       option->choices_display[1] = g_strdup ("False");
4422
4423       gtk_printer_option_set (option, ppd_option->defchoice);
4424     }
4425 #ifdef PRINT_IGNORED_OPTIONS
4426   else
4427     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
4428 #endif
4429   g_free (available);
4430
4431   return option;
4432 }
4433
4434 static gchar *
4435 get_ppd_option_name (const gchar *keyword)
4436 {
4437   int i;
4438
4439   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
4440     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
4441       return g_strdup (ppd_option_names[i].name);
4442
4443   return g_strdup_printf ("cups-%s", keyword);
4444 }
4445
4446 static gchar *
4447 get_lpoption_name (const gchar *lpoption)
4448 {
4449   int i;
4450
4451   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
4452     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
4453       return g_strdup (ppd_option_names[i].name);
4454
4455   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
4456     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
4457       return g_strdup (lpoption_names[i].name);
4458
4459   return g_strdup_printf ("cups-%s", lpoption);
4460 }
4461
4462 static int
4463 strptr_cmp (const void *a,
4464             const void *b)
4465 {
4466   char **aa = (char **)a;
4467   char **bb = (char **)b;
4468   return strcmp (*aa, *bb);
4469 }
4470
4471
4472 static gboolean
4473 string_in_table (gchar       *str,
4474                  const gchar *table[],
4475                  gint         table_len)
4476 {
4477   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
4478 }
4479
4480 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
4481
4482 static void
4483 handle_option (GtkPrinterOptionSet *set,
4484                ppd_file_t          *ppd_file,
4485                ppd_option_t        *ppd_option,
4486                ppd_group_t         *toplevel_group,
4487                GtkPrintSettings    *settings)
4488 {
4489   GtkPrinterOption *option;
4490   char *name;
4491   int i;
4492
4493   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
4494     return;
4495
4496   name = get_ppd_option_name (ppd_option->keyword);
4497
4498   option = NULL;
4499   if (ppd_option->ui == PPD_UI_PICKONE)
4500     {
4501       option = create_pickone_option (ppd_file, ppd_option, name);
4502     }
4503   else if (ppd_option->ui == PPD_UI_BOOLEAN)
4504     {
4505       option = create_boolean_option (ppd_file, ppd_option, name);
4506     }
4507 #ifdef PRINT_IGNORED_OPTIONS
4508   else
4509     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
4510 #endif
4511
4512   if (option)
4513     {
4514       char *name;
4515
4516       name = ppd_group_name (toplevel_group);
4517       if (STRING_IN_TABLE (name,
4518                            color_group_whitelist) ||
4519           STRING_IN_TABLE (ppd_option->keyword,
4520                            color_option_whitelist))
4521         {
4522           option->group = g_strdup ("ColorPage");
4523         }
4524       else if (STRING_IN_TABLE (name,
4525                                 image_quality_group_whitelist) ||
4526                STRING_IN_TABLE (ppd_option->keyword,
4527                                 image_quality_option_whitelist))
4528         {
4529           option->group = g_strdup ("ImageQualityPage");
4530         }
4531       else if (STRING_IN_TABLE (name,
4532                                 finishing_group_whitelist) ||
4533                STRING_IN_TABLE (ppd_option->keyword,
4534                                 finishing_option_whitelist))
4535         {
4536           option->group = g_strdup ("FinishingPage");
4537         }
4538       else
4539         {
4540           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
4541             {
4542               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
4543                 {
4544                   option->group = g_strdup (_(cups_group_translations[i].translation));
4545                   break;
4546                 }
4547             }
4548
4549           if (i == G_N_ELEMENTS (cups_group_translations))
4550             option->group = g_strdup (toplevel_group->text);
4551         }
4552
4553       set_option_from_settings (option, settings);
4554
4555       gtk_printer_option_set_add (set, option);
4556     }
4557
4558   g_free (name);
4559 }
4560
4561 static void
4562 handle_group (GtkPrinterOptionSet *set,
4563               ppd_file_t          *ppd_file,
4564               ppd_group_t         *group,
4565               ppd_group_t         *toplevel_group,
4566               GtkPrintSettings    *settings)
4567 {
4568   gint i;
4569   gchar *name;
4570
4571   /* Ignore installable options */
4572   name = ppd_group_name (toplevel_group);
4573   if (strcmp (name, "InstallableOptions") == 0)
4574     return;
4575
4576   for (i = 0; i < group->num_options; i++)
4577     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
4578
4579   for (i = 0; i < group->num_subgroups; i++)
4580     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
4581
4582 }
4583
4584 #ifdef HAVE_COLORD
4585
4586 typedef struct {
4587         GtkPrintSettings     *settings;
4588         GtkPrinter           *printer;
4589 } GtkPrintBackendCupsColordHelper;
4590
4591 static void
4592 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
4593                                       GtkPrintBackendCupsColordHelper *helper)
4594 {
4595   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
4596                                     helper->settings,
4597                                     set);
4598 }
4599 #endif
4600
4601 static GtkPrinterOptionSet *
4602 cups_printer_get_options (GtkPrinter           *printer,
4603                           GtkPrintSettings     *settings,
4604                           GtkPageSetup         *page_setup,
4605                           GtkPrintCapabilities  capabilities)
4606 {
4607   GtkPrinterOptionSet *set;
4608   GtkPrinterOption *option;
4609   ppd_file_t *ppd_file;
4610   int i;
4611   char *print_at[] = { "now", "at", "on-hold" };
4612   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
4613   char *prio[] = {"100", "80", "50", "30" };
4614   /* Translators: These strings name the possible values of the
4615    * job priority option in the print dialog
4616    */
4617   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
4618   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
4619   /* Translators: These strings name the possible arrangements of
4620    * multiple pages on a sheet when printing
4621    */
4622   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
4623                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
4624                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
4625                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
4626   char *name;
4627   int num_opts;
4628   cups_option_t *opts = NULL;
4629   GtkPrintBackendCups *backend;
4630   GtkTextDirection text_direction;
4631   GtkPrinterCups *cups_printer = NULL;
4632 #ifdef HAVE_COLORD
4633   GtkPrintBackendCupsColordHelper *helper;
4634 #endif
4635   char *default_number_up;
4636
4637   set = gtk_printer_option_set_new ();
4638
4639   /* Cups specific, non-ppd related settings */
4640
4641   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
4642     prio_display[i] = _(prio_display[i]);
4643
4644   /* Translators, this string is used to label the job priority option
4645    * in the print dialog
4646    */
4647   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4648   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
4649                                          prio, prio_display);
4650   gtk_printer_option_set (option, "50");
4651   set_option_from_settings (option, settings);
4652   gtk_printer_option_set_add (set, option);
4653   g_object_unref (option);
4654
4655   /* Translators, this string is used to label the billing info entry
4656    * in the print dialog
4657    */
4658   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
4659   gtk_printer_option_set (option, "");
4660   set_option_from_settings (option, settings);
4661   gtk_printer_option_set_add (set, option);
4662   g_object_unref (option);
4663
4664   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
4665   cups_printer = GTK_PRINTER_CUPS (printer);
4666
4667   if (backend != NULL && printer != NULL)
4668     {
4669       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
4670       /* Translators, these strings are names for various 'standard' cover
4671        * pages that the printing system may support.
4672        */
4673       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
4674       char **cover = NULL;
4675       char **cover_display = NULL;
4676       char **cover_display_translated = NULL;
4677       gint num_of_covers = 0;
4678       gpointer value;
4679       gint j;
4680
4681        /* Translators, this string is used to label the pages-per-sheet option
4682         * in the print dialog
4683         */
4684       option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4685       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
4686                                              n_up, n_up);
4687       default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
4688       gtk_printer_option_set (option, default_number_up);
4689       g_free (default_number_up);
4690       set_option_from_settings (option, settings);
4691       gtk_printer_option_set_add (set, option);
4692       g_object_unref (option);
4693
4694       if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
4695         {
4696           for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
4697             n_up_layout_display[i] = _(n_up_layout_display[i]);
4698
4699            /* Translators, this string is used to label the option in the print
4700             * dialog that controls in what order multiple pages are arranged
4701             */
4702           option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4703           gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
4704                                                  n_up_layout, n_up_layout_display);
4705
4706           text_direction = gtk_widget_get_default_direction ();
4707           if (text_direction == GTK_TEXT_DIR_LTR)
4708             gtk_printer_option_set (option, "lrtb");
4709           else
4710             gtk_printer_option_set (option, "rltb");
4711
4712           set_option_from_settings (option, settings);
4713           gtk_printer_option_set_add (set, option);
4714           g_object_unref (option);
4715         }
4716
4717       num_of_covers = backend->number_of_covers;
4718       cover = g_new (char *, num_of_covers + 1);
4719       cover[num_of_covers] = NULL;
4720       cover_display = g_new (char *, num_of_covers + 1);
4721       cover_display[num_of_covers] = NULL;
4722       cover_display_translated = g_new (char *, num_of_covers + 1);
4723       cover_display_translated[num_of_covers] = NULL;
4724
4725       for (i = 0; i < num_of_covers; i++)
4726         {
4727           cover[i] = g_strdup (backend->covers[i]);
4728           value = NULL;
4729           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
4730             if (strcmp (cover_default[j], cover[i]) == 0)
4731               {
4732                 value = cover_display_default[j];
4733                 break;
4734               }
4735           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
4736         }
4737
4738       for (i = 0; i < num_of_covers; i++)
4739         cover_display_translated[i] = _(cover_display[i]);
4740
4741       /* Translators, this is the label used for the option in the print
4742        * dialog that controls the front cover page.
4743        */
4744       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4745       gtk_printer_option_choices_from_array (option, num_of_covers,
4746                                          cover, cover_display_translated);
4747
4748       if (cups_printer->default_cover_before != NULL)
4749         gtk_printer_option_set (option, cups_printer->default_cover_before);
4750       else
4751         gtk_printer_option_set (option, "none");
4752       set_option_from_settings (option, settings);
4753       gtk_printer_option_set_add (set, option);
4754       g_object_unref (option);
4755
4756       /* Translators, this is the label used for the option in the print
4757        * dialog that controls the back cover page.
4758        */
4759       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4760       gtk_printer_option_choices_from_array (option, num_of_covers,
4761                                          cover, cover_display_translated);
4762       if (cups_printer->default_cover_after != NULL)
4763         gtk_printer_option_set (option, cups_printer->default_cover_after);
4764       else
4765         gtk_printer_option_set (option, "none");
4766       set_option_from_settings (option, settings);
4767       gtk_printer_option_set_add (set, option);
4768       g_object_unref (option);
4769
4770       g_strfreev (cover);
4771       g_strfreev (cover_display);
4772       g_free (cover_display_translated);
4773     }
4774
4775   /* Translators: this is the name of the option that controls when
4776    * a print job is printed. Possible values are 'now', a specified time,
4777    * or 'on hold'
4778    */
4779   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4780   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
4781                                          print_at, print_at);
4782   gtk_printer_option_set (option, "now");
4783   set_option_from_settings (option, settings);
4784   gtk_printer_option_set_add (set, option);
4785   g_object_unref (option);
4786
4787   /* Translators: this is the name of the option that allows the user
4788    * to specify a time when a print job will be printed.
4789    */
4790   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
4791   gtk_printer_option_set (option, "");
4792   set_option_from_settings (option, settings);
4793   gtk_printer_option_set_add (set, option);
4794   g_object_unref (option);
4795
4796   /* Printer (ppd) specific settings */
4797   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4798   if (ppd_file)
4799     {
4800       GtkPaperSize *paper_size;
4801       ppd_option_t *option;
4802       const gchar  *ppd_name;
4803
4804       ppdMarkDefaults (ppd_file);
4805
4806       paper_size = gtk_page_setup_get_paper_size (page_setup);
4807
4808       option = ppdFindOption (ppd_file, "PageSize");
4809       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
4810
4811       if (ppd_name)
4812         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
4813       else
4814         {
4815           gchar *custom_name;
4816           char width[G_ASCII_DTOSTR_BUF_SIZE];
4817           char height[G_ASCII_DTOSTR_BUF_SIZE];
4818
4819           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4820           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4821           /* Translators: this format is used to display a custom paper
4822            * size. The two placeholders are replaced with the width and height
4823            * in points. E.g: "Custom 230.4x142.9"
4824            */
4825           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
4826           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
4827           g_free (custom_name);
4828         }
4829
4830       for (i = 0; i < ppd_file->num_groups; i++)
4831         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
4832     }
4833
4834   /* Now honor the user set defaults for this printer */
4835   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
4836
4837   for (i = 0; i < num_opts; i++)
4838     {
4839       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
4840         continue;
4841
4842       name = get_lpoption_name (opts[i].name);
4843       if (strcmp (name, "cups-job-sheets") == 0)
4844         {
4845           gchar **values;
4846           gint    num_values;
4847
4848           values = g_strsplit (opts[i].value, ",", 2);
4849           num_values = g_strv_length (values);
4850
4851           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
4852           if (option && num_values > 0)
4853             gtk_printer_option_set (option, g_strstrip (values[0]));
4854
4855           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
4856           if (option && num_values > 1)
4857             gtk_printer_option_set (option, g_strstrip (values[1]));
4858
4859           g_strfreev (values);
4860         }
4861       else if (strcmp (name, "cups-job-hold-until") == 0)
4862         {
4863           GtkPrinterOption *option2 = NULL;
4864
4865           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
4866           if (option && opts[i].value)
4867             {
4868               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
4869               if (option2)
4870                 {
4871                   if (strcmp (opts[i].value, "indefinite") == 0)
4872                     gtk_printer_option_set (option2, "on-hold");
4873                   else
4874                     {
4875                       gtk_printer_option_set (option2, "at");
4876                       gtk_printer_option_set (option, opts[i].value);
4877                     }
4878                 }
4879             }
4880         }
4881       else if (strcmp (name, "cups-sides") == 0)
4882         {
4883           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
4884           if (option && opts[i].value)
4885             {
4886               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
4887                 gtk_printer_option_set (option, "DuplexTumble");
4888               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
4889                 gtk_printer_option_set (option, "DuplexNoTumble");
4890             }
4891         }
4892       else
4893         {
4894           option = gtk_printer_option_set_lookup (set, name);
4895           if (option)
4896             gtk_printer_option_set (option, opts[i].value);
4897         }
4898       g_free (name);
4899     }
4900
4901   cupsFreeOptions (num_opts, opts);
4902
4903 #ifdef HAVE_COLORD
4904   /* TRANSLATORS: this this the ICC color profile to use for this job */
4905   option = gtk_printer_option_new ("colord-profile",
4906                                    _("Printer Profile"),
4907                                    GTK_PRINTER_OPTION_TYPE_INFO);
4908
4909   /* assign it to the color page */
4910   option->group = g_strdup ("ColorPage");
4911
4912   /* TRANSLATORS: this is when color profile information is unavailable */
4913   gtk_printer_option_set (option, _("Unavailable"));
4914   gtk_printer_option_set_add (set, option);
4915
4916   /* watch to see if the user changed the options */
4917   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
4918   helper->printer = printer;
4919   helper->settings = settings;
4920   g_signal_connect_data (set, "changed",
4921                          G_CALLBACK (colord_printer_option_set_changed_cb),
4922                          helper,
4923                          (GClosureNotify) g_free,
4924                          0);
4925
4926   /* initial coldplug */
4927   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
4928                                     settings, set);
4929   g_object_bind_property (printer, "profile-title",
4930                           option, "value",
4931                           G_BINDING_DEFAULT);
4932
4933 #endif
4934
4935   return set;
4936 }
4937
4938
4939 static void
4940 mark_option_from_set (GtkPrinterOptionSet *set,
4941                       ppd_file_t          *ppd_file,
4942                       ppd_option_t        *ppd_option)
4943 {
4944   GtkPrinterOption *option;
4945   char *name = get_ppd_option_name (ppd_option->keyword);
4946
4947   option = gtk_printer_option_set_lookup (set, name);
4948
4949   if (option)
4950     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
4951
4952   g_free (name);
4953 }
4954
4955
4956 static void
4957 mark_group_from_set (GtkPrinterOptionSet *set,
4958                      ppd_file_t          *ppd_file,
4959                      ppd_group_t         *group)
4960 {
4961   int i;
4962
4963   for (i = 0; i < group->num_options; i++)
4964     mark_option_from_set (set, ppd_file, &group->options[i]);
4965
4966   for (i = 0; i < group->num_subgroups; i++)
4967     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
4968 }
4969
4970 static void
4971 set_conflicts_from_option (GtkPrinterOptionSet *set,
4972                            ppd_file_t          *ppd_file,
4973                            ppd_option_t        *ppd_option)
4974 {
4975   GtkPrinterOption *option;
4976   char *name;
4977
4978   if (ppd_option->conflicted)
4979     {
4980       name = get_ppd_option_name (ppd_option->keyword);
4981       option = gtk_printer_option_set_lookup (set, name);
4982
4983       if (option)
4984         gtk_printer_option_set_has_conflict (option, TRUE);
4985 #ifdef PRINT_IGNORED_OPTIONS
4986       else
4987         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
4988 #endif
4989
4990       g_free (name);
4991     }
4992 }
4993
4994 static void
4995 set_conflicts_from_group (GtkPrinterOptionSet *set,
4996                           ppd_file_t          *ppd_file,
4997                           ppd_group_t         *group)
4998 {
4999   int i;
5000
5001   for (i = 0; i < group->num_options; i++)
5002     set_conflicts_from_option (set, ppd_file, &group->options[i]);
5003
5004   for (i = 0; i < group->num_subgroups; i++)
5005     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
5006 }
5007
5008 static gboolean
5009 cups_printer_mark_conflicts (GtkPrinter          *printer,
5010                              GtkPrinterOptionSet *options)
5011 {
5012   ppd_file_t *ppd_file;
5013   int num_conflicts;
5014   int i;
5015
5016   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5017
5018   if (ppd_file == NULL)
5019     return FALSE;
5020
5021   ppdMarkDefaults (ppd_file);
5022
5023   for (i = 0; i < ppd_file->num_groups; i++)
5024     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
5025
5026   num_conflicts = ppdConflicts (ppd_file);
5027
5028   if (num_conflicts > 0)
5029     {
5030       for (i = 0; i < ppd_file->num_groups; i++)
5031         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
5032     }
5033
5034   return num_conflicts > 0;
5035 }
5036
5037 struct OptionData {
5038   GtkPrinter *printer;
5039   GtkPrinterOptionSet *options;
5040   GtkPrintSettings *settings;
5041   ppd_file_t *ppd_file;
5042 };
5043
5044 typedef struct {
5045   const char *cups;
5046   const char *standard;
5047 } NameMapping;
5048
5049 static void
5050 map_settings_to_option (GtkPrinterOption  *option,
5051                         const NameMapping  table[],
5052                         gint               n_elements,
5053                         GtkPrintSettings  *settings,
5054                         const gchar       *standard_name,
5055                         const gchar       *cups_name)
5056 {
5057   int i;
5058   char *name;
5059   const char *cups_value;
5060   const char *standard_value;
5061
5062   /* If the cups-specific setting is set, always use that */
5063   name = g_strdup_printf ("cups-%s", cups_name);
5064   cups_value = gtk_print_settings_get (settings, name);
5065   g_free (name);
5066
5067   if (cups_value != NULL)
5068     {
5069       gtk_printer_option_set (option, cups_value);
5070       return;
5071     }
5072
5073   /* Otherwise we try to convert from the general setting */
5074   standard_value = gtk_print_settings_get (settings, standard_name);
5075   if (standard_value == NULL)
5076     return;
5077
5078   for (i = 0; i < n_elements; i++)
5079     {
5080       if (table[i].cups == NULL && table[i].standard == NULL)
5081         {
5082           gtk_printer_option_set (option, standard_value);
5083           break;
5084         }
5085       else if (table[i].cups == NULL &&
5086                strcmp (table[i].standard, standard_value) == 0)
5087         {
5088           set_option_off (option);
5089           break;
5090         }
5091       else if (strcmp (table[i].standard, standard_value) == 0)
5092         {
5093           gtk_printer_option_set (option, table[i].cups);
5094           break;
5095         }
5096     }
5097 }
5098
5099 static void
5100 map_option_to_settings (const gchar       *value,
5101                         const NameMapping  table[],
5102                         gint               n_elements,
5103                         GtkPrintSettings  *settings,
5104                         const gchar       *standard_name,
5105                         const gchar       *cups_name)
5106 {
5107   int i;
5108   char *name;
5109
5110   for (i = 0; i < n_elements; i++)
5111     {
5112       if (table[i].cups == NULL && table[i].standard == NULL)
5113         {
5114           gtk_print_settings_set (settings,
5115                                   standard_name,
5116                                   value);
5117           break;
5118         }
5119       else if (table[i].cups == NULL && table[i].standard != NULL)
5120         {
5121           if (value_is_off (value))
5122             {
5123               gtk_print_settings_set (settings,
5124                                       standard_name,
5125                                       table[i].standard);
5126               break;
5127             }
5128         }
5129       else if (strcmp (table[i].cups, value) == 0)
5130         {
5131           gtk_print_settings_set (settings,
5132                                   standard_name,
5133                                   table[i].standard);
5134           break;
5135         }
5136     }
5137
5138   /* Always set the corresponding cups-specific setting */
5139   name = g_strdup_printf ("cups-%s", cups_name);
5140   gtk_print_settings_set (settings, name, value);
5141   g_free (name);
5142 }
5143
5144
5145 static const NameMapping paper_source_map[] = {
5146   { "Lower", "lower"},
5147   { "Middle", "middle"},
5148   { "Upper", "upper"},
5149   { "Rear", "rear"},
5150   { "Envelope", "envelope"},
5151   { "Cassette", "cassette"},
5152   { "LargeCapacity", "large-capacity"},
5153   { "AnySmallFormat", "small-format"},
5154   { "AnyLargeFormat", "large-format"},
5155   { NULL, NULL}
5156 };
5157
5158 static const NameMapping output_tray_map[] = {
5159   { "Upper", "upper"},
5160   { "Lower", "lower"},
5161   { "Rear", "rear"},
5162   { NULL, NULL}
5163 };
5164
5165 static const NameMapping duplex_map[] = {
5166   { "DuplexTumble", "vertical" },
5167   { "DuplexNoTumble", "horizontal" },
5168   { NULL, "simplex" }
5169 };
5170
5171 static const NameMapping output_mode_map[] = {
5172   { "Standard", "normal" },
5173   { "Normal", "normal" },
5174   { "Draft", "draft" },
5175   { "Fast", "draft" },
5176 };
5177
5178 static const NameMapping media_type_map[] = {
5179   { "Transparency", "transparency"},
5180   { "Standard", "stationery"},
5181   { NULL, NULL}
5182 };
5183
5184 static const NameMapping all_map[] = {
5185   { NULL, NULL}
5186 };
5187
5188
5189 static void
5190 set_option_from_settings (GtkPrinterOption *option,
5191                           GtkPrintSettings *settings)
5192 {
5193   const char *cups_value;
5194   char *value;
5195
5196   if (settings == NULL)
5197     return;
5198
5199   if (strcmp (option->name, "gtk-paper-source") == 0)
5200     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
5201                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
5202   else if (strcmp (option->name, "gtk-output-tray") == 0)
5203     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
5204                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
5205   else if (strcmp (option->name, "gtk-duplex") == 0)
5206     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
5207                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
5208   else if (strcmp (option->name, "cups-OutputMode") == 0)
5209     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
5210                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
5211   else if (strcmp (option->name, "cups-Resolution") == 0)
5212     {
5213       cups_value = gtk_print_settings_get (settings, option->name);
5214       if (cups_value)
5215         gtk_printer_option_set (option, cups_value);
5216       else
5217         {
5218           if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
5219               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
5220               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
5221               option->value == NULL || option->value[0] == '\0')
5222             {
5223               int res = gtk_print_settings_get_resolution (settings);
5224               int res_x = gtk_print_settings_get_resolution_x (settings);
5225               int res_y = gtk_print_settings_get_resolution_y (settings);
5226
5227               if (res_x != res_y)
5228                 {
5229                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
5230                   gtk_printer_option_set (option, value);
5231                   g_free (value);
5232                 }
5233               else if (res != 0)
5234                 {
5235                   value = g_strdup_printf ("%ddpi", res);
5236                   gtk_printer_option_set (option, value);
5237                   g_free (value);
5238                 }
5239             }
5240         }
5241     }
5242   else if (strcmp (option->name, "gtk-paper-type") == 0)
5243     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
5244                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
5245   else if (strcmp (option->name, "gtk-n-up") == 0)
5246     {
5247       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
5248                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
5249     }
5250   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
5251     {
5252       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
5253                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
5254     }
5255   else if (strcmp (option->name, "gtk-billing-info") == 0)
5256     {
5257       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
5258       if (cups_value)
5259         gtk_printer_option_set (option, cups_value);
5260     }
5261   else if (strcmp (option->name, "gtk-job-prio") == 0)
5262     {
5263       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
5264       if (cups_value)
5265         gtk_printer_option_set (option, cups_value);
5266     }
5267   else if (strcmp (option->name, "gtk-cover-before") == 0)
5268     {
5269       cups_value = gtk_print_settings_get (settings, "cover-before");
5270       if (cups_value)
5271         gtk_printer_option_set (option, cups_value);
5272     }
5273   else if (strcmp (option->name, "gtk-cover-after") == 0)
5274     {
5275       cups_value = gtk_print_settings_get (settings, "cover-after");
5276       if (cups_value)
5277         gtk_printer_option_set (option, cups_value);
5278     }
5279   else if (strcmp (option->name, "gtk-print-time") == 0)
5280     {
5281       cups_value = gtk_print_settings_get (settings, "print-at");
5282       if (cups_value)
5283         gtk_printer_option_set (option, cups_value);
5284     }
5285   else if (strcmp (option->name, "gtk-print-time-text") == 0)
5286     {
5287       cups_value = gtk_print_settings_get (settings, "print-at-time");
5288       if (cups_value)
5289         gtk_printer_option_set (option, cups_value);
5290     }
5291   else if (g_str_has_prefix (option->name, "cups-"))
5292     {
5293       cups_value = gtk_print_settings_get (settings, option->name);
5294       if (cups_value)
5295         gtk_printer_option_set (option, cups_value);
5296     }
5297 }
5298
5299 static void
5300 foreach_option_get_settings (GtkPrinterOption *option,
5301                              gpointer          user_data)
5302 {
5303   struct OptionData *data = user_data;
5304   GtkPrintSettings *settings = data->settings;
5305   const char *value;
5306
5307   value = option->value;
5308
5309   if (strcmp (option->name, "gtk-paper-source") == 0)
5310     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
5311                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
5312   else if (strcmp (option->name, "gtk-output-tray") == 0)
5313     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
5314                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
5315   else if (strcmp (option->name, "gtk-duplex") == 0)
5316     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
5317                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
5318   else if (strcmp (option->name, "cups-OutputMode") == 0)
5319     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
5320                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
5321   else if (strcmp (option->name, "cups-Resolution") == 0)
5322     {
5323       int res, res_x, res_y;
5324
5325       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
5326         {
5327           if (res_x > 0 && res_y > 0)
5328             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
5329         }
5330       else if (sscanf (value, "%ddpi", &res) == 1)
5331         {
5332           if (res > 0)
5333             gtk_print_settings_set_resolution (settings, res);
5334         }
5335
5336       gtk_print_settings_set (settings, option->name, value);
5337     }
5338   else if (strcmp (option->name, "gtk-paper-type") == 0)
5339     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
5340                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
5341   else if (strcmp (option->name, "gtk-n-up") == 0)
5342     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
5343                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
5344   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
5345     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
5346                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
5347   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
5348     gtk_print_settings_set (settings, "cups-job-billing", value);
5349   else if (strcmp (option->name, "gtk-job-prio") == 0)
5350     gtk_print_settings_set (settings, "cups-job-priority", value);
5351   else if (strcmp (option->name, "gtk-cover-before") == 0)
5352     gtk_print_settings_set (settings, "cover-before", value);
5353   else if (strcmp (option->name, "gtk-cover-after") == 0)
5354     gtk_print_settings_set (settings, "cover-after", value);
5355   else if (strcmp (option->name, "gtk-print-time") == 0)
5356     gtk_print_settings_set (settings, "print-at", value);
5357   else if (strcmp (option->name, "gtk-print-time-text") == 0)
5358     gtk_print_settings_set (settings, "print-at-time", value);
5359   else if (g_str_has_prefix (option->name, "cups-"))
5360     gtk_print_settings_set (settings, option->name, value);
5361 }
5362
5363 static gboolean
5364 supports_am_pm (void)
5365 {
5366   struct tm tmp_tm = { 0 };
5367   char   time[8];
5368   int    length;
5369
5370   length = strftime (time, sizeof (time), "%p", &tmp_tm);
5371
5372   return length != 0;
5373 }
5374
5375 /* Converts local time to UTC time. Local time has to be in one of these
5376  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
5377  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
5378  * Returns a newly allocated string holding UTC time in HH:MM:SS format
5379  * or NULL.
5380  */
5381 gchar *
5382 localtime_to_utctime (const char *local_time)
5383 {
5384   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
5385                              " %H : %M : %S ",
5386                              " %I : %M %p ", " %p %I : %M ",
5387                              " %H : %M ",
5388                              " %I %p ", " %p %I "};
5389   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
5390   const char *end = NULL;
5391   struct tm  *actual_local_time;
5392   struct tm  *actual_utc_time;
5393   struct tm   local_print_time;
5394   struct tm   utc_print_time;
5395   struct tm   diff_time;
5396   gchar      *utc_time = NULL;
5397   int         i, n;
5398
5399   if (local_time == NULL || local_time[0] == '\0')
5400     return NULL;
5401
5402   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
5403
5404   for (i = 0; i < n; i++)
5405     {
5406       local_print_time.tm_hour = 0;
5407       local_print_time.tm_min  = 0;
5408       local_print_time.tm_sec  = 0;
5409
5410       if (supports_am_pm ())
5411         end = strptime (local_time, formats_0[i], &local_print_time);
5412       else
5413         end = strptime (local_time, formats_1[i], &local_print_time);
5414
5415       if (end != NULL && end[0] == '\0')
5416         break;
5417     }
5418
5419   if (end != NULL && end[0] == '\0')
5420     {
5421       time_t rawtime;
5422       time (&rawtime);
5423
5424       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
5425       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
5426
5427       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
5428       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
5429       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
5430
5431       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
5432       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
5433       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
5434
5435       utc_time = g_strdup_printf ("%02d:%02d:%02d",
5436                                   utc_print_time.tm_hour,
5437                                   utc_print_time.tm_min,
5438                                   utc_print_time.tm_sec);
5439     }
5440
5441   return utc_time;
5442 }
5443
5444 static void
5445 cups_printer_get_settings_from_options (GtkPrinter          *printer,
5446                                         GtkPrinterOptionSet *options,
5447                                         GtkPrintSettings    *settings)
5448 {
5449   struct OptionData data;
5450   const char *print_at, *print_at_time;
5451
5452   data.printer = printer;
5453   data.options = options;
5454   data.settings = settings;
5455   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5456
5457   gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
5458   if (data.ppd_file != NULL)
5459     {
5460       GtkPrinterOption *cover_before, *cover_after;
5461
5462       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
5463       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
5464       if (cover_before && cover_after)
5465         {
5466           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
5467           gtk_print_settings_set (settings, "cups-job-sheets", value);
5468           g_free (value);
5469         }
5470
5471       print_at = gtk_print_settings_get (settings, "print-at");
5472       print_at_time = gtk_print_settings_get (settings, "print-at-time");
5473
5474       if (strcmp (print_at, "at") == 0)
5475         {
5476           gchar *utc_time = NULL;
5477
5478           utc_time = localtime_to_utctime (print_at_time);
5479
5480           if (utc_time != NULL)
5481             {
5482               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
5483               g_free (utc_time);
5484             }
5485           else
5486             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
5487         }
5488       else if (strcmp (print_at, "on-hold") == 0)
5489         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
5490     }
5491 }
5492
5493 static void
5494 cups_printer_prepare_for_print (GtkPrinter       *printer,
5495                                 GtkPrintJob      *print_job,
5496                                 GtkPrintSettings *settings,
5497                                 GtkPageSetup     *page_setup)
5498 {
5499   GtkPrintPages pages;
5500   GtkPageRange *ranges;
5501   gint n_ranges;
5502   GtkPageSet page_set;
5503   GtkPaperSize *paper_size;
5504   const char *ppd_paper_name;
5505   double scale;
5506   GtkPrintCapabilities  capabilities;
5507
5508   capabilities = cups_printer_get_capabilities (printer);
5509   pages = gtk_print_settings_get_print_pages (settings);
5510   gtk_print_job_set_pages (print_job, pages);
5511
5512   if (pages == GTK_PRINT_PAGES_RANGES)
5513     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
5514   else
5515     {
5516       ranges = NULL;
5517       n_ranges = 0;
5518     }
5519
5520   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
5521
5522   if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
5523     {
5524       if (gtk_print_settings_get_collate (settings))
5525         gtk_print_settings_set (settings, "cups-Collate", "True");
5526       gtk_print_job_set_collate (print_job, FALSE);
5527     }
5528   else
5529     {
5530       gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
5531     }
5532
5533   if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
5534     {
5535       if (gtk_print_settings_get_reverse (settings))
5536         gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
5537       gtk_print_job_set_reverse (print_job, FALSE);
5538     }
5539   else
5540     {
5541       gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
5542     }
5543
5544   if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
5545     {
5546       if (gtk_print_settings_get_n_copies (settings) > 1)
5547         gtk_print_settings_set_int (settings, "cups-copies",
5548                                     gtk_print_settings_get_n_copies (settings));
5549       gtk_print_job_set_num_copies (print_job, 1);
5550     }
5551   else
5552     {
5553       gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings));
5554     }
5555
5556   scale = gtk_print_settings_get_scale (settings);
5557   if (scale != 100.0)
5558     gtk_print_job_set_scale (print_job, scale / 100.0);
5559
5560   page_set = gtk_print_settings_get_page_set (settings);
5561   if (page_set == GTK_PAGE_SET_EVEN)
5562     gtk_print_settings_set (settings, "cups-page-set", "even");
5563   else if (page_set == GTK_PAGE_SET_ODD)
5564     gtk_print_settings_set (settings, "cups-page-set", "odd");
5565   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
5566
5567   paper_size = gtk_page_setup_get_paper_size (page_setup);
5568   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
5569   if (ppd_paper_name != NULL)
5570     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
5571   else
5572     {
5573       char width[G_ASCII_DTOSTR_BUF_SIZE];
5574       char height[G_ASCII_DTOSTR_BUF_SIZE];
5575       char *custom_name;
5576
5577       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
5578       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
5579       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
5580       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
5581       g_free (custom_name);
5582     }
5583
5584   if (gtk_print_settings_get_number_up (settings) > 1)
5585     {
5586       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
5587       GEnumClass        *enum_class;
5588       GEnumValue        *enum_value;
5589
5590       switch (gtk_page_setup_get_orientation (page_setup))
5591         {
5592           case GTK_PAGE_ORIENTATION_PORTRAIT:
5593             break;
5594           case GTK_PAGE_ORIENTATION_LANDSCAPE:
5595             if (layout < 4)
5596               layout = layout + 2 + 4 * (1 - layout / 2);
5597             else
5598               layout = layout - 3 - 2 * (layout % 2);
5599             break;
5600           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
5601             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
5602             break;
5603           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
5604             if (layout < 4)
5605               layout = layout + 5 - 2 * (layout % 2);
5606             else
5607               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
5608             break;
5609         }
5610
5611       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
5612       enum_value = g_enum_get_value (enum_class, layout);
5613       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
5614       g_type_class_unref (enum_class);
5615
5616       if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
5617         {
5618           gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
5619           gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
5620         }
5621     }
5622
5623   gtk_print_job_set_rotate (print_job, TRUE);
5624 }
5625
5626 static GtkPageSetup *
5627 create_page_setup (ppd_file_t *ppd_file,
5628                    ppd_size_t *size)
5629  {
5630    char *display_name;
5631    GtkPageSetup *page_setup;
5632    GtkPaperSize *paper_size;
5633    ppd_option_t *option;
5634    ppd_choice_t *choice;
5635
5636   display_name = NULL;
5637   option = ppdFindOption (ppd_file, "PageSize");
5638   if (option)
5639     {
5640       choice = ppdFindChoice (option, size->name);
5641       if (choice)
5642         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
5643     }
5644
5645   if (display_name == NULL)
5646     display_name = g_strdup (size->name);
5647
5648   page_setup = gtk_page_setup_new ();
5649   paper_size = gtk_paper_size_new_from_ppd (size->name,
5650                                             display_name,
5651                                             size->width,
5652                                             size->length);
5653   gtk_page_setup_set_paper_size (page_setup, paper_size);
5654   gtk_paper_size_free (paper_size);
5655
5656   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
5657   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
5658   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
5659   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
5660
5661   g_free (display_name);
5662
5663   return page_setup;
5664 }
5665
5666 static GList *
5667 cups_printer_list_papers (GtkPrinter *printer)
5668 {
5669   ppd_file_t *ppd_file;
5670   ppd_size_t *size;
5671   GtkPageSetup *page_setup;
5672   GList *l;
5673   int i;
5674
5675   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5676   if (ppd_file == NULL)
5677     return NULL;
5678
5679   l = NULL;
5680
5681   for (i = 0; i < ppd_file->num_sizes; i++)
5682     {
5683       size = &ppd_file->sizes[i];
5684
5685       page_setup = create_page_setup (ppd_file, size);
5686
5687       l = g_list_prepend (l, page_setup);
5688     }
5689
5690   return g_list_reverse (l);
5691 }
5692
5693 static GtkPageSetup *
5694 cups_printer_get_default_page_size (GtkPrinter *printer)
5695 {
5696   ppd_file_t *ppd_file;
5697   ppd_size_t *size;
5698   ppd_option_t *option;
5699
5700
5701   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5702   if (ppd_file == NULL)
5703     return NULL;
5704
5705   option = ppdFindOption (ppd_file, "PageSize");
5706   if (option == NULL)
5707     return NULL;
5708
5709   size = ppdPageSize (ppd_file, option->defchoice);
5710   if (size == NULL)
5711     return NULL;
5712
5713   return create_page_setup (ppd_file, size);
5714 }
5715
5716 static gboolean
5717 cups_printer_get_hard_margins (GtkPrinter *printer,
5718                                gdouble    *top,
5719                                gdouble    *bottom,
5720                                gdouble    *left,
5721                                gdouble    *right)
5722 {
5723   ppd_file_t *ppd_file;
5724
5725   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5726   if (ppd_file == NULL)
5727     return FALSE;
5728
5729   *left = ppd_file->custom_margins[0];
5730   *bottom = ppd_file->custom_margins[1];
5731   *right = ppd_file->custom_margins[2];
5732   *top = ppd_file->custom_margins[3];
5733
5734   return TRUE;
5735 }
5736
5737 static GtkPrintCapabilities
5738 cups_printer_get_capabilities (GtkPrinter *printer)
5739 {
5740   GtkPrintCapabilities  capabilities = 0;
5741   GtkPrinterCups       *cups_printer = GTK_PRINTER_CUPS (printer);
5742
5743   if (gtk_printer_cups_get_ppd (cups_printer))
5744     {
5745       capabilities = GTK_PRINT_CAPABILITY_REVERSE;
5746     }
5747
5748   if (cups_printer->supports_copies)
5749     {
5750       capabilities |= GTK_PRINT_CAPABILITY_COPIES;
5751     }
5752
5753   if (cups_printer->supports_collate)
5754     {
5755       capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
5756     }
5757
5758   if (cups_printer->supports_number_up)
5759     {
5760       capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
5761                       GTK_PRINT_CAPABILITY_NUMBER_UP;
5762     }
5763
5764   return capabilities;
5765 }