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