]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
4d095de79a752209156d8f81b26c23b6f0c0cc03
[~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   gchar                host[AVAHI_ADDRESS_STR_MAX];
2537   gchar               *suffix = NULL;
2538   gchar               *printer_uri;
2539
2540   avahi_address_snprint (host, sizeof (host), address);
2541
2542   item = avahi_string_list_find (txt, "rp");
2543   if (item)
2544     avahi_string_list_get_pair (item, NULL, &suffix, NULL);
2545
2546   if (suffix)
2547     {
2548       if (g_strcmp0 (type, "_ipp._tcp") == 0)
2549         printer_uri = g_strdup_printf ("ipp://%s:%u/%s", host, port, suffix);
2550       else
2551         printer_uri = g_strdup_printf ("ipps://%s:%u/%s", host, port, suffix);
2552
2553       cups_request_avahi_printer_info (printer_uri,
2554                                        host,
2555                                        port,
2556                                        name,
2557                                        type,
2558                                        domain,
2559                                        backend);
2560
2561       g_free (printer_uri);
2562       g_free (suffix);
2563     }
2564 }
2565
2566 static void
2567 avahi_browser_new_service_cb (GaServiceBrowser *browser,
2568                               int               interface,
2569                               GaProtocol        protocol,
2570                               const char       *name,
2571                               const char       *type,
2572                               const char       *domain,
2573                               glong             flags,
2574                               gpointer          user_data)
2575 {
2576   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
2577   GaServiceResolver   *resolver;
2578   GError              *error = NULL;
2579
2580   resolver = ga_service_resolver_new (AVAHI_IF_UNSPEC,
2581                                       AVAHI_PROTO_UNSPEC,
2582                                       name, type, domain,
2583                                       AVAHI_PROTO_UNSPEC,
2584                                       GA_LOOKUP_USE_MULTICAST);
2585
2586   g_signal_connect (resolver, "found",
2587                     G_CALLBACK (avahi_resolver_found_cb), user_data);
2588
2589   if (!ga_service_resolver_attach (resolver,
2590                                    backend->avahi_client,
2591                                    &error))
2592     {
2593       GTK_NOTE (PRINTING,
2594                 g_warning ("CUPS Backend: Error resolving Avahi service %s: %s",
2595                            name, error->message));
2596       g_clear_object (&resolver);
2597       g_clear_error (&error);
2598       return;
2599     }
2600
2601   backend->avahi_resolvers = g_list_append (backend->avahi_resolvers, resolver);
2602 }
2603
2604 static void
2605 avahi_browser_removed_service_cb (GaServiceBrowser *browser,
2606                                   int               interface,
2607                                   GaProtocol        protocol,
2608                                   const char       *name,
2609                                   const char       *type,
2610                                   const char       *domain,
2611                                   glong             flags,
2612                                   gpointer          user_data)
2613 {
2614   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
2615   GtkPrinterCups      *printer;
2616   GList               *list;
2617   GList               *iter;
2618
2619   list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
2620   for (iter = list; iter; iter = iter->next)
2621     {
2622       printer = GTK_PRINTER_CUPS (iter->data);
2623       if (g_strcmp0 (printer->avahi_name, name) == 0 &&
2624           g_strcmp0 (printer->avahi_type, type) == 0 &&
2625           g_strcmp0 (printer->avahi_domain, domain) == 0)
2626         {
2627           if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
2628                          backend->avahi_default_printer) == 0)
2629             g_clear_pointer (&backend->avahi_default_printer, g_free);
2630
2631           g_signal_emit_by_name (backend, "printer-removed", printer);
2632           gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
2633                                             GTK_PRINTER (printer));
2634           g_signal_emit_by_name (backend, "printer-list-changed");
2635           break;
2636         }
2637     }
2638
2639   g_list_free (list);
2640 }
2641
2642 static gboolean
2643 avahi_client_renew (gpointer user_data)
2644 {
2645   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
2646   gboolean             avahi_printers_removed = TRUE;
2647   GList               *list;
2648   GList               *iter;
2649
2650   list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
2651   for (iter = list; iter; iter = iter->next)
2652     if (GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
2653       avahi_printers_removed = FALSE;
2654
2655   g_list_free (list);
2656
2657   if (avahi_printers_removed)
2658     {
2659       avahi_data_free (backend);
2660       avahi_request_printer_list (backend);
2661       return FALSE;
2662     }
2663   else
2664     {
2665       return TRUE;
2666     }
2667 }
2668
2669 static void
2670 avahi_client_state_changed_cb (GaClient      *ga_client,
2671                                GaClientState  state,
2672                                gpointer       user_data)
2673 {
2674   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
2675   GtkPrinterCups      *printer;
2676   gboolean             list_has_changed = FALSE;
2677   GError              *error = NULL;
2678   GList               *list;
2679   GList               *iter;
2680
2681   switch (state)
2682     {
2683       case GA_CLIENT_STATE_FAILURE:
2684         if (avahi_client_errno (ga_client->avahi_client) == AVAHI_ERR_DISCONNECTED)
2685           {
2686             list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
2687
2688             for (iter = list; iter; iter = iter->next)
2689               {
2690                 printer = GTK_PRINTER_CUPS (iter->data);
2691                 if (printer->avahi_browsed)
2692                   {
2693                     g_signal_emit_by_name (backend, "printer-removed", printer);
2694                     gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
2695                                                       GTK_PRINTER (printer));
2696                     list_has_changed = TRUE;
2697                   }
2698               }
2699
2700             if (list_has_changed)
2701               g_signal_emit_by_name (backend, "printer-list-changed");
2702
2703             g_idle_add (avahi_client_renew, backend);
2704
2705             g_list_free (list);
2706           }
2707         break;
2708
2709       case GA_CLIENT_STATE_S_RUNNING:
2710         backend->avahi_ipp_browser = ga_service_browser_new ("_ipp._tcp");
2711
2712         g_signal_connect (backend->avahi_ipp_browser, "new-service",
2713                           G_CALLBACK (avahi_browser_new_service_cb),
2714                           user_data);
2715
2716         g_signal_connect (backend->avahi_ipp_browser, "removed-service",
2717                           G_CALLBACK (avahi_browser_removed_service_cb),
2718                           user_data);
2719
2720         if (!ga_service_browser_attach (backend->avahi_ipp_browser,
2721                                         backend->avahi_client,
2722                                         &error))
2723           {
2724             GTK_NOTE (PRINTING,
2725                       g_warning ("CUPS Backend: Error getting printer list from Avahi: %s",
2726                                  error->message));
2727             g_clear_object (&backend->avahi_ipp_browser);
2728             g_clear_error (&error);
2729           }
2730
2731         backend->avahi_ipps_browser = ga_service_browser_new ("_ipps._tcp");
2732
2733         g_signal_connect (backend->avahi_ipps_browser, "new-service",
2734                           G_CALLBACK (avahi_browser_new_service_cb),
2735                           user_data);
2736
2737         g_signal_connect (backend->avahi_ipps_browser, "removed-service",
2738                           G_CALLBACK (avahi_browser_removed_service_cb),
2739                           user_data);
2740
2741         if (!ga_service_browser_attach (backend->avahi_ipps_browser,
2742                                         backend->avahi_client,
2743                                         &error))
2744           {
2745             GTK_NOTE (PRINTING,
2746                       g_warning ("CUPS Backend: Error getting printer list from Avahi: %s",
2747                                  error->message));
2748             g_clear_object (&backend->avahi_ipps_browser);
2749             g_clear_error (&error);
2750           }
2751         break;
2752
2753       default:
2754         break;
2755     }
2756 }
2757
2758 static void
2759 avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
2760 {
2761   GError *error = NULL;
2762
2763   if (!cups_backend->avahi_client)
2764     {
2765       cups_backend->avahi_client = ga_client_new (GA_CLIENT_FLAG_NO_FAIL);
2766
2767       g_signal_connect (cups_backend->avahi_client, "state-changed",
2768                         G_CALLBACK (avahi_client_state_changed_cb),
2769                         cups_backend);
2770
2771       if (!ga_client_start (cups_backend->avahi_client, &error))
2772         {
2773           GTK_NOTE (PRINTING,
2774                     g_warning ("CUPS Backend: Error getting printer list from Avahi: %s",
2775                                error->message));
2776           g_clear_object (&cups_backend->avahi_client);
2777           g_clear_error (&error);
2778         }
2779     }
2780 }
2781 #endif
2782
2783 static void
2784 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
2785                               GtkCupsResult       *result,
2786                               gpointer             user_data)
2787 {
2788   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
2789   ipp_attribute_t *attr;
2790   ipp_t *response;
2791   gboolean list_has_changed;
2792   GList *removed_printer_checklist;
2793   gchar *remote_default_printer = NULL;
2794   GList *iter;
2795
2796   gdk_threads_enter ();
2797
2798   list_has_changed = FALSE;
2799
2800   GTK_NOTE (PRINTING,
2801             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2802
2803   cups_backend->list_printers_pending = FALSE;
2804
2805   if (gtk_cups_result_is_error (result))
2806     {
2807       GTK_NOTE (PRINTING,
2808                 g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
2809                            gtk_cups_result_get_error_string (result),
2810                            gtk_cups_result_get_error_type (result),
2811                            gtk_cups_result_get_error_code (result)));
2812
2813       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2814           gtk_cups_result_get_error_code (result) == 1)
2815         {
2816           /* Canceled by user, stop popping up more password dialogs */
2817           if (cups_backend->list_printers_poll > 0)
2818             g_source_remove (cups_backend->list_printers_poll);
2819           cups_backend->list_printers_poll = 0;
2820           cups_backend->list_printers_attempts = 0;
2821         }
2822
2823       goto done;
2824     }
2825
2826   /* Gather the names of the printers in the current queue
2827    * so we may check to see if they were removed
2828    */
2829   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
2830
2831   response = gtk_cups_result_get_response (result);
2832 #ifdef HAVE_CUPS_API_1_6
2833   for (attr = ippFirstAttribute (response); attr != NULL;
2834        attr = ippNextAttribute (response))
2835     {
2836       GtkPrinter *printer;
2837       gboolean status_changed = FALSE;
2838       GList *node;
2839       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
2840
2841       /* Skip leading attributes until we hit a printer...
2842        */
2843       while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
2844         attr = ippNextAttribute (response);
2845
2846       if (attr == NULL)
2847         break;
2848       while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
2849       {
2850         cups_printer_handle_attribute (cups_backend, attr, info);
2851         attr = ippNextAttribute (response);
2852       }
2853 #else
2854   for (attr = response->attrs; attr != NULL; attr = attr->next)
2855     {
2856       GtkPrinter *printer;
2857       gboolean status_changed = FALSE;
2858       GList *node;
2859       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
2860       info->default_number_up = 1;
2861
2862       /* Skip leading attributes until we hit a printer...
2863        */
2864       while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
2865         attr = attr->next;
2866
2867       if (attr == NULL)
2868         break;
2869       while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
2870       {
2871         cups_printer_handle_attribute (cups_backend, attr, info);
2872         attr = attr->next;
2873       }
2874 #endif
2875
2876       if (info->printer_name == NULL ||
2877           (info->printer_uri == NULL && info->member_uris == NULL))
2878       {
2879         if (attr == NULL)
2880           break;
2881         else
2882           continue;
2883       }
2884
2885       if (info->got_printer_type)
2886         {
2887           if (info->default_printer && !cups_backend->got_default_printer)
2888             {
2889               if (!info->remote_printer)
2890                 {
2891                   cups_backend->got_default_printer = TRUE;
2892                   cups_backend->default_printer = g_strdup (info->printer_name);
2893                 }
2894               else
2895                 {
2896                   if (remote_default_printer == NULL)
2897                     remote_default_printer = g_strdup (info->printer_name);
2898                 }
2899             }
2900         }
2901       else
2902         {
2903           if (!cups_backend->got_default_printer)
2904             cups_get_default_printer (cups_backend);
2905         }
2906
2907       /* remove name from checklist if it was found */
2908       node = g_list_find_custom (removed_printer_checklist,
2909                                  info->printer_name,
2910                                  (GCompareFunc) find_printer);
2911       removed_printer_checklist = g_list_delete_link (removed_printer_checklist,
2912                                                       node);
2913
2914       printer = gtk_print_backend_find_printer (backend, info->printer_name);
2915       if (!printer)
2916         {
2917           printer = cups_create_printer (cups_backend, info);
2918           list_has_changed = TRUE;
2919         }
2920
2921       else
2922         g_object_ref (printer);
2923
2924       GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
2925
2926       gtk_printer_set_is_paused (printer, info->is_paused);
2927       gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
2928
2929       if (!gtk_printer_is_active (printer))
2930         {
2931           gtk_printer_set_is_active (printer, TRUE);
2932           gtk_printer_set_is_new (printer, TRUE);
2933           list_has_changed = TRUE;
2934         }
2935
2936       if (gtk_printer_is_new (printer))
2937         {
2938           g_signal_emit_by_name (backend, "printer-added", printer);
2939
2940           gtk_printer_set_is_new (printer, FALSE);
2941         }
2942
2943 #if 0
2944       /* Getting printer info with separate requests overwhelms cups
2945        * when the printer list has more than a handful of printers.
2946        */
2947       cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
2948 #endif
2949
2950       GTK_PRINTER_CUPS (printer)->state = info->state;
2951       GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
2952       GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
2953       GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
2954       GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
2955       GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
2956       status_changed = gtk_printer_set_job_count (printer, info->job_count);
2957       status_changed |= gtk_printer_set_location (printer, info->location);
2958       status_changed |= gtk_printer_set_description (printer,
2959                                                      info->description);
2960
2961       set_info_state_message (info);
2962
2963       status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
2964       status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
2965
2966       set_printer_icon_name_from_info (printer, info);
2967
2968       if (status_changed)
2969         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
2970                                "printer-status-changed", printer);
2971
2972       /* The ref is held by GtkPrintBackend, in add_printer() */
2973       g_object_unref (printer);
2974       g_free (info->state_msg);
2975       g_slice_free (PrinterSetupInfo, info);
2976
2977       if (attr == NULL)
2978         break;
2979     }
2980
2981   /* look at the removed printers checklist and mark any printer
2982      as inactive if it is in the list, emitting a printer_removed signal */
2983   if (removed_printer_checklist != NULL)
2984     {
2985       for (iter = removed_printer_checklist; iter; iter = iter->next)
2986         {
2987 #ifdef HAVE_AVAHI_BROWSING
2988           if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
2989 #endif
2990             {
2991               mark_printer_inactive (GTK_PRINTER (iter->data), backend);
2992               list_has_changed = TRUE;
2993             }
2994         }
2995
2996       g_list_free (removed_printer_checklist);
2997     }
2998
2999 done:
3000   if (list_has_changed)
3001     g_signal_emit_by_name (backend, "printer-list-changed");
3002
3003   gtk_print_backend_set_list_done (backend);
3004
3005   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
3006     {
3007       set_default_printer (cups_backend, remote_default_printer);
3008       g_free (remote_default_printer);
3009     }
3010
3011 #ifdef HAVE_AVAHI_BROWSING
3012   if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
3013     {
3014       set_default_printer (cups_backend, cups_backend->avahi_default_printer);
3015     }
3016 #endif
3017
3018   gdk_threads_leave ();
3019 }
3020
3021 static void
3022 update_backend_status (GtkPrintBackendCups    *cups_backend,
3023                        GtkCupsConnectionState  state)
3024 {
3025   switch (state)
3026     {
3027     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
3028       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
3029       break;
3030     case GTK_CUPS_CONNECTION_AVAILABLE:
3031       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
3032       break;
3033     default: ;
3034     }
3035 }
3036
3037 static gboolean
3038 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
3039 {
3040   GtkCupsConnectionState state;
3041   GtkCupsRequest *request;
3042
3043   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
3044     return TRUE;
3045
3046   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
3047   update_backend_status (cups_backend, state);
3048
3049   if (cups_backend->list_printers_attempts == 60)
3050     {
3051       cups_backend->list_printers_attempts = -1;
3052       if (cups_backend->list_printers_poll > 0)
3053         g_source_remove (cups_backend->list_printers_poll);
3054       cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
3055                                            (GSourceFunc) cups_request_printer_list,
3056                                            cups_backend);
3057     }
3058   else if (cups_backend->list_printers_attempts != -1)
3059     cups_backend->list_printers_attempts++;
3060
3061   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3062     return TRUE;
3063   else
3064     if (cups_backend->list_printers_attempts > 0)
3065       cups_backend->list_printers_attempts = 60;
3066
3067   cups_backend->list_printers_pending = TRUE;
3068
3069   request = gtk_cups_request_new_with_username (NULL,
3070                                                 GTK_CUPS_POST,
3071                                                 CUPS_GET_PRINTERS,
3072                                                 NULL,
3073                                                 NULL,
3074                                                 NULL,
3075                                                 cups_backend->username);
3076
3077   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3078                                     "requested-attributes", G_N_ELEMENTS (printer_attrs),
3079                                     NULL, printer_attrs);
3080
3081   cups_request_execute (cups_backend,
3082                         request,
3083                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
3084                         request,
3085                         NULL);
3086
3087   return TRUE;
3088 }
3089
3090 static void
3091 cups_get_printer_list (GtkPrintBackend *backend)
3092 {
3093   GtkPrintBackendCups *cups_backend;
3094
3095   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
3096
3097   if (cups_backend->cups_connection_test == NULL)
3098     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
3099
3100   if (cups_backend->list_printers_poll == 0)
3101     {
3102       if (cups_request_printer_list (cups_backend))
3103         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
3104                                              (GSourceFunc) cups_request_printer_list,
3105                                              backend);
3106
3107 #ifdef HAVE_AVAHI_BROWSING
3108       avahi_request_printer_list (cups_backend);
3109 #endif
3110     }
3111 }
3112
3113 typedef struct {
3114   GtkPrinterCups *printer;
3115   GIOChannel *ppd_io;
3116   http_t *http;
3117 } GetPPDData;
3118
3119 static void
3120 get_ppd_data_free (GetPPDData *data)
3121 {
3122   GTK_NOTE (PRINTING,
3123             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3124   httpClose (data->http);
3125   g_io_channel_unref (data->ppd_io);
3126   g_object_unref (data->printer);
3127   g_free (data);
3128 }
3129
3130 static void
3131 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
3132                      GtkCupsResult       *result,
3133                      GetPPDData          *data)
3134 {
3135   GtkPrinter *printer;
3136
3137   gdk_threads_enter ();
3138
3139   GTK_NOTE (PRINTING,
3140             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3141
3142   printer = GTK_PRINTER (data->printer);
3143   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
3144   print_backend->reading_ppds--;
3145
3146   if (gtk_cups_result_is_error (result))
3147     {
3148       gboolean success = FALSE;
3149
3150       /* If we get a 404 then it is just a raw printer without a ppd
3151          and not an error. Standalone Avahi printers also don't have
3152          PPD files. */
3153       if (((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
3154            (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
3155 #ifdef HAVE_AVAHI_BROWSING
3156            || GTK_PRINTER_CUPS (printer)->avahi_browsed
3157 #endif
3158            )
3159         {
3160           gtk_printer_set_has_details (printer, TRUE);
3161           success = TRUE;
3162         }
3163
3164       g_signal_emit_by_name (printer, "details-acquired", success);
3165       goto done;
3166     }
3167
3168   /* let ppdOpenFd take over the ownership of the open file */
3169   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
3170   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
3171   ppdLocalize (data->printer->ppd_file);
3172   ppdMarkDefaults (data->printer->ppd_file);
3173
3174   gtk_printer_set_has_details (printer, TRUE);
3175   g_signal_emit_by_name (printer, "details-acquired", TRUE);
3176
3177 done:
3178   gdk_threads_leave ();
3179 }
3180
3181 static gboolean
3182 cups_request_ppd (GtkPrinter *printer)
3183 {
3184   GError *error;
3185   GtkPrintBackend *print_backend;
3186   GtkPrinterCups *cups_printer;
3187   GtkCupsRequest *request;
3188   char *ppd_filename;
3189   gchar *resource;
3190   http_t *http;
3191   GetPPDData *data;
3192   int fd;
3193
3194   cups_printer = GTK_PRINTER_CUPS (printer);
3195
3196   error = NULL;
3197
3198   GTK_NOTE (PRINTING,
3199             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3200
3201   if (cups_printer->remote)
3202     {
3203       GtkCupsConnectionState state;
3204
3205       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
3206
3207       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
3208         {
3209           if (cups_printer->get_remote_ppd_attempts == 60)
3210             {
3211               cups_printer->get_remote_ppd_attempts = -1;
3212               if (cups_printer->get_remote_ppd_poll > 0)
3213                 g_source_remove (cups_printer->get_remote_ppd_poll);
3214               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
3215                                                     (GSourceFunc) cups_request_ppd,
3216                                                     printer);
3217             }
3218           else if (cups_printer->get_remote_ppd_attempts != -1)
3219             cups_printer->get_remote_ppd_attempts++;
3220
3221           return TRUE;
3222         }
3223
3224       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
3225       cups_printer->remote_cups_connection_test = NULL;
3226       cups_printer->get_remote_ppd_poll = 0;
3227       cups_printer->get_remote_ppd_attempts = 0;
3228
3229       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3230         {
3231           g_signal_emit_by_name (printer, "details-acquired", FALSE);
3232           return FALSE;
3233         }
3234     }
3235
3236   http = httpConnectEncrypt (cups_printer->hostname,
3237                              cups_printer->port,
3238                              cupsEncryption ());
3239
3240   data = g_new0 (GetPPDData, 1);
3241
3242   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
3243                         &ppd_filename,
3244                         &error);
3245
3246 #ifdef G_ENABLE_DEBUG
3247   /* If we are debugging printing don't delete the tmp files */
3248   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
3249     unlink (ppd_filename);
3250 #else
3251   unlink (ppd_filename);
3252 #endif /* G_ENABLE_DEBUG */
3253
3254   if (error != NULL)
3255     {
3256       GTK_NOTE (PRINTING,
3257                 g_warning ("CUPS Backend: Failed to create temp file, %s\n",
3258                            error->message));
3259       g_error_free (error);
3260       httpClose (http);
3261       g_free (ppd_filename);
3262       g_free (data);
3263
3264       g_signal_emit_by_name (printer, "details-acquired", FALSE);
3265       return FALSE;
3266     }
3267
3268   data->http = http;
3269   fchmod (fd, S_IRUSR | S_IWUSR);
3270   data->ppd_io = g_io_channel_unix_new (fd);
3271   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
3272   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
3273
3274   data->printer = g_object_ref (printer);
3275
3276   resource = g_strdup_printf ("/printers/%s.ppd",
3277                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
3278
3279   print_backend = gtk_printer_get_backend (printer);
3280
3281   request = gtk_cups_request_new_with_username (data->http,
3282                                                 GTK_CUPS_GET,
3283                                                 0,
3284                                                 data->ppd_io,
3285                                                 cups_printer->hostname,
3286                                                 resource,
3287                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
3288
3289   gtk_cups_request_set_ipp_version (request,
3290                                     cups_printer->ipp_version_major,
3291                                     cups_printer->ipp_version_minor);
3292
3293   GTK_NOTE (PRINTING,
3294             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
3295
3296
3297   cups_printer->reading_ppd = TRUE;
3298   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
3299
3300   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
3301                         request,
3302                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
3303                         data,
3304                         (GDestroyNotify)get_ppd_data_free);
3305
3306   g_free (resource);
3307   g_free (ppd_filename);
3308
3309   return FALSE;
3310 }
3311
3312 /* Ordering matters for default preference */
3313 static const char *lpoptions_locations[] = {
3314   "/etc/cups/lpoptions",
3315   ".lpoptions",
3316   ".cups/lpoptions"
3317 };
3318
3319 static void
3320 cups_parse_user_default_printer (const char  *filename,
3321                                  char       **printer_name)
3322 {
3323   FILE *fp;
3324   char line[1024], *lineptr, *defname = NULL;
3325
3326   if ((fp = g_fopen (filename, "r")) == NULL)
3327     return;
3328
3329   while (fgets (line, sizeof (line), fp) != NULL)
3330     {
3331       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
3332         continue;
3333
3334       lineptr = line + 8;
3335       while (isspace (*lineptr))
3336         lineptr++;
3337
3338       if (!*lineptr)
3339         continue;
3340
3341       defname = lineptr;
3342       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
3343         lineptr++;
3344
3345       *lineptr = '\0';
3346
3347       if (*printer_name != NULL)
3348         g_free (*printer_name);
3349
3350       *printer_name = g_strdup (defname);
3351     }
3352
3353   fclose (fp);
3354 }
3355
3356 static void
3357 cups_get_user_default_printer (char **printer_name)
3358 {
3359   int i;
3360
3361   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
3362     {
3363       if (g_path_is_absolute (lpoptions_locations[i]))
3364         {
3365           cups_parse_user_default_printer (lpoptions_locations[i],
3366                                            printer_name);
3367         }
3368       else
3369         {
3370           char *filename;
3371
3372           filename = g_build_filename (g_get_home_dir (),
3373                                        lpoptions_locations[i], NULL);
3374           cups_parse_user_default_printer (filename, printer_name);
3375           g_free (filename);
3376         }
3377     }
3378 }
3379
3380 static int
3381 cups_parse_user_options (const char     *filename,
3382                          const char     *printer_name,
3383                          int             num_options,
3384                          cups_option_t **options)
3385 {
3386   FILE *fp;
3387   gchar line[1024], *lineptr, *name;
3388
3389   if ((fp = g_fopen (filename, "r")) == NULL)
3390     return num_options;
3391
3392   while (fgets (line, sizeof (line), fp) != NULL)
3393     {
3394       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
3395         lineptr = line + 4;
3396       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
3397         lineptr = line + 7;
3398       else
3399         continue;
3400
3401       /* Skip leading whitespace */
3402       while (isspace (*lineptr))
3403         lineptr++;
3404
3405       if (!*lineptr)
3406         continue;
3407
3408       /* NUL-terminate the name, stripping the instance name */
3409       name = lineptr;
3410       while (!isspace (*lineptr) && *lineptr)
3411         {
3412           if (*lineptr == '/')
3413             *lineptr = '\0';
3414           lineptr++;
3415         }
3416
3417       if (!*lineptr)
3418         continue;
3419
3420       *lineptr++ = '\0';
3421
3422       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
3423           continue;
3424
3425       /* We found our printer, parse the options */
3426       num_options = cupsParseOptions (lineptr, num_options, options);
3427     }
3428
3429   fclose (fp);
3430
3431   return num_options;
3432 }
3433
3434 static int
3435 cups_get_user_options (const char     *printer_name,
3436                        int             num_options,
3437                        cups_option_t **options)
3438 {
3439   int i;
3440
3441   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
3442     {
3443       if (g_path_is_absolute (lpoptions_locations[i]))
3444         {
3445            num_options = cups_parse_user_options (lpoptions_locations[i],
3446                                                   printer_name,
3447                                                   num_options,
3448                                                   options);
3449         }
3450       else
3451         {
3452           char *filename;
3453
3454           filename = g_build_filename (g_get_home_dir (),
3455                                        lpoptions_locations[i], NULL);
3456           num_options = cups_parse_user_options (filename, printer_name,
3457                                                  num_options, options);
3458           g_free (filename);
3459         }
3460     }
3461
3462   return num_options;
3463 }
3464
3465 /* This function requests default printer from a CUPS server in regular intervals.
3466  * In the case of unreachable CUPS server the request is repeated later.
3467  * The default printer is not requested in the case of previous success.
3468  */
3469 static void
3470 cups_get_default_printer (GtkPrintBackendCups *backend)
3471 {
3472   GtkPrintBackendCups *cups_backend;
3473
3474   cups_backend = backend;
3475
3476   if (cups_backend->cups_connection_test == NULL)
3477     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
3478
3479   if (cups_backend->default_printer_poll == 0)
3480     {
3481       if (cups_request_default_printer (cups_backend))
3482         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
3483                                                (GSourceFunc) cups_request_default_printer,
3484                                                backend);
3485     }
3486 }
3487
3488 /* This function gets default printer from local settings.*/
3489 static void
3490 cups_get_local_default_printer (GtkPrintBackendCups *backend)
3491 {
3492   const char *str;
3493   char *name = NULL;
3494
3495   if ((str = g_getenv ("LPDEST")) != NULL)
3496     {
3497       backend->default_printer = g_strdup (str);
3498       backend->got_default_printer = TRUE;
3499       return;
3500     }
3501   else if ((str = g_getenv ("PRINTER")) != NULL &&
3502            strcmp (str, "lp") != 0)
3503     {
3504       backend->default_printer = g_strdup (str);
3505       backend->got_default_printer = TRUE;
3506       return;
3507     }
3508
3509   /* Figure out user setting for default printer */
3510   cups_get_user_default_printer (&name);
3511   if (name != NULL)
3512     {
3513       backend->default_printer = name;
3514       backend->got_default_printer = TRUE;
3515       return;
3516     }
3517 }
3518
3519 static void
3520 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
3521                                  GtkCupsResult       *result,
3522                                  gpointer             user_data)
3523 {
3524   ipp_t *response;
3525   ipp_attribute_t *attr;
3526   GtkPrinter *printer;
3527
3528   gdk_threads_enter ();
3529
3530   if (gtk_cups_result_is_error (result))
3531     {
3532       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
3533           gtk_cups_result_get_error_code (result) == 1)
3534         {
3535           /* Canceled by user, stop popping up more password dialogs */
3536           if (print_backend->list_printers_poll > 0)
3537             g_source_remove (print_backend->list_printers_poll);
3538           print_backend->list_printers_poll = 0;
3539         }
3540
3541       return;
3542     }
3543
3544   response = gtk_cups_result_get_response (result);
3545
3546   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
3547       print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
3548
3549   print_backend->got_default_printer = TRUE;
3550
3551   if (print_backend->default_printer != NULL)
3552     {
3553       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
3554       if (printer != NULL)
3555         {
3556           gtk_printer_set_is_default (printer, TRUE);
3557           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
3558         }
3559     }
3560
3561   /* Make sure to kick off get_printers if we are polling it,
3562    * as we could have blocked this reading the default printer
3563    */
3564   if (print_backend->list_printers_poll != 0)
3565     cups_request_printer_list (print_backend);
3566
3567   gdk_threads_leave ();
3568 }
3569
3570 static gboolean
3571 cups_request_default_printer (GtkPrintBackendCups *print_backend)
3572 {
3573   GtkCupsConnectionState state;
3574   GtkCupsRequest *request;
3575
3576   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
3577   update_backend_status (print_backend, state);
3578
3579   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3580     return TRUE;
3581
3582   request = gtk_cups_request_new_with_username (NULL,
3583                                                 GTK_CUPS_POST,
3584                                                 CUPS_GET_DEFAULT,
3585                                                 NULL,
3586                                                 NULL,
3587                                                 NULL,
3588                                                 print_backend->username);
3589
3590   cups_request_execute (print_backend,
3591                         request,
3592                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
3593                         g_object_ref (print_backend),
3594                         g_object_unref);
3595
3596   return FALSE;
3597 }
3598
3599 static void
3600 cups_printer_request_details (GtkPrinter *printer)
3601 {
3602   GtkPrinterCups *cups_printer;
3603
3604   cups_printer = GTK_PRINTER_CUPS (printer);
3605   if (!cups_printer->reading_ppd &&
3606       gtk_printer_cups_get_ppd (cups_printer) == NULL)
3607     {
3608       if (cups_printer->remote)
3609         {
3610           if (cups_printer->get_remote_ppd_poll == 0)
3611             {
3612               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
3613
3614               if (cups_request_ppd (printer))
3615                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
3616                                                     (GSourceFunc) cups_request_ppd,
3617                                                     printer);
3618             }
3619         }
3620       else
3621         cups_request_ppd (printer);
3622     }
3623 }
3624
3625 static char *
3626 ppd_text_to_utf8 (ppd_file_t *ppd_file,
3627                   const char *text)
3628 {
3629   const char *encoding = NULL;
3630   char *res;
3631
3632   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
3633     {
3634       return g_strdup (text);
3635     }
3636   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
3637     {
3638       encoding = "ISO-8859-1";
3639     }
3640   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
3641     {
3642       encoding = "ISO-8859-2";
3643     }
3644   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
3645     {
3646       encoding = "ISO-8859-5";
3647     }
3648   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
3649     {
3650       encoding = "SHIFT-JIS";
3651     }
3652   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
3653     {
3654       encoding = "MACINTOSH";
3655     }
3656   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
3657     {
3658       encoding = "WINDOWS-1252";
3659     }
3660   else
3661     {
3662       /* Fallback, try iso-8859-1... */
3663       encoding = "ISO-8859-1";
3664     }
3665
3666   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
3667
3668   if (res == NULL)
3669     {
3670       GTK_NOTE (PRINTING,
3671                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
3672       res = g_strdup ("???");
3673     }
3674
3675   return res;
3676 }
3677
3678 /* TODO: Add more translations for common settings here */
3679
3680 static const struct {
3681   const char *keyword;
3682   const char *translation;
3683 } cups_option_translations[] = {
3684   { "Duplex", N_("Two Sided") },
3685   { "MediaType", N_("Paper Type") },
3686   { "InputSlot", N_("Paper Source") },
3687   { "OutputBin", N_("Output Tray") },
3688   { "Resolution", N_("Resolution") },
3689   { "PreFilter", N_("GhostScript pre-filtering") },
3690 };
3691
3692
3693 static const struct {
3694   const char *keyword;
3695   const char *choice;
3696   const char *translation;
3697 } cups_choice_translations[] = {
3698   { "Duplex", "None", N_("One Sided") },
3699   /* Translators: this is an option of "Two Sided" */
3700   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
3701   /* Translators: this is an option of "Two Sided" */
3702   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
3703   /* Translators: this is an option of "Paper Source" */
3704   { "InputSlot", "Auto", N_("Auto Select") },
3705   /* Translators: this is an option of "Paper Source" */
3706   { "InputSlot", "AutoSelect", N_("Auto Select") },
3707   /* Translators: this is an option of "Paper Source" */
3708   { "InputSlot", "Default", N_("Printer Default") },
3709   /* Translators: this is an option of "Paper Source" */
3710   { "InputSlot", "None", N_("Printer Default") },
3711   /* Translators: this is an option of "Paper Source" */
3712   { "InputSlot", "PrinterDefault", N_("Printer Default") },
3713   /* Translators: this is an option of "Paper Source" */
3714   { "InputSlot", "Unspecified", N_("Auto Select") },
3715   /* Translators: this is an option of "Resolution" */
3716   { "Resolution", "default", N_("Printer Default") },
3717   /* Translators: this is an option of "GhostScript" */
3718   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
3719   /* Translators: this is an option of "GhostScript" */
3720   { "PreFilter", "Level1", N_("Convert to PS level 1") },
3721   /* Translators: this is an option of "GhostScript" */
3722   { "PreFilter", "Level2", N_("Convert to PS level 2") },
3723   /* Translators: this is an option of "GhostScript" */
3724   { "PreFilter", "No", N_("No pre-filtering") },
3725 };
3726
3727 static const struct {
3728   const char *name;
3729   const char *translation;
3730 } cups_group_translations[] = {
3731 /* Translators: "Miscellaneous" is the label for a button, that opens
3732    up an extra panel of settings in a print dialog. */
3733   { "Miscellaneous", N_("Miscellaneous") },
3734 };
3735
3736 static const struct {
3737   const char *ppd_keyword;
3738   const char *name;
3739 } ppd_option_names[] = {
3740   {"Duplex", "gtk-duplex" },
3741   {"MediaType", "gtk-paper-type"},
3742   {"InputSlot", "gtk-paper-source"},
3743   {"OutputBin", "gtk-output-tray"},
3744 };
3745
3746 static const struct {
3747   const char *lpoption;
3748   const char *name;
3749 } lpoption_names[] = {
3750   {"number-up", "gtk-n-up" },
3751   {"number-up-layout", "gtk-n-up-layout"},
3752   {"job-billing", "gtk-billing-info"},
3753   {"job-priority", "gtk-job-prio"},
3754 };
3755
3756 /* keep sorted when changing */
3757 static const char *color_option_whitelist[] = {
3758   "BRColorEnhancement",
3759   "BRColorMatching",
3760   "BRColorMatching",
3761   "BRColorMode",
3762   "BRGammaValue",
3763   "BRImprovedGray",
3764   "BlackSubstitution",
3765   "ColorModel",
3766   "HPCMYKInks",
3767   "HPCSGraphics",
3768   "HPCSImages",
3769   "HPCSText",
3770   "HPColorSmart",
3771   "RPSBlackMode",
3772   "RPSBlackOverPrint",
3773   "Rcmyksimulation",
3774 };
3775
3776 /* keep sorted when changing */
3777 static const char *color_group_whitelist[] = {
3778   "ColorPage",
3779   "FPColorWise1",
3780   "FPColorWise2",
3781   "FPColorWise3",
3782   "FPColorWise4",
3783   "FPColorWise5",
3784   "HPColorOptionsPanel",
3785 };
3786
3787 /* keep sorted when changing */
3788 static const char *image_quality_option_whitelist[] = {
3789   "BRDocument",
3790   "BRHalfTonePattern",
3791   "BRNormalPrt",
3792   "BRPrintQuality",
3793   "BitsPerPixel",
3794   "Darkness",
3795   "Dithering",
3796   "EconoMode",
3797   "Economode",
3798   "HPEconoMode",
3799   "HPEdgeControl",
3800   "HPGraphicsHalftone",
3801   "HPHalftone",
3802   "HPLJDensity",
3803   "HPPhotoHalftone",
3804   "OutputMode",
3805   "REt",
3806   "RPSBitsPerPixel",
3807   "RPSDitherType",
3808   "Resolution",
3809   "ScreenLock",
3810   "Smoothing",
3811   "TonerSaveMode",
3812   "UCRGCRForImage",
3813 };
3814
3815 /* keep sorted when changing */
3816 static const char *image_quality_group_whitelist[] = {
3817   "FPImageQuality1",
3818   "FPImageQuality2",
3819   "FPImageQuality3",
3820   "ImageQualityPage",
3821 };
3822
3823 /* keep sorted when changing */
3824 static const char * finishing_option_whitelist[] = {
3825   "BindColor",
3826   "BindEdge",
3827   "BindType",
3828   "BindWhen",
3829   "Booklet",
3830   "FoldType",
3831   "FoldWhen",
3832   "HPStaplerOptions",
3833   "Jog",
3834   "Slipsheet",
3835   "Sorter",
3836   "StapleLocation",
3837   "StapleOrientation",
3838   "StapleWhen",
3839   "StapleX",
3840   "StapleY",
3841 };
3842
3843 /* keep sorted when changing */
3844 static const char *finishing_group_whitelist[] = {
3845   "FPFinishing1",
3846   "FPFinishing2",
3847   "FPFinishing3",
3848   "FPFinishing4",
3849   "FinishingPage",
3850   "HPFinishingPanel",
3851 };
3852
3853 /* keep sorted when changing */
3854 static const char *cups_option_blacklist[] = {
3855   "Collate",
3856   "Copies",
3857   "OutputOrder",
3858   "PageRegion",
3859   "PageSize",
3860 };
3861
3862 static char *
3863 get_option_text (ppd_file_t   *ppd_file,
3864                  ppd_option_t *option)
3865 {
3866   int i;
3867   char *utf8;
3868
3869   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
3870     {
3871       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
3872         return g_strdup (_(cups_option_translations[i].translation));
3873     }
3874
3875   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
3876
3877   /* Some ppd files have spaces in the text before the colon */
3878   g_strchomp (utf8);
3879
3880   return utf8;
3881 }
3882
3883 static char *
3884 get_choice_text (ppd_file_t   *ppd_file,
3885                  ppd_choice_t *choice)
3886 {
3887   int i;
3888   ppd_option_t *option = choice->option;
3889   const char *keyword = option->keyword;
3890
3891   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
3892     {
3893       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
3894           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
3895         return g_strdup (_(cups_choice_translations[i].translation));
3896     }
3897   return ppd_text_to_utf8 (ppd_file, choice->text);
3898 }
3899
3900 static gboolean
3901 group_has_option (ppd_group_t  *group,
3902                   ppd_option_t *option)
3903 {
3904   int i;
3905
3906   if (group == NULL)
3907     return FALSE;
3908
3909   if (group->num_options > 0 &&
3910       option >= group->options && option < group->options + group->num_options)
3911     return TRUE;
3912
3913   for (i = 0; i < group->num_subgroups; i++)
3914     {
3915       if (group_has_option (&group->subgroups[i],option))
3916         return TRUE;
3917     }
3918   return FALSE;
3919 }
3920
3921 static void
3922 set_option_off (GtkPrinterOption *option)
3923 {
3924   /* Any of these will do, _set only applies the value
3925    * if its allowed of the option */
3926   gtk_printer_option_set (option, "False");
3927   gtk_printer_option_set (option, "Off");
3928   gtk_printer_option_set (option, "None");
3929 }
3930
3931 static gboolean
3932 value_is_off (const char *value)
3933 {
3934   return  (strcasecmp (value, "None") == 0 ||
3935            strcasecmp (value, "Off") == 0 ||
3936            strcasecmp (value, "False") == 0);
3937 }
3938
3939 static char *
3940 ppd_group_name (ppd_group_t *group)
3941 {
3942   return group->name;
3943 }
3944
3945 static int
3946 available_choices (ppd_file_t     *ppd,
3947                    ppd_option_t   *option,
3948                    ppd_choice_t ***available,
3949                    gboolean        keep_if_only_one_option)
3950 {
3951   ppd_option_t *other_option;
3952   int i, j;
3953   gchar *conflicts;
3954   ppd_const_t *constraint;
3955   const char *choice, *other_choice;
3956   ppd_option_t *option1, *option2;
3957   ppd_group_t *installed_options;
3958   int num_conflicts;
3959   gboolean all_default;
3960   int add_auto;
3961
3962   if (available)
3963     *available = NULL;
3964
3965   conflicts = g_new0 (char, option->num_choices);
3966
3967   installed_options = NULL;
3968   for (i = 0; i < ppd->num_groups; i++)
3969     {
3970       char *name;
3971
3972       name = ppd_group_name (&ppd->groups[i]);
3973       if (strcmp (name, "InstallableOptions") == 0)
3974         {
3975           installed_options = &ppd->groups[i];
3976           break;
3977         }
3978     }
3979
3980   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
3981     {
3982       option1 = ppdFindOption (ppd, constraint->option1);
3983       if (option1 == NULL)
3984         continue;
3985
3986       option2 = ppdFindOption (ppd, constraint->option2);
3987       if (option2 == NULL)
3988         continue;
3989
3990       if (option == option1)
3991         {
3992           choice = constraint->choice1;
3993           other_option = option2;
3994           other_choice = constraint->choice2;
3995         }
3996       else if (option == option2)
3997         {
3998           choice = constraint->choice2;
3999           other_option = option1;
4000           other_choice = constraint->choice1;
4001         }
4002       else
4003         continue;
4004
4005       /* We only care of conflicts with installed_options and
4006          PageSize */
4007       if (!group_has_option (installed_options, other_option) &&
4008           (strcmp (other_option->keyword, "PageSize") != 0))
4009         continue;
4010
4011       if (*other_choice == 0)
4012         {
4013           /* Conflict only if the installed option is not off */
4014           if (value_is_off (other_option->defchoice))
4015             continue;
4016         }
4017       /* Conflict if the installed option has the specified default */
4018       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
4019         continue;
4020
4021       if (*choice == 0)
4022         {
4023           /* Conflict with all non-off choices */
4024           for (j = 0; j < option->num_choices; j++)
4025             {
4026               if (!value_is_off (option->choices[j].choice))
4027                 conflicts[j] = 1;
4028             }
4029         }
4030       else
4031         {
4032           for (j = 0; j < option->num_choices; j++)
4033             {
4034               if (strcasecmp (option->choices[j].choice, choice) == 0)
4035                 conflicts[j] = 1;
4036             }
4037         }
4038     }
4039
4040   num_conflicts = 0;
4041   all_default = TRUE;
4042   for (j = 0; j < option->num_choices; j++)
4043     {
4044       if (conflicts[j])
4045         num_conflicts++;
4046       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
4047         all_default = FALSE;
4048     }
4049
4050   if ((all_default && !keep_if_only_one_option) ||
4051       (num_conflicts == option->num_choices))
4052     {
4053       g_free (conflicts);
4054
4055       return 0;
4056     }
4057
4058   /* Some ppds don't have a "use printer default" option for
4059    * InputSlot. This means you always have to select a particular slot,
4060    * and you can't auto-pick source based on the paper size. To support
4061    * this we always add an auto option if there isn't one already. If
4062    * the user chooses the generated option we don't send any InputSlot
4063    * value when printing. The way we detect existing auto-cases is based
4064    * on feedback from Michael Sweet of cups fame.
4065    */
4066   add_auto = 0;
4067   if (strcmp (option->keyword, "InputSlot") == 0)
4068     {
4069       gboolean found_auto = FALSE;
4070       for (j = 0; j < option->num_choices; j++)
4071         {
4072           if (!conflicts[j])
4073             {
4074               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
4075                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
4076                   strcmp (option->choices[j].choice, "Default") == 0 ||
4077                   strcmp (option->choices[j].choice, "None") == 0 ||
4078                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
4079                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
4080                   option->choices[j].code == NULL ||
4081                   option->choices[j].code[0] == 0)
4082                 {
4083                   found_auto = TRUE;
4084                   break;
4085                 }
4086             }
4087         }
4088
4089       if (!found_auto)
4090         add_auto = 1;
4091     }
4092
4093   if (available)
4094     {
4095       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
4096
4097       i = 0;
4098       for (j = 0; j < option->num_choices; j++)
4099         {
4100           if (!conflicts[j])
4101             (*available)[i++] = &option->choices[j];
4102         }
4103
4104       if (add_auto)
4105         (*available)[i++] = NULL;
4106     }
4107
4108   g_free (conflicts);
4109
4110   return option->num_choices - num_conflicts + add_auto;
4111 }
4112
4113 static GtkPrinterOption *
4114 create_pickone_option (ppd_file_t   *ppd_file,
4115                        ppd_option_t *ppd_option,
4116                        const gchar  *gtk_name)
4117 {
4118   GtkPrinterOption *option;
4119   ppd_choice_t **available;
4120   char *label;
4121   int n_choices;
4122   int i;
4123   ppd_coption_t *coption;
4124
4125   g_assert (ppd_option->ui == PPD_UI_PICKONE);
4126
4127   option = NULL;
4128
4129   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
4130   if (n_choices > 0)
4131     {
4132
4133       /* right now only support one parameter per custom option
4134        * if more than one print warning and only offer the default choices
4135        */
4136
4137       label = get_option_text (ppd_file, ppd_option);
4138
4139       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
4140
4141       if (coption)
4142         {
4143           ppd_cparam_t *cparam;
4144
4145           cparam = ppdFirstCustomParam (coption);
4146
4147           if (ppdNextCustomParam (coption) == NULL)
4148             {
4149               switch (cparam->type)
4150                 {
4151                 case PPD_CUSTOM_INT:
4152                   option = gtk_printer_option_new (gtk_name, label,
4153                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
4154                   break;
4155                 case PPD_CUSTOM_PASSCODE:
4156                   option = gtk_printer_option_new (gtk_name, label,
4157                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
4158                   break;
4159                 case PPD_CUSTOM_PASSWORD:
4160                     option = gtk_printer_option_new (gtk_name, label,
4161                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
4162                   break;
4163                case PPD_CUSTOM_REAL:
4164                     option = gtk_printer_option_new (gtk_name, label,
4165                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
4166                   break;
4167                 case PPD_CUSTOM_STRING:
4168                   option = gtk_printer_option_new (gtk_name, label,
4169                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
4170                   break;
4171 #ifdef PRINT_IGNORED_OPTIONS
4172                 case PPD_CUSTOM_POINTS:
4173                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
4174                   break;
4175                 case PPD_CUSTOM_CURVE:
4176                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
4177                   break;
4178                 case PPD_CUSTOM_INVCURVE:
4179                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
4180                   break;
4181 #endif
4182                 default:
4183                   break;
4184                 }
4185             }
4186 #ifdef PRINT_IGNORED_OPTIONS
4187           else
4188             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
4189 #endif
4190         }
4191
4192       if (!option)
4193         option = gtk_printer_option_new (gtk_name, label,
4194                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
4195       g_free (label);
4196
4197       gtk_printer_option_allocate_choices (option, n_choices);
4198       for (i = 0; i < n_choices; i++)
4199         {
4200           if (available[i] == NULL)
4201             {
4202               /* This was auto-added */
4203               option->choices[i] = g_strdup ("gtk-ignore-value");
4204               option->choices_display[i] = g_strdup (_("Printer Default"));
4205             }
4206           else
4207             {
4208               option->choices[i] = g_strdup (available[i]->choice);
4209               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
4210             }
4211         }
4212
4213       if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
4214         {
4215           if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
4216             gtk_printer_option_set (option, ppd_option->defchoice + 7);
4217           else
4218             gtk_printer_option_set (option, ppd_option->defchoice);
4219         }
4220       else
4221         {
4222           gtk_printer_option_set (option, ppd_option->defchoice);
4223         }
4224     }
4225 #ifdef PRINT_IGNORED_OPTIONS
4226   else
4227     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
4228 #endif
4229   g_free (available);
4230
4231   return option;
4232 }
4233
4234 static GtkPrinterOption *
4235 create_boolean_option (ppd_file_t   *ppd_file,
4236                        ppd_option_t *ppd_option,
4237                        const gchar  *gtk_name)
4238 {
4239   GtkPrinterOption *option;
4240   ppd_choice_t **available;
4241   char *label;
4242   int n_choices;
4243
4244   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
4245
4246   option = NULL;
4247
4248   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
4249   if (n_choices == 2)
4250     {
4251       label = get_option_text (ppd_file, ppd_option);
4252       option = gtk_printer_option_new (gtk_name, label,
4253                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
4254       g_free (label);
4255
4256       gtk_printer_option_allocate_choices (option, 2);
4257       option->choices[0] = g_strdup ("True");
4258       option->choices_display[0] = g_strdup ("True");
4259       option->choices[1] = g_strdup ("False");
4260       option->choices_display[1] = g_strdup ("False");
4261
4262       gtk_printer_option_set (option, ppd_option->defchoice);
4263     }
4264 #ifdef PRINT_IGNORED_OPTIONS
4265   else
4266     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
4267 #endif
4268   g_free (available);
4269
4270   return option;
4271 }
4272
4273 static gchar *
4274 get_ppd_option_name (const gchar *keyword)
4275 {
4276   int i;
4277
4278   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
4279     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
4280       return g_strdup (ppd_option_names[i].name);
4281
4282   return g_strdup_printf ("cups-%s", keyword);
4283 }
4284
4285 static gchar *
4286 get_lpoption_name (const gchar *lpoption)
4287 {
4288   int i;
4289
4290   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
4291     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
4292       return g_strdup (ppd_option_names[i].name);
4293
4294   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
4295     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
4296       return g_strdup (lpoption_names[i].name);
4297
4298   return g_strdup_printf ("cups-%s", lpoption);
4299 }
4300
4301 static int
4302 strptr_cmp (const void *a,
4303             const void *b)
4304 {
4305   char **aa = (char **)a;
4306   char **bb = (char **)b;
4307   return strcmp (*aa, *bb);
4308 }
4309
4310
4311 static gboolean
4312 string_in_table (gchar       *str,
4313                  const gchar *table[],
4314                  gint         table_len)
4315 {
4316   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
4317 }
4318
4319 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
4320
4321 static void
4322 handle_option (GtkPrinterOptionSet *set,
4323                ppd_file_t          *ppd_file,
4324                ppd_option_t        *ppd_option,
4325                ppd_group_t         *toplevel_group,
4326                GtkPrintSettings    *settings)
4327 {
4328   GtkPrinterOption *option;
4329   char *name;
4330   int i;
4331
4332   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
4333     return;
4334
4335   name = get_ppd_option_name (ppd_option->keyword);
4336
4337   option = NULL;
4338   if (ppd_option->ui == PPD_UI_PICKONE)
4339     {
4340       option = create_pickone_option (ppd_file, ppd_option, name);
4341     }
4342   else if (ppd_option->ui == PPD_UI_BOOLEAN)
4343     {
4344       option = create_boolean_option (ppd_file, ppd_option, name);
4345     }
4346 #ifdef PRINT_IGNORED_OPTIONS
4347   else
4348     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
4349 #endif
4350
4351   if (option)
4352     {
4353       char *name;
4354
4355       name = ppd_group_name (toplevel_group);
4356       if (STRING_IN_TABLE (name,
4357                            color_group_whitelist) ||
4358           STRING_IN_TABLE (ppd_option->keyword,
4359                            color_option_whitelist))
4360         {
4361           option->group = g_strdup ("ColorPage");
4362         }
4363       else if (STRING_IN_TABLE (name,
4364                                 image_quality_group_whitelist) ||
4365                STRING_IN_TABLE (ppd_option->keyword,
4366                                 image_quality_option_whitelist))
4367         {
4368           option->group = g_strdup ("ImageQualityPage");
4369         }
4370       else if (STRING_IN_TABLE (name,
4371                                 finishing_group_whitelist) ||
4372                STRING_IN_TABLE (ppd_option->keyword,
4373                                 finishing_option_whitelist))
4374         {
4375           option->group = g_strdup ("FinishingPage");
4376         }
4377       else
4378         {
4379           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
4380             {
4381               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
4382                 {
4383                   option->group = g_strdup (_(cups_group_translations[i].translation));
4384                   break;
4385                 }
4386             }
4387
4388           if (i == G_N_ELEMENTS (cups_group_translations))
4389             option->group = g_strdup (toplevel_group->text);
4390         }
4391
4392       set_option_from_settings (option, settings);
4393
4394       gtk_printer_option_set_add (set, option);
4395     }
4396
4397   g_free (name);
4398 }
4399
4400 static void
4401 handle_group (GtkPrinterOptionSet *set,
4402               ppd_file_t          *ppd_file,
4403               ppd_group_t         *group,
4404               ppd_group_t         *toplevel_group,
4405               GtkPrintSettings    *settings)
4406 {
4407   gint i;
4408   gchar *name;
4409
4410   /* Ignore installable options */
4411   name = ppd_group_name (toplevel_group);
4412   if (strcmp (name, "InstallableOptions") == 0)
4413     return;
4414
4415   for (i = 0; i < group->num_options; i++)
4416     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
4417
4418   for (i = 0; i < group->num_subgroups; i++)
4419     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
4420
4421 }
4422
4423 #ifdef HAVE_COLORD
4424
4425 typedef struct {
4426         GtkPrintSettings     *settings;
4427         GtkPrinter           *printer;
4428 } GtkPrintBackendCupsColordHelper;
4429
4430 static void
4431 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
4432                                       GtkPrintBackendCupsColordHelper *helper)
4433 {
4434   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
4435                                     helper->settings,
4436                                     set);
4437 }
4438 #endif
4439
4440 static GtkPrinterOptionSet *
4441 cups_printer_get_options (GtkPrinter           *printer,
4442                           GtkPrintSettings     *settings,
4443                           GtkPageSetup         *page_setup,
4444                           GtkPrintCapabilities  capabilities)
4445 {
4446   GtkPrinterOptionSet *set;
4447   GtkPrinterOption *option;
4448   ppd_file_t *ppd_file;
4449   int i;
4450   char *print_at[] = { "now", "at", "on-hold" };
4451   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
4452   char *prio[] = {"100", "80", "50", "30" };
4453   /* Translators: These strings name the possible values of the
4454    * job priority option in the print dialog
4455    */
4456   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
4457   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
4458   /* Translators: These strings name the possible arrangements of
4459    * multiple pages on a sheet when printing
4460    */
4461   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
4462                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
4463                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
4464                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
4465   char *name;
4466   int num_opts;
4467   cups_option_t *opts = NULL;
4468   GtkPrintBackendCups *backend;
4469   GtkTextDirection text_direction;
4470   GtkPrinterCups *cups_printer = NULL;
4471 #ifdef HAVE_COLORD
4472   GtkPrintBackendCupsColordHelper *helper;
4473 #endif
4474   char *default_number_up;
4475
4476   set = gtk_printer_option_set_new ();
4477
4478   /* Cups specific, non-ppd related settings */
4479
4480   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
4481     prio_display[i] = _(prio_display[i]);
4482
4483   /* Translators, this string is used to label the job priority option
4484    * in the print dialog
4485    */
4486   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4487   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
4488                                          prio, prio_display);
4489   gtk_printer_option_set (option, "50");
4490   set_option_from_settings (option, settings);
4491   gtk_printer_option_set_add (set, option);
4492   g_object_unref (option);
4493
4494   /* Translators, this string is used to label the billing info entry
4495    * in the print dialog
4496    */
4497   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
4498   gtk_printer_option_set (option, "");
4499   set_option_from_settings (option, settings);
4500   gtk_printer_option_set_add (set, option);
4501   g_object_unref (option);
4502
4503   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
4504   cups_printer = GTK_PRINTER_CUPS (printer);
4505
4506   if (backend != NULL && printer != NULL)
4507     {
4508       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
4509       /* Translators, these strings are names for various 'standard' cover
4510        * pages that the printing system may support.
4511        */
4512       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
4513       char **cover = NULL;
4514       char **cover_display = NULL;
4515       char **cover_display_translated = NULL;
4516       gint num_of_covers = 0;
4517       gpointer value;
4518       gint j;
4519
4520        /* Translators, this string is used to label the pages-per-sheet option
4521         * in the print dialog
4522         */
4523       option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4524       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
4525                                              n_up, n_up);
4526       default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
4527       gtk_printer_option_set (option, default_number_up);
4528       g_free (default_number_up);
4529       set_option_from_settings (option, settings);
4530       gtk_printer_option_set_add (set, option);
4531       g_object_unref (option);
4532
4533       if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
4534         {
4535           for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
4536             n_up_layout_display[i] = _(n_up_layout_display[i]);
4537
4538            /* Translators, this string is used to label the option in the print
4539             * dialog that controls in what order multiple pages are arranged
4540             */
4541           option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4542           gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
4543                                                  n_up_layout, n_up_layout_display);
4544
4545           text_direction = gtk_widget_get_default_direction ();
4546           if (text_direction == GTK_TEXT_DIR_LTR)
4547             gtk_printer_option_set (option, "lrtb");
4548           else
4549             gtk_printer_option_set (option, "rltb");
4550
4551           set_option_from_settings (option, settings);
4552           gtk_printer_option_set_add (set, option);
4553           g_object_unref (option);
4554         }
4555
4556       num_of_covers = backend->number_of_covers;
4557       cover = g_new (char *, num_of_covers + 1);
4558       cover[num_of_covers] = NULL;
4559       cover_display = g_new (char *, num_of_covers + 1);
4560       cover_display[num_of_covers] = NULL;
4561       cover_display_translated = g_new (char *, num_of_covers + 1);
4562       cover_display_translated[num_of_covers] = NULL;
4563
4564       for (i = 0; i < num_of_covers; i++)
4565         {
4566           cover[i] = g_strdup (backend->covers[i]);
4567           value = NULL;
4568           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
4569             if (strcmp (cover_default[j], cover[i]) == 0)
4570               {
4571                 value = cover_display_default[j];
4572                 break;
4573               }
4574           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
4575         }
4576
4577       for (i = 0; i < num_of_covers; i++)
4578         cover_display_translated[i] = _(cover_display[i]);
4579
4580       /* Translators, this is the label used for the option in the print
4581        * dialog that controls the front cover page.
4582        */
4583       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4584       gtk_printer_option_choices_from_array (option, num_of_covers,
4585                                          cover, cover_display_translated);
4586
4587       if (cups_printer->default_cover_before != NULL)
4588         gtk_printer_option_set (option, cups_printer->default_cover_before);
4589       else
4590         gtk_printer_option_set (option, "none");
4591       set_option_from_settings (option, settings);
4592       gtk_printer_option_set_add (set, option);
4593       g_object_unref (option);
4594
4595       /* Translators, this is the label used for the option in the print
4596        * dialog that controls the back cover page.
4597        */
4598       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4599       gtk_printer_option_choices_from_array (option, num_of_covers,
4600                                          cover, cover_display_translated);
4601       if (cups_printer->default_cover_after != NULL)
4602         gtk_printer_option_set (option, cups_printer->default_cover_after);
4603       else
4604         gtk_printer_option_set (option, "none");
4605       set_option_from_settings (option, settings);
4606       gtk_printer_option_set_add (set, option);
4607       g_object_unref (option);
4608
4609       g_strfreev (cover);
4610       g_strfreev (cover_display);
4611       g_free (cover_display_translated);
4612     }
4613
4614   /* Translators: this is the name of the option that controls when
4615    * a print job is printed. Possible values are 'now', a specified time,
4616    * or 'on hold'
4617    */
4618   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
4619   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
4620                                          print_at, print_at);
4621   gtk_printer_option_set (option, "now");
4622   set_option_from_settings (option, settings);
4623   gtk_printer_option_set_add (set, option);
4624   g_object_unref (option);
4625
4626   /* Translators: this is the name of the option that allows the user
4627    * to specify a time when a print job will be printed.
4628    */
4629   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
4630   gtk_printer_option_set (option, "");
4631   set_option_from_settings (option, settings);
4632   gtk_printer_option_set_add (set, option);
4633   g_object_unref (option);
4634
4635   /* Printer (ppd) specific settings */
4636   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4637   if (ppd_file)
4638     {
4639       GtkPaperSize *paper_size;
4640       ppd_option_t *option;
4641       const gchar  *ppd_name;
4642
4643       ppdMarkDefaults (ppd_file);
4644
4645       paper_size = gtk_page_setup_get_paper_size (page_setup);
4646
4647       option = ppdFindOption (ppd_file, "PageSize");
4648       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
4649
4650       if (ppd_name)
4651         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
4652       else
4653         {
4654           gchar *custom_name;
4655           char width[G_ASCII_DTOSTR_BUF_SIZE];
4656           char height[G_ASCII_DTOSTR_BUF_SIZE];
4657
4658           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4659           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4660           /* Translators: this format is used to display a custom paper
4661            * size. The two placeholders are replaced with the width and height
4662            * in points. E.g: "Custom 230.4x142.9"
4663            */
4664           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
4665           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
4666           g_free (custom_name);
4667         }
4668
4669       for (i = 0; i < ppd_file->num_groups; i++)
4670         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
4671     }
4672
4673   /* Now honor the user set defaults for this printer */
4674   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
4675
4676   for (i = 0; i < num_opts; i++)
4677     {
4678       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
4679         continue;
4680
4681       name = get_lpoption_name (opts[i].name);
4682       if (strcmp (name, "cups-job-sheets") == 0)
4683         {
4684           gchar **values;
4685           gint    num_values;
4686
4687           values = g_strsplit (opts[i].value, ",", 2);
4688           num_values = g_strv_length (values);
4689
4690           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
4691           if (option && num_values > 0)
4692             gtk_printer_option_set (option, g_strstrip (values[0]));
4693
4694           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
4695           if (option && num_values > 1)
4696             gtk_printer_option_set (option, g_strstrip (values[1]));
4697
4698           g_strfreev (values);
4699         }
4700       else if (strcmp (name, "cups-job-hold-until") == 0)
4701         {
4702           GtkPrinterOption *option2 = NULL;
4703
4704           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
4705           if (option && opts[i].value)
4706             {
4707               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
4708               if (option2)
4709                 {
4710                   if (strcmp (opts[i].value, "indefinite") == 0)
4711                     gtk_printer_option_set (option2, "on-hold");
4712                   else
4713                     {
4714                       gtk_printer_option_set (option2, "at");
4715                       gtk_printer_option_set (option, opts[i].value);
4716                     }
4717                 }
4718             }
4719         }
4720       else if (strcmp (name, "cups-sides") == 0)
4721         {
4722           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
4723           if (option && opts[i].value)
4724             {
4725               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
4726                 gtk_printer_option_set (option, "DuplexTumble");
4727               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
4728                 gtk_printer_option_set (option, "DuplexNoTumble");
4729             }
4730         }
4731       else
4732         {
4733           option = gtk_printer_option_set_lookup (set, name);
4734           if (option)
4735             gtk_printer_option_set (option, opts[i].value);
4736         }
4737       g_free (name);
4738     }
4739
4740   cupsFreeOptions (num_opts, opts);
4741
4742 #ifdef HAVE_COLORD
4743   /* TRANSLATORS: this this the ICC color profile to use for this job */
4744   option = gtk_printer_option_new ("colord-profile",
4745                                    _("Printer Profile"),
4746                                    GTK_PRINTER_OPTION_TYPE_INFO);
4747
4748   /* assign it to the color page */
4749   option->group = g_strdup ("ColorPage");
4750
4751   /* TRANSLATORS: this is when color profile information is unavailable */
4752   gtk_printer_option_set (option, _("Unavailable"));
4753   gtk_printer_option_set_add (set, option);
4754
4755   /* watch to see if the user changed the options */
4756   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
4757   helper->printer = printer;
4758   helper->settings = settings;
4759   g_signal_connect_data (set, "changed",
4760                          G_CALLBACK (colord_printer_option_set_changed_cb),
4761                          helper,
4762                          (GClosureNotify) g_free,
4763                          0);
4764
4765   /* initial coldplug */
4766   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
4767                                     settings, set);
4768   g_object_bind_property (printer, "profile-title",
4769                           option, "value",
4770                           G_BINDING_DEFAULT);
4771
4772 #endif
4773
4774   return set;
4775 }
4776
4777
4778 static void
4779 mark_option_from_set (GtkPrinterOptionSet *set,
4780                       ppd_file_t          *ppd_file,
4781                       ppd_option_t        *ppd_option)
4782 {
4783   GtkPrinterOption *option;
4784   char *name = get_ppd_option_name (ppd_option->keyword);
4785
4786   option = gtk_printer_option_set_lookup (set, name);
4787
4788   if (option)
4789     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
4790
4791   g_free (name);
4792 }
4793
4794
4795 static void
4796 mark_group_from_set (GtkPrinterOptionSet *set,
4797                      ppd_file_t          *ppd_file,
4798                      ppd_group_t         *group)
4799 {
4800   int i;
4801
4802   for (i = 0; i < group->num_options; i++)
4803     mark_option_from_set (set, ppd_file, &group->options[i]);
4804
4805   for (i = 0; i < group->num_subgroups; i++)
4806     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
4807 }
4808
4809 static void
4810 set_conflicts_from_option (GtkPrinterOptionSet *set,
4811                            ppd_file_t          *ppd_file,
4812                            ppd_option_t        *ppd_option)
4813 {
4814   GtkPrinterOption *option;
4815   char *name;
4816
4817   if (ppd_option->conflicted)
4818     {
4819       name = get_ppd_option_name (ppd_option->keyword);
4820       option = gtk_printer_option_set_lookup (set, name);
4821
4822       if (option)
4823         gtk_printer_option_set_has_conflict (option, TRUE);
4824 #ifdef PRINT_IGNORED_OPTIONS
4825       else
4826         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
4827 #endif
4828
4829       g_free (name);
4830     }
4831 }
4832
4833 static void
4834 set_conflicts_from_group (GtkPrinterOptionSet *set,
4835                           ppd_file_t          *ppd_file,
4836                           ppd_group_t         *group)
4837 {
4838   int i;
4839
4840   for (i = 0; i < group->num_options; i++)
4841     set_conflicts_from_option (set, ppd_file, &group->options[i]);
4842
4843   for (i = 0; i < group->num_subgroups; i++)
4844     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
4845 }
4846
4847 static gboolean
4848 cups_printer_mark_conflicts (GtkPrinter          *printer,
4849                              GtkPrinterOptionSet *options)
4850 {
4851   ppd_file_t *ppd_file;
4852   int num_conflicts;
4853   int i;
4854
4855   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4856
4857   if (ppd_file == NULL)
4858     return FALSE;
4859
4860   ppdMarkDefaults (ppd_file);
4861
4862   for (i = 0; i < ppd_file->num_groups; i++)
4863     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
4864
4865   num_conflicts = ppdConflicts (ppd_file);
4866
4867   if (num_conflicts > 0)
4868     {
4869       for (i = 0; i < ppd_file->num_groups; i++)
4870         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
4871     }
4872
4873   return num_conflicts > 0;
4874 }
4875
4876 struct OptionData {
4877   GtkPrinter *printer;
4878   GtkPrinterOptionSet *options;
4879   GtkPrintSettings *settings;
4880   ppd_file_t *ppd_file;
4881 };
4882
4883 typedef struct {
4884   const char *cups;
4885   const char *standard;
4886 } NameMapping;
4887
4888 static void
4889 map_settings_to_option (GtkPrinterOption  *option,
4890                         const NameMapping  table[],
4891                         gint               n_elements,
4892                         GtkPrintSettings  *settings,
4893                         const gchar       *standard_name,
4894                         const gchar       *cups_name)
4895 {
4896   int i;
4897   char *name;
4898   const char *cups_value;
4899   const char *standard_value;
4900
4901   /* If the cups-specific setting is set, always use that */
4902   name = g_strdup_printf ("cups-%s", cups_name);
4903   cups_value = gtk_print_settings_get (settings, name);
4904   g_free (name);
4905
4906   if (cups_value != NULL)
4907     {
4908       gtk_printer_option_set (option, cups_value);
4909       return;
4910     }
4911
4912   /* Otherwise we try to convert from the general setting */
4913   standard_value = gtk_print_settings_get (settings, standard_name);
4914   if (standard_value == NULL)
4915     return;
4916
4917   for (i = 0; i < n_elements; i++)
4918     {
4919       if (table[i].cups == NULL && table[i].standard == NULL)
4920         {
4921           gtk_printer_option_set (option, standard_value);
4922           break;
4923         }
4924       else if (table[i].cups == NULL &&
4925                strcmp (table[i].standard, standard_value) == 0)
4926         {
4927           set_option_off (option);
4928           break;
4929         }
4930       else if (strcmp (table[i].standard, standard_value) == 0)
4931         {
4932           gtk_printer_option_set (option, table[i].cups);
4933           break;
4934         }
4935     }
4936 }
4937
4938 static void
4939 map_option_to_settings (const gchar       *value,
4940                         const NameMapping  table[],
4941                         gint               n_elements,
4942                         GtkPrintSettings  *settings,
4943                         const gchar       *standard_name,
4944                         const gchar       *cups_name)
4945 {
4946   int i;
4947   char *name;
4948
4949   for (i = 0; i < n_elements; i++)
4950     {
4951       if (table[i].cups == NULL && table[i].standard == NULL)
4952         {
4953           gtk_print_settings_set (settings,
4954                                   standard_name,
4955                                   value);
4956           break;
4957         }
4958       else if (table[i].cups == NULL && table[i].standard != NULL)
4959         {
4960           if (value_is_off (value))
4961             {
4962               gtk_print_settings_set (settings,
4963                                       standard_name,
4964                                       table[i].standard);
4965               break;
4966             }
4967         }
4968       else if (strcmp (table[i].cups, value) == 0)
4969         {
4970           gtk_print_settings_set (settings,
4971                                   standard_name,
4972                                   table[i].standard);
4973           break;
4974         }
4975     }
4976
4977   /* Always set the corresponding cups-specific setting */
4978   name = g_strdup_printf ("cups-%s", cups_name);
4979   gtk_print_settings_set (settings, name, value);
4980   g_free (name);
4981 }
4982
4983
4984 static const NameMapping paper_source_map[] = {
4985   { "Lower", "lower"},
4986   { "Middle", "middle"},
4987   { "Upper", "upper"},
4988   { "Rear", "rear"},
4989   { "Envelope", "envelope"},
4990   { "Cassette", "cassette"},
4991   { "LargeCapacity", "large-capacity"},
4992   { "AnySmallFormat", "small-format"},
4993   { "AnyLargeFormat", "large-format"},
4994   { NULL, NULL}
4995 };
4996
4997 static const NameMapping output_tray_map[] = {
4998   { "Upper", "upper"},
4999   { "Lower", "lower"},
5000   { "Rear", "rear"},
5001   { NULL, NULL}
5002 };
5003
5004 static const NameMapping duplex_map[] = {
5005   { "DuplexTumble", "vertical" },
5006   { "DuplexNoTumble", "horizontal" },
5007   { NULL, "simplex" }
5008 };
5009
5010 static const NameMapping output_mode_map[] = {
5011   { "Standard", "normal" },
5012   { "Normal", "normal" },
5013   { "Draft", "draft" },
5014   { "Fast", "draft" },
5015 };
5016
5017 static const NameMapping media_type_map[] = {
5018   { "Transparency", "transparency"},
5019   { "Standard", "stationery"},
5020   { NULL, NULL}
5021 };
5022
5023 static const NameMapping all_map[] = {
5024   { NULL, NULL}
5025 };
5026
5027
5028 static void
5029 set_option_from_settings (GtkPrinterOption *option,
5030                           GtkPrintSettings *settings)
5031 {
5032   const char *cups_value;
5033   char *value;
5034
5035   if (settings == NULL)
5036     return;
5037
5038   if (strcmp (option->name, "gtk-paper-source") == 0)
5039     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
5040                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
5041   else if (strcmp (option->name, "gtk-output-tray") == 0)
5042     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
5043                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
5044   else if (strcmp (option->name, "gtk-duplex") == 0)
5045     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
5046                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
5047   else if (strcmp (option->name, "cups-OutputMode") == 0)
5048     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
5049                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
5050   else if (strcmp (option->name, "cups-Resolution") == 0)
5051     {
5052       cups_value = gtk_print_settings_get (settings, option->name);
5053       if (cups_value)
5054         gtk_printer_option_set (option, cups_value);
5055       else
5056         {
5057           if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
5058               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
5059               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
5060               option->value == NULL || option->value[0] == '\0')
5061             {
5062               int res = gtk_print_settings_get_resolution (settings);
5063               int res_x = gtk_print_settings_get_resolution_x (settings);
5064               int res_y = gtk_print_settings_get_resolution_y (settings);
5065
5066               if (res_x != res_y)
5067                 {
5068                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
5069                   gtk_printer_option_set (option, value);
5070                   g_free (value);
5071                 }
5072               else if (res != 0)
5073                 {
5074                   value = g_strdup_printf ("%ddpi", res);
5075                   gtk_printer_option_set (option, value);
5076                   g_free (value);
5077                 }
5078             }
5079         }
5080     }
5081   else if (strcmp (option->name, "gtk-paper-type") == 0)
5082     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
5083                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
5084   else if (strcmp (option->name, "gtk-n-up") == 0)
5085     {
5086       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
5087                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
5088     }
5089   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
5090     {
5091       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
5092                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
5093     }
5094   else if (strcmp (option->name, "gtk-billing-info") == 0)
5095     {
5096       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
5097       if (cups_value)
5098         gtk_printer_option_set (option, cups_value);
5099     }
5100   else if (strcmp (option->name, "gtk-job-prio") == 0)
5101     {
5102       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
5103       if (cups_value)
5104         gtk_printer_option_set (option, cups_value);
5105     }
5106   else if (strcmp (option->name, "gtk-cover-before") == 0)
5107     {
5108       cups_value = gtk_print_settings_get (settings, "cover-before");
5109       if (cups_value)
5110         gtk_printer_option_set (option, cups_value);
5111     }
5112   else if (strcmp (option->name, "gtk-cover-after") == 0)
5113     {
5114       cups_value = gtk_print_settings_get (settings, "cover-after");
5115       if (cups_value)
5116         gtk_printer_option_set (option, cups_value);
5117     }
5118   else if (strcmp (option->name, "gtk-print-time") == 0)
5119     {
5120       cups_value = gtk_print_settings_get (settings, "print-at");
5121       if (cups_value)
5122         gtk_printer_option_set (option, cups_value);
5123     }
5124   else if (strcmp (option->name, "gtk-print-time-text") == 0)
5125     {
5126       cups_value = gtk_print_settings_get (settings, "print-at-time");
5127       if (cups_value)
5128         gtk_printer_option_set (option, cups_value);
5129     }
5130   else if (g_str_has_prefix (option->name, "cups-"))
5131     {
5132       cups_value = gtk_print_settings_get (settings, option->name);
5133       if (cups_value)
5134         gtk_printer_option_set (option, cups_value);
5135     }
5136 }
5137
5138 static void
5139 foreach_option_get_settings (GtkPrinterOption *option,
5140                              gpointer          user_data)
5141 {
5142   struct OptionData *data = user_data;
5143   GtkPrintSettings *settings = data->settings;
5144   const char *value;
5145
5146   value = option->value;
5147
5148   if (strcmp (option->name, "gtk-paper-source") == 0)
5149     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
5150                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
5151   else if (strcmp (option->name, "gtk-output-tray") == 0)
5152     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
5153                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
5154   else if (strcmp (option->name, "gtk-duplex") == 0)
5155     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
5156                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
5157   else if (strcmp (option->name, "cups-OutputMode") == 0)
5158     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
5159                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
5160   else if (strcmp (option->name, "cups-Resolution") == 0)
5161     {
5162       int res, res_x, res_y;
5163
5164       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
5165         {
5166           if (res_x > 0 && res_y > 0)
5167             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
5168         }
5169       else if (sscanf (value, "%ddpi", &res) == 1)
5170         {
5171           if (res > 0)
5172             gtk_print_settings_set_resolution (settings, res);
5173         }
5174
5175       gtk_print_settings_set (settings, option->name, value);
5176     }
5177   else if (strcmp (option->name, "gtk-paper-type") == 0)
5178     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
5179                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
5180   else if (strcmp (option->name, "gtk-n-up") == 0)
5181     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
5182                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
5183   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
5184     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
5185                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
5186   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
5187     gtk_print_settings_set (settings, "cups-job-billing", value);
5188   else if (strcmp (option->name, "gtk-job-prio") == 0)
5189     gtk_print_settings_set (settings, "cups-job-priority", value);
5190   else if (strcmp (option->name, "gtk-cover-before") == 0)
5191     gtk_print_settings_set (settings, "cover-before", value);
5192   else if (strcmp (option->name, "gtk-cover-after") == 0)
5193     gtk_print_settings_set (settings, "cover-after", value);
5194   else if (strcmp (option->name, "gtk-print-time") == 0)
5195     gtk_print_settings_set (settings, "print-at", value);
5196   else if (strcmp (option->name, "gtk-print-time-text") == 0)
5197     gtk_print_settings_set (settings, "print-at-time", value);
5198   else if (g_str_has_prefix (option->name, "cups-"))
5199     gtk_print_settings_set (settings, option->name, value);
5200 }
5201
5202 static gboolean
5203 supports_am_pm (void)
5204 {
5205   struct tm tmp_tm = { 0 };
5206   char   time[8];
5207   int    length;
5208
5209   length = strftime (time, sizeof (time), "%p", &tmp_tm);
5210
5211   return length != 0;
5212 }
5213
5214 /* Converts local time to UTC time. Local time has to be in one of these
5215  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
5216  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
5217  * Returns a newly allocated string holding UTC time in HH:MM:SS format
5218  * or NULL.
5219  */
5220 gchar *
5221 localtime_to_utctime (const char *local_time)
5222 {
5223   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
5224                              " %H : %M : %S ",
5225                              " %I : %M %p ", " %p %I : %M ",
5226                              " %H : %M ",
5227                              " %I %p ", " %p %I "};
5228   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
5229   const char *end = NULL;
5230   struct tm  *actual_local_time;
5231   struct tm  *actual_utc_time;
5232   struct tm   local_print_time;
5233   struct tm   utc_print_time;
5234   struct tm   diff_time;
5235   gchar      *utc_time = NULL;
5236   int         i, n;
5237
5238   if (local_time == NULL || local_time[0] == '\0')
5239     return NULL;
5240
5241   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
5242
5243   for (i = 0; i < n; i++)
5244     {
5245       local_print_time.tm_hour = 0;
5246       local_print_time.tm_min  = 0;
5247       local_print_time.tm_sec  = 0;
5248
5249       if (supports_am_pm ())
5250         end = strptime (local_time, formats_0[i], &local_print_time);
5251       else
5252         end = strptime (local_time, formats_1[i], &local_print_time);
5253
5254       if (end != NULL && end[0] == '\0')
5255         break;
5256     }
5257
5258   if (end != NULL && end[0] == '\0')
5259     {
5260       time_t rawtime;
5261       time (&rawtime);
5262
5263       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
5264       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
5265
5266       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
5267       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
5268       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
5269
5270       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
5271       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
5272       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
5273
5274       utc_time = g_strdup_printf ("%02d:%02d:%02d",
5275                                   utc_print_time.tm_hour,
5276                                   utc_print_time.tm_min,
5277                                   utc_print_time.tm_sec);
5278     }
5279
5280   return utc_time;
5281 }
5282
5283 static void
5284 cups_printer_get_settings_from_options (GtkPrinter          *printer,
5285                                         GtkPrinterOptionSet *options,
5286                                         GtkPrintSettings    *settings)
5287 {
5288   struct OptionData data;
5289   const char *print_at, *print_at_time;
5290
5291   data.printer = printer;
5292   data.options = options;
5293   data.settings = settings;
5294   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5295
5296   gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
5297   if (data.ppd_file != NULL)
5298     {
5299       GtkPrinterOption *cover_before, *cover_after;
5300
5301       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
5302       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
5303       if (cover_before && cover_after)
5304         {
5305           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
5306           gtk_print_settings_set (settings, "cups-job-sheets", value);
5307           g_free (value);
5308         }
5309
5310       print_at = gtk_print_settings_get (settings, "print-at");
5311       print_at_time = gtk_print_settings_get (settings, "print-at-time");
5312
5313       if (strcmp (print_at, "at") == 0)
5314         {
5315           gchar *utc_time = NULL;
5316
5317           utc_time = localtime_to_utctime (print_at_time);
5318
5319           if (utc_time != NULL)
5320             {
5321               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
5322               g_free (utc_time);
5323             }
5324           else
5325             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
5326         }
5327       else if (strcmp (print_at, "on-hold") == 0)
5328         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
5329     }
5330 }
5331
5332 static void
5333 cups_printer_prepare_for_print (GtkPrinter       *printer,
5334                                 GtkPrintJob      *print_job,
5335                                 GtkPrintSettings *settings,
5336                                 GtkPageSetup     *page_setup)
5337 {
5338   GtkPrintPages pages;
5339   GtkPageRange *ranges;
5340   gint n_ranges;
5341   GtkPageSet page_set;
5342   GtkPaperSize *paper_size;
5343   const char *ppd_paper_name;
5344   double scale;
5345   GtkPrintCapabilities  capabilities;
5346
5347   capabilities = cups_printer_get_capabilities (printer);
5348   pages = gtk_print_settings_get_print_pages (settings);
5349   gtk_print_job_set_pages (print_job, pages);
5350
5351   if (pages == GTK_PRINT_PAGES_RANGES)
5352     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
5353   else
5354     {
5355       ranges = NULL;
5356       n_ranges = 0;
5357     }
5358
5359   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
5360
5361   if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
5362     {
5363       if (gtk_print_settings_get_collate (settings))
5364         gtk_print_settings_set (settings, "cups-Collate", "True");
5365       gtk_print_job_set_collate (print_job, FALSE);
5366     }
5367   else
5368     {
5369       gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
5370     }
5371
5372   if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
5373     {
5374       if (gtk_print_settings_get_reverse (settings))
5375         gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
5376       gtk_print_job_set_reverse (print_job, FALSE);
5377     }
5378   else
5379     {
5380       gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
5381     }
5382
5383   if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
5384     {
5385       if (gtk_print_settings_get_n_copies (settings) > 1)
5386         gtk_print_settings_set_int (settings, "cups-copies",
5387                                     gtk_print_settings_get_n_copies (settings));
5388       gtk_print_job_set_num_copies (print_job, 1);
5389     }
5390   else
5391     {
5392       gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings));
5393     }
5394
5395   scale = gtk_print_settings_get_scale (settings);
5396   if (scale != 100.0)
5397     gtk_print_job_set_scale (print_job, scale / 100.0);
5398
5399   page_set = gtk_print_settings_get_page_set (settings);
5400   if (page_set == GTK_PAGE_SET_EVEN)
5401     gtk_print_settings_set (settings, "cups-page-set", "even");
5402   else if (page_set == GTK_PAGE_SET_ODD)
5403     gtk_print_settings_set (settings, "cups-page-set", "odd");
5404   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
5405
5406   paper_size = gtk_page_setup_get_paper_size (page_setup);
5407   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
5408   if (ppd_paper_name != NULL)
5409     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
5410   else
5411     {
5412       char width[G_ASCII_DTOSTR_BUF_SIZE];
5413       char height[G_ASCII_DTOSTR_BUF_SIZE];
5414       char *custom_name;
5415
5416       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
5417       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
5418       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
5419       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
5420       g_free (custom_name);
5421     }
5422
5423   if (gtk_print_settings_get_number_up (settings) > 1)
5424     {
5425       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
5426       GEnumClass        *enum_class;
5427       GEnumValue        *enum_value;
5428
5429       switch (gtk_page_setup_get_orientation (page_setup))
5430         {
5431           case GTK_PAGE_ORIENTATION_PORTRAIT:
5432             break;
5433           case GTK_PAGE_ORIENTATION_LANDSCAPE:
5434             if (layout < 4)
5435               layout = layout + 2 + 4 * (1 - layout / 2);
5436             else
5437               layout = layout - 3 - 2 * (layout % 2);
5438             break;
5439           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
5440             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
5441             break;
5442           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
5443             if (layout < 4)
5444               layout = layout + 5 - 2 * (layout % 2);
5445             else
5446               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
5447             break;
5448         }
5449
5450       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
5451       enum_value = g_enum_get_value (enum_class, layout);
5452       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
5453       g_type_class_unref (enum_class);
5454
5455       if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
5456         {
5457           gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
5458           gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
5459         }
5460     }
5461
5462   gtk_print_job_set_rotate (print_job, TRUE);
5463 }
5464
5465 static GtkPageSetup *
5466 create_page_setup (ppd_file_t *ppd_file,
5467                    ppd_size_t *size)
5468  {
5469    char *display_name;
5470    GtkPageSetup *page_setup;
5471    GtkPaperSize *paper_size;
5472    ppd_option_t *option;
5473    ppd_choice_t *choice;
5474
5475   display_name = NULL;
5476   option = ppdFindOption (ppd_file, "PageSize");
5477   if (option)
5478     {
5479       choice = ppdFindChoice (option, size->name);
5480       if (choice)
5481         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
5482     }
5483
5484   if (display_name == NULL)
5485     display_name = g_strdup (size->name);
5486
5487   page_setup = gtk_page_setup_new ();
5488   paper_size = gtk_paper_size_new_from_ppd (size->name,
5489                                             display_name,
5490                                             size->width,
5491                                             size->length);
5492   gtk_page_setup_set_paper_size (page_setup, paper_size);
5493   gtk_paper_size_free (paper_size);
5494
5495   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
5496   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
5497   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
5498   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
5499
5500   g_free (display_name);
5501
5502   return page_setup;
5503 }
5504
5505 static GList *
5506 cups_printer_list_papers (GtkPrinter *printer)
5507 {
5508   ppd_file_t *ppd_file;
5509   ppd_size_t *size;
5510   GtkPageSetup *page_setup;
5511   GList *l;
5512   int i;
5513
5514   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5515   if (ppd_file == NULL)
5516     return NULL;
5517
5518   l = NULL;
5519
5520   for (i = 0; i < ppd_file->num_sizes; i++)
5521     {
5522       size = &ppd_file->sizes[i];
5523
5524       page_setup = create_page_setup (ppd_file, size);
5525
5526       l = g_list_prepend (l, page_setup);
5527     }
5528
5529   return g_list_reverse (l);
5530 }
5531
5532 static GtkPageSetup *
5533 cups_printer_get_default_page_size (GtkPrinter *printer)
5534 {
5535   ppd_file_t *ppd_file;
5536   ppd_size_t *size;
5537   ppd_option_t *option;
5538
5539
5540   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5541   if (ppd_file == NULL)
5542     return NULL;
5543
5544   option = ppdFindOption (ppd_file, "PageSize");
5545   if (option == NULL)
5546     return NULL;
5547
5548   size = ppdPageSize (ppd_file, option->defchoice);
5549   if (size == NULL)
5550     return NULL;
5551
5552   return create_page_setup (ppd_file, size);
5553 }
5554
5555 static gboolean
5556 cups_printer_get_hard_margins (GtkPrinter *printer,
5557                                gdouble    *top,
5558                                gdouble    *bottom,
5559                                gdouble    *left,
5560                                gdouble    *right)
5561 {
5562   ppd_file_t *ppd_file;
5563
5564   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5565   if (ppd_file == NULL)
5566     return FALSE;
5567
5568   *left = ppd_file->custom_margins[0];
5569   *bottom = ppd_file->custom_margins[1];
5570   *right = ppd_file->custom_margins[2];
5571   *top = ppd_file->custom_margins[3];
5572
5573   return TRUE;
5574 }
5575
5576 static GtkPrintCapabilities
5577 cups_printer_get_capabilities (GtkPrinter *printer)
5578 {
5579   GtkPrintCapabilities  capabilities = 0;
5580   GtkPrinterCups       *cups_printer = GTK_PRINTER_CUPS (printer);
5581
5582   if (gtk_printer_cups_get_ppd (cups_printer))
5583     {
5584       capabilities = GTK_PRINT_CAPABILITY_REVERSE;
5585     }
5586
5587   if (cups_printer->supports_copies)
5588     {
5589       capabilities |= GTK_PRINT_CAPABILITY_COPIES;
5590     }
5591
5592   if (cups_printer->supports_collate)
5593     {
5594       capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
5595     }
5596
5597   if (cups_printer->supports_number_up)
5598     {
5599       capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
5600                       GTK_PRINT_CAPABILITY_NUMBER_UP;
5601     }
5602
5603   return capabilities;
5604 }