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