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