]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Use the ppd groups "name" instead of "text" (bug #687065)
[~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_free_full (removed_printer_checklist, (GDestroyNotify) mark_printer_inactive);
2214       list_has_changed = TRUE;
2215     }
2216
2217 done:
2218   if (list_has_changed)
2219     g_signal_emit_by_name (backend, "printer-list-changed");
2220
2221   gtk_print_backend_set_list_done (backend);
2222
2223   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
2224     {
2225       cups_backend->default_printer = g_strdup (remote_default_printer);
2226       cups_backend->got_default_printer = TRUE;
2227       g_free (remote_default_printer);
2228
2229       if (cups_backend->default_printer != NULL)
2230         {
2231           GtkPrinter *default_printer = NULL;
2232           default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
2233                                                             cups_backend->default_printer);
2234           if (default_printer != NULL)
2235             {
2236               gtk_printer_set_is_default (default_printer, TRUE);
2237               g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
2238                                      "printer-status-changed", default_printer);
2239             }
2240         }
2241     }
2242
2243   gdk_threads_leave ();
2244 }
2245
2246 static void
2247 update_backend_status (GtkPrintBackendCups    *cups_backend,
2248                        GtkCupsConnectionState  state)
2249 {
2250   switch (state)
2251     {
2252     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
2253       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
2254       break;
2255     case GTK_CUPS_CONNECTION_AVAILABLE:
2256       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
2257       break;
2258     default: ;
2259     }
2260 }
2261
2262 static gboolean
2263 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
2264 {
2265   GtkCupsConnectionState state;
2266   GtkCupsRequest *request;
2267   static const char * const pattrs[] =  /* Attributes we're interested in */
2268     {
2269       "printer-name",
2270       "printer-uri-supported",
2271       "member-uris",
2272       "printer-location",
2273       "printer-info",
2274       "printer-state-message",
2275       "printer-state-reasons",
2276       "printer-state",
2277       "queued-job-count",
2278       "printer-is-accepting-jobs",
2279       "job-sheets-supported",
2280       "job-sheets-default",
2281       "printer-type",
2282       "auth-info-required",
2283       "number-up-default"
2284     };
2285
2286   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
2287     return TRUE;
2288
2289   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
2290   update_backend_status (cups_backend, state);
2291
2292   if (cups_backend->list_printers_attempts == 60)
2293     {
2294       cups_backend->list_printers_attempts = -1;
2295       if (cups_backend->list_printers_poll > 0)
2296         g_source_remove (cups_backend->list_printers_poll);
2297       cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
2298                                            (GSourceFunc) cups_request_printer_list,
2299                                            cups_backend);
2300     }
2301   else if (cups_backend->list_printers_attempts != -1)
2302     cups_backend->list_printers_attempts++;
2303
2304   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2305     return TRUE;
2306   else
2307     if (cups_backend->list_printers_attempts > 0)
2308       cups_backend->list_printers_attempts = 60;
2309
2310   cups_backend->list_printers_pending = TRUE;
2311
2312   request = gtk_cups_request_new_with_username (NULL,
2313                                                 GTK_CUPS_POST,
2314                                                 CUPS_GET_PRINTERS,
2315                                                 NULL,
2316                                                 NULL,
2317                                                 NULL,
2318                                                 cups_backend->username);
2319
2320   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2321                                     "requested-attributes", G_N_ELEMENTS (pattrs),
2322                                     NULL, pattrs);
2323
2324   cups_request_execute (cups_backend,
2325                         request,
2326                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
2327                         request,
2328                         NULL);
2329
2330   return TRUE;
2331 }
2332
2333 static void
2334 cups_get_printer_list (GtkPrintBackend *backend)
2335 {
2336   GtkPrintBackendCups *cups_backend;
2337
2338   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
2339
2340   if (cups_backend->cups_connection_test == NULL)
2341     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2342
2343   if (cups_backend->list_printers_poll == 0)
2344     {
2345       if (cups_request_printer_list (cups_backend))
2346         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
2347                                              (GSourceFunc) cups_request_printer_list,
2348                                              backend);
2349     }
2350 }
2351
2352 typedef struct {
2353   GtkPrinterCups *printer;
2354   GIOChannel *ppd_io;
2355   http_t *http;
2356 } GetPPDData;
2357
2358 static void
2359 get_ppd_data_free (GetPPDData *data)
2360 {
2361   GTK_NOTE (PRINTING,
2362             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2363   httpClose (data->http);
2364   g_io_channel_unref (data->ppd_io);
2365   g_object_unref (data->printer);
2366   g_free (data);
2367 }
2368
2369 static void
2370 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
2371                      GtkCupsResult       *result,
2372                      GetPPDData          *data)
2373 {
2374   GtkPrinter *printer;
2375
2376   gdk_threads_enter ();
2377
2378   GTK_NOTE (PRINTING,
2379             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2380
2381   printer = GTK_PRINTER (data->printer);
2382   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
2383   print_backend->reading_ppds--;
2384
2385   if (gtk_cups_result_is_error (result))
2386     {
2387       gboolean success = FALSE;
2388
2389       /* if we get a 404 then it is just a raw printer without a ppd
2390          and not an error */
2391       if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
2392           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
2393         {
2394           gtk_printer_set_has_details (printer, TRUE);
2395           success = TRUE;
2396         }
2397
2398       g_signal_emit_by_name (printer, "details-acquired", success);
2399       goto done;
2400     }
2401
2402   /* let ppdOpenFd take over the ownership of the open file */
2403   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
2404   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
2405   ppdLocalize (data->printer->ppd_file);
2406   ppdMarkDefaults (data->printer->ppd_file);
2407
2408   gtk_printer_set_has_details (printer, TRUE);
2409   g_signal_emit_by_name (printer, "details-acquired", TRUE);
2410
2411 done:
2412   gdk_threads_leave ();
2413 }
2414
2415 static gboolean
2416 cups_request_ppd (GtkPrinter *printer)
2417 {
2418   GError *error;
2419   GtkPrintBackend *print_backend;
2420   GtkPrinterCups *cups_printer;
2421   GtkCupsRequest *request;
2422   char *ppd_filename;
2423   gchar *resource;
2424   http_t *http;
2425   GetPPDData *data;
2426   int fd;
2427
2428   cups_printer = GTK_PRINTER_CUPS (printer);
2429
2430   error = NULL;
2431
2432   GTK_NOTE (PRINTING,
2433             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2434
2435   if (cups_printer->remote)
2436     {
2437       GtkCupsConnectionState state;
2438
2439       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
2440
2441       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
2442         {
2443           if (cups_printer->get_remote_ppd_attempts == 60)
2444             {
2445               cups_printer->get_remote_ppd_attempts = -1;
2446               if (cups_printer->get_remote_ppd_poll > 0)
2447                 g_source_remove (cups_printer->get_remote_ppd_poll);
2448               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
2449                                                     (GSourceFunc) cups_request_ppd,
2450                                                     printer);
2451             }
2452           else if (cups_printer->get_remote_ppd_attempts != -1)
2453             cups_printer->get_remote_ppd_attempts++;
2454
2455           return TRUE;
2456         }
2457
2458       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
2459       cups_printer->remote_cups_connection_test = NULL;
2460       cups_printer->get_remote_ppd_poll = 0;
2461       cups_printer->get_remote_ppd_attempts = 0;
2462
2463       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2464         {
2465           g_signal_emit_by_name (printer, "details-acquired", FALSE);
2466           return FALSE;
2467         }
2468     }
2469
2470   http = httpConnectEncrypt (cups_printer->hostname,
2471                              cups_printer->port,
2472                              cupsEncryption ());
2473
2474   data = g_new0 (GetPPDData, 1);
2475
2476   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
2477                         &ppd_filename,
2478                         &error);
2479
2480 #ifdef G_ENABLE_DEBUG
2481   /* If we are debugging printing don't delete the tmp files */
2482   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
2483     unlink (ppd_filename);
2484 #else
2485   unlink (ppd_filename);
2486 #endif /* G_ENABLE_DEBUG */
2487
2488   if (error != NULL)
2489     {
2490       GTK_NOTE (PRINTING,
2491                 g_warning ("CUPS Backend: Failed to create temp file, %s\n",
2492                            error->message));
2493       g_error_free (error);
2494       httpClose (http);
2495       g_free (ppd_filename);
2496       g_free (data);
2497
2498       g_signal_emit_by_name (printer, "details-acquired", FALSE);
2499       return FALSE;
2500     }
2501
2502   data->http = http;
2503   fchmod (fd, S_IRUSR | S_IWUSR);
2504   data->ppd_io = g_io_channel_unix_new (fd);
2505   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
2506   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
2507
2508   data->printer = g_object_ref (printer);
2509
2510   resource = g_strdup_printf ("/printers/%s.ppd",
2511                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
2512
2513   print_backend = gtk_printer_get_backend (printer);
2514
2515   request = gtk_cups_request_new_with_username (data->http,
2516                                                 GTK_CUPS_GET,
2517                                                 0,
2518                                                 data->ppd_io,
2519                                                 cups_printer->hostname,
2520                                                 resource,
2521                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
2522
2523   GTK_NOTE (PRINTING,
2524             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
2525
2526
2527   cups_printer->reading_ppd = TRUE;
2528   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
2529
2530   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
2531                         request,
2532                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
2533                         data,
2534                         (GDestroyNotify)get_ppd_data_free);
2535
2536   g_free (resource);
2537   g_free (ppd_filename);
2538
2539   return FALSE;
2540 }
2541
2542 /* Ordering matters for default preference */
2543 static const char *lpoptions_locations[] = {
2544   "/etc/cups/lpoptions",
2545   ".lpoptions",
2546   ".cups/lpoptions"
2547 };
2548
2549 static void
2550 cups_parse_user_default_printer (const char  *filename,
2551                                  char       **printer_name)
2552 {
2553   FILE *fp;
2554   char line[1024], *lineptr, *defname = NULL;
2555
2556   if ((fp = g_fopen (filename, "r")) == NULL)
2557     return;
2558
2559   while (fgets (line, sizeof (line), fp) != NULL)
2560     {
2561       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
2562         continue;
2563
2564       lineptr = line + 8;
2565       while (isspace (*lineptr))
2566         lineptr++;
2567
2568       if (!*lineptr)
2569         continue;
2570
2571       defname = lineptr;
2572       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
2573         lineptr++;
2574
2575       *lineptr = '\0';
2576
2577       if (*printer_name != NULL)
2578         g_free (*printer_name);
2579
2580       *printer_name = g_strdup (defname);
2581     }
2582
2583   fclose (fp);
2584 }
2585
2586 static void
2587 cups_get_user_default_printer (char **printer_name)
2588 {
2589   int i;
2590
2591   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2592     {
2593       if (g_path_is_absolute (lpoptions_locations[i]))
2594         {
2595           cups_parse_user_default_printer (lpoptions_locations[i],
2596                                            printer_name);
2597         }
2598       else
2599         {
2600           char *filename;
2601
2602           filename = g_build_filename (g_get_home_dir (),
2603                                        lpoptions_locations[i], NULL);
2604           cups_parse_user_default_printer (filename, printer_name);
2605           g_free (filename);
2606         }
2607     }
2608 }
2609
2610 static int
2611 cups_parse_user_options (const char     *filename,
2612                          const char     *printer_name,
2613                          int             num_options,
2614                          cups_option_t **options)
2615 {
2616   FILE *fp;
2617   gchar line[1024], *lineptr, *name;
2618
2619   if ((fp = g_fopen (filename, "r")) == NULL)
2620     return num_options;
2621
2622   while (fgets (line, sizeof (line), fp) != NULL)
2623     {
2624       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
2625         lineptr = line + 4;
2626       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
2627         lineptr = line + 7;
2628       else
2629         continue;
2630
2631       /* Skip leading whitespace */
2632       while (isspace (*lineptr))
2633         lineptr++;
2634
2635       if (!*lineptr)
2636         continue;
2637
2638       /* NUL-terminate the name, stripping the instance name */
2639       name = lineptr;
2640       while (!isspace (*lineptr) && *lineptr)
2641         {
2642           if (*lineptr == '/')
2643             *lineptr = '\0';
2644           lineptr++;
2645         }
2646
2647       if (!*lineptr)
2648         continue;
2649
2650       *lineptr++ = '\0';
2651
2652       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
2653           continue;
2654
2655       /* We found our printer, parse the options */
2656       num_options = cupsParseOptions (lineptr, num_options, options);
2657     }
2658
2659   fclose (fp);
2660
2661   return num_options;
2662 }
2663
2664 static int
2665 cups_get_user_options (const char     *printer_name,
2666                        int             num_options,
2667                        cups_option_t **options)
2668 {
2669   int i;
2670
2671   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2672     {
2673       if (g_path_is_absolute (lpoptions_locations[i]))
2674         {
2675            num_options = cups_parse_user_options (lpoptions_locations[i],
2676                                                   printer_name,
2677                                                   num_options,
2678                                                   options);
2679         }
2680       else
2681         {
2682           char *filename;
2683
2684           filename = g_build_filename (g_get_home_dir (),
2685                                        lpoptions_locations[i], NULL);
2686           num_options = cups_parse_user_options (filename, printer_name,
2687                                                  num_options, options);
2688           g_free (filename);
2689         }
2690     }
2691
2692   return num_options;
2693 }
2694
2695 /* This function requests default printer from a CUPS server in regular intervals.
2696  * In the case of unreachable CUPS server the request is repeated later.
2697  * The default printer is not requested in the case of previous success.
2698  */
2699 static void
2700 cups_get_default_printer (GtkPrintBackendCups *backend)
2701 {
2702   GtkPrintBackendCups *cups_backend;
2703
2704   cups_backend = backend;
2705
2706   if (cups_backend->cups_connection_test == NULL)
2707     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2708
2709   if (cups_backend->default_printer_poll == 0)
2710     {
2711       if (cups_request_default_printer (cups_backend))
2712         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
2713                                                (GSourceFunc) cups_request_default_printer,
2714                                                backend);
2715     }
2716 }
2717
2718 /* This function gets default printer from local settings.*/
2719 static void
2720 cups_get_local_default_printer (GtkPrintBackendCups *backend)
2721 {
2722   const char *str;
2723   char *name = NULL;
2724
2725   if ((str = g_getenv ("LPDEST")) != NULL)
2726     {
2727       backend->default_printer = g_strdup (str);
2728       backend->got_default_printer = TRUE;
2729       return;
2730     }
2731   else if ((str = g_getenv ("PRINTER")) != NULL &&
2732            strcmp (str, "lp") != 0)
2733     {
2734       backend->default_printer = g_strdup (str);
2735       backend->got_default_printer = TRUE;
2736       return;
2737     }
2738
2739   /* Figure out user setting for default printer */
2740   cups_get_user_default_printer (&name);
2741   if (name != NULL)
2742     {
2743       backend->default_printer = name;
2744       backend->got_default_printer = TRUE;
2745       return;
2746     }
2747 }
2748
2749 static void
2750 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
2751                                  GtkCupsResult       *result,
2752                                  gpointer             user_data)
2753 {
2754   ipp_t *response;
2755   ipp_attribute_t *attr;
2756   GtkPrinter *printer;
2757
2758   gdk_threads_enter ();
2759
2760   if (gtk_cups_result_is_error (result))
2761     {
2762       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2763           gtk_cups_result_get_error_code (result) == 1)
2764         {
2765           /* Canceled by user, stop popping up more password dialogs */
2766           if (print_backend->list_printers_poll > 0)
2767             g_source_remove (print_backend->list_printers_poll);
2768           print_backend->list_printers_poll = 0;
2769         }
2770
2771       return;
2772     }
2773
2774   response = gtk_cups_result_get_response (result);
2775
2776   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
2777       print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
2778
2779   print_backend->got_default_printer = TRUE;
2780
2781   if (print_backend->default_printer != NULL)
2782     {
2783       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
2784       if (printer != NULL)
2785         {
2786           gtk_printer_set_is_default (printer, TRUE);
2787           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
2788         }
2789     }
2790
2791   /* Make sure to kick off get_printers if we are polling it,
2792    * as we could have blocked this reading the default printer
2793    */
2794   if (print_backend->list_printers_poll != 0)
2795     cups_request_printer_list (print_backend);
2796
2797   gdk_threads_leave ();
2798 }
2799
2800 static gboolean
2801 cups_request_default_printer (GtkPrintBackendCups *print_backend)
2802 {
2803   GtkCupsConnectionState state;
2804   GtkCupsRequest *request;
2805
2806   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
2807   update_backend_status (print_backend, state);
2808
2809   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2810     return TRUE;
2811
2812   request = gtk_cups_request_new_with_username (NULL,
2813                                                 GTK_CUPS_POST,
2814                                                 CUPS_GET_DEFAULT,
2815                                                 NULL,
2816                                                 NULL,
2817                                                 NULL,
2818                                                 print_backend->username);
2819
2820   cups_request_execute (print_backend,
2821                         request,
2822                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
2823                         g_object_ref (print_backend),
2824                         g_object_unref);
2825
2826   return FALSE;
2827 }
2828
2829 static void
2830 cups_printer_request_details (GtkPrinter *printer)
2831 {
2832   GtkPrinterCups *cups_printer;
2833
2834   cups_printer = GTK_PRINTER_CUPS (printer);
2835   if (!cups_printer->reading_ppd &&
2836       gtk_printer_cups_get_ppd (cups_printer) == NULL)
2837     {
2838       if (cups_printer->remote)
2839         {
2840           if (cups_printer->get_remote_ppd_poll == 0)
2841             {
2842               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
2843
2844               if (cups_request_ppd (printer))
2845                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
2846                                                     (GSourceFunc) cups_request_ppd,
2847                                                     printer);
2848             }
2849         }
2850       else
2851         cups_request_ppd (printer);
2852     }
2853 }
2854
2855 static char *
2856 ppd_text_to_utf8 (ppd_file_t *ppd_file,
2857                   const char *text)
2858 {
2859   const char *encoding = NULL;
2860   char *res;
2861
2862   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
2863     {
2864       return g_strdup (text);
2865     }
2866   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
2867     {
2868       encoding = "ISO-8859-1";
2869     }
2870   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
2871     {
2872       encoding = "ISO-8859-2";
2873     }
2874   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
2875     {
2876       encoding = "ISO-8859-5";
2877     }
2878   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
2879     {
2880       encoding = "SHIFT-JIS";
2881     }
2882   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
2883     {
2884       encoding = "MACINTOSH";
2885     }
2886   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
2887     {
2888       encoding = "WINDOWS-1252";
2889     }
2890   else
2891     {
2892       /* Fallback, try iso-8859-1... */
2893       encoding = "ISO-8859-1";
2894     }
2895
2896   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
2897
2898   if (res == NULL)
2899     {
2900       GTK_NOTE (PRINTING,
2901                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
2902       res = g_strdup ("???");
2903     }
2904
2905   return res;
2906 }
2907
2908 /* TODO: Add more translations for common settings here */
2909
2910 static const struct {
2911   const char *keyword;
2912   const char *translation;
2913 } cups_option_translations[] = {
2914   { "Duplex", N_("Two Sided") },
2915   { "MediaType", N_("Paper Type") },
2916   { "InputSlot", N_("Paper Source") },
2917   { "OutputBin", N_("Output Tray") },
2918   { "Resolution", N_("Resolution") },
2919   { "PreFilter", N_("GhostScript pre-filtering") },
2920 };
2921
2922
2923 static const struct {
2924   const char *keyword;
2925   const char *choice;
2926   const char *translation;
2927 } cups_choice_translations[] = {
2928   { "Duplex", "None", N_("One Sided") },
2929   /* Translators: this is an option of "Two Sided" */
2930   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
2931   /* Translators: this is an option of "Two Sided" */
2932   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
2933   /* Translators: this is an option of "Paper Source" */
2934   { "InputSlot", "Auto", N_("Auto Select") },
2935   /* Translators: this is an option of "Paper Source" */
2936   { "InputSlot", "AutoSelect", N_("Auto Select") },
2937   /* Translators: this is an option of "Paper Source" */
2938   { "InputSlot", "Default", N_("Printer Default") },
2939   /* Translators: this is an option of "Paper Source" */
2940   { "InputSlot", "None", N_("Printer Default") },
2941   /* Translators: this is an option of "Paper Source" */
2942   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2943   /* Translators: this is an option of "Paper Source" */
2944   { "InputSlot", "Unspecified", N_("Auto Select") },
2945   /* Translators: this is an option of "Resolution" */
2946   { "Resolution", "default", N_("Printer Default") },
2947   /* Translators: this is an option of "GhostScript" */
2948   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
2949   /* Translators: this is an option of "GhostScript" */
2950   { "PreFilter", "Level1", N_("Convert to PS level 1") },
2951   /* Translators: this is an option of "GhostScript" */
2952   { "PreFilter", "Level2", N_("Convert to PS level 2") },
2953   /* Translators: this is an option of "GhostScript" */
2954   { "PreFilter", "No", N_("No pre-filtering") },
2955 };
2956
2957 static const struct {
2958   const char *name;
2959   const char *translation;
2960 } cups_group_translations[] = {
2961 /* Translators: "Miscellaneous" is the label for a button, that opens
2962    up an extra panel of settings in a print dialog. */
2963   { "Miscellaneous", N_("Miscellaneous") },
2964 };
2965
2966 static const struct {
2967   const char *ppd_keyword;
2968   const char *name;
2969 } ppd_option_names[] = {
2970   {"Duplex", "gtk-duplex" },
2971   {"MediaType", "gtk-paper-type"},
2972   {"InputSlot", "gtk-paper-source"},
2973   {"OutputBin", "gtk-output-tray"},
2974 };
2975
2976 static const struct {
2977   const char *lpoption;
2978   const char *name;
2979 } lpoption_names[] = {
2980   {"number-up", "gtk-n-up" },
2981   {"number-up-layout", "gtk-n-up-layout"},
2982   {"job-billing", "gtk-billing-info"},
2983   {"job-priority", "gtk-job-prio"},
2984 };
2985
2986 /* keep sorted when changing */
2987 static const char *color_option_whitelist[] = {
2988   "BRColorEnhancement",
2989   "BRColorMatching",
2990   "BRColorMatching",
2991   "BRColorMode",
2992   "BRGammaValue",
2993   "BRImprovedGray",
2994   "BlackSubstitution",
2995   "ColorModel",
2996   "HPCMYKInks",
2997   "HPCSGraphics",
2998   "HPCSImages",
2999   "HPCSText",
3000   "HPColorSmart",
3001   "RPSBlackMode",
3002   "RPSBlackOverPrint",
3003   "Rcmyksimulation",
3004 };
3005
3006 /* keep sorted when changing */
3007 static const char *color_group_whitelist[] = {
3008   "ColorPage",
3009   "FPColorWise1",
3010   "FPColorWise2",
3011   "FPColorWise3",
3012   "FPColorWise4",
3013   "FPColorWise5",
3014   "HPColorOptionsPanel",
3015 };
3016
3017 /* keep sorted when changing */
3018 static const char *image_quality_option_whitelist[] = {
3019   "BRDocument",
3020   "BRHalfTonePattern",
3021   "BRNormalPrt",
3022   "BRPrintQuality",
3023   "BitsPerPixel",
3024   "Darkness",
3025   "Dithering",
3026   "EconoMode",
3027   "Economode",
3028   "HPEconoMode",
3029   "HPEdgeControl",
3030   "HPGraphicsHalftone",
3031   "HPHalftone",
3032   "HPLJDensity",
3033   "HPPhotoHalftone",
3034   "OutputMode",
3035   "REt",
3036   "RPSBitsPerPixel",
3037   "RPSDitherType",
3038   "Resolution",
3039   "ScreenLock",
3040   "Smoothing",
3041   "TonerSaveMode",
3042   "UCRGCRForImage",
3043 };
3044
3045 /* keep sorted when changing */
3046 static const char *image_quality_group_whitelist[] = {
3047   "FPImageQuality1",
3048   "FPImageQuality2",
3049   "FPImageQuality3",
3050   "ImageQualityPage",
3051 };
3052
3053 /* keep sorted when changing */
3054 static const char * finishing_option_whitelist[] = {
3055   "BindColor",
3056   "BindEdge",
3057   "BindType",
3058   "BindWhen",
3059   "Booklet",
3060   "FoldType",
3061   "FoldWhen",
3062   "HPStaplerOptions",
3063   "Jog",
3064   "Slipsheet",
3065   "Sorter",
3066   "StapleLocation",
3067   "StapleOrientation",
3068   "StapleWhen",
3069   "StapleX",
3070   "StapleY",
3071 };
3072
3073 /* keep sorted when changing */
3074 static const char *finishing_group_whitelist[] = {
3075   "FPFinishing1",
3076   "FPFinishing2",
3077   "FPFinishing3",
3078   "FPFinishing4",
3079   "FinishingPage",
3080   "HPFinishingPanel",
3081 };
3082
3083 /* keep sorted when changing */
3084 static const char *cups_option_blacklist[] = {
3085   "Collate",
3086   "Copies",
3087   "OutputOrder",
3088   "PageRegion",
3089   "PageSize",
3090 };
3091
3092 static char *
3093 get_option_text (ppd_file_t   *ppd_file,
3094                  ppd_option_t *option)
3095 {
3096   int i;
3097   char *utf8;
3098
3099   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
3100     {
3101       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
3102         return g_strdup (_(cups_option_translations[i].translation));
3103     }
3104
3105   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
3106
3107   /* Some ppd files have spaces in the text before the colon */
3108   g_strchomp (utf8);
3109
3110   return utf8;
3111 }
3112
3113 static char *
3114 get_choice_text (ppd_file_t   *ppd_file,
3115                  ppd_choice_t *choice)
3116 {
3117   int i;
3118   ppd_option_t *option = choice->option;
3119   const char *keyword = option->keyword;
3120
3121   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
3122     {
3123       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
3124           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
3125         return g_strdup (_(cups_choice_translations[i].translation));
3126     }
3127   return ppd_text_to_utf8 (ppd_file, choice->text);
3128 }
3129
3130 static gboolean
3131 group_has_option (ppd_group_t  *group,
3132                   ppd_option_t *option)
3133 {
3134   int i;
3135
3136   if (group == NULL)
3137     return FALSE;
3138
3139   if (group->num_options > 0 &&
3140       option >= group->options && option < group->options + group->num_options)
3141     return TRUE;
3142
3143   for (i = 0; i < group->num_subgroups; i++)
3144     {
3145       if (group_has_option (&group->subgroups[i],option))
3146         return TRUE;
3147     }
3148   return FALSE;
3149 }
3150
3151 static void
3152 set_option_off (GtkPrinterOption *option)
3153 {
3154   /* Any of these will do, _set only applies the value
3155    * if its allowed of the option */
3156   gtk_printer_option_set (option, "False");
3157   gtk_printer_option_set (option, "Off");
3158   gtk_printer_option_set (option, "None");
3159 }
3160
3161 static gboolean
3162 value_is_off (const char *value)
3163 {
3164   return  (strcasecmp (value, "None") == 0 ||
3165            strcasecmp (value, "Off") == 0 ||
3166            strcasecmp (value, "False") == 0);
3167 }
3168
3169 static char *
3170 ppd_group_name (ppd_group_t *group)
3171 {
3172   return group->name;
3173 }
3174
3175 static int
3176 available_choices (ppd_file_t     *ppd,
3177                    ppd_option_t   *option,
3178                    ppd_choice_t ***available,
3179                    gboolean        keep_if_only_one_option)
3180 {
3181   ppd_option_t *other_option;
3182   int i, j;
3183   gchar *conflicts;
3184   ppd_const_t *constraint;
3185   const char *choice, *other_choice;
3186   ppd_option_t *option1, *option2;
3187   ppd_group_t *installed_options;
3188   int num_conflicts;
3189   gboolean all_default;
3190   int add_auto;
3191
3192   if (available)
3193     *available = NULL;
3194
3195   conflicts = g_new0 (char, option->num_choices);
3196
3197   installed_options = NULL;
3198   for (i = 0; i < ppd->num_groups; i++)
3199     {
3200       char *name;
3201
3202       name = ppd_group_name (&ppd->groups[i]);
3203       if (strcmp (name, "InstallableOptions") == 0)
3204         {
3205           installed_options = &ppd->groups[i];
3206           break;
3207         }
3208     }
3209
3210   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
3211     {
3212       option1 = ppdFindOption (ppd, constraint->option1);
3213       if (option1 == NULL)
3214         continue;
3215
3216       option2 = ppdFindOption (ppd, constraint->option2);
3217       if (option2 == NULL)
3218         continue;
3219
3220       if (option == option1)
3221         {
3222           choice = constraint->choice1;
3223           other_option = option2;
3224           other_choice = constraint->choice2;
3225         }
3226       else if (option == option2)
3227         {
3228           choice = constraint->choice2;
3229           other_option = option1;
3230           other_choice = constraint->choice1;
3231         }
3232       else
3233         continue;
3234
3235       /* We only care of conflicts with installed_options and
3236          PageSize */
3237       if (!group_has_option (installed_options, other_option) &&
3238           (strcmp (other_option->keyword, "PageSize") != 0))
3239         continue;
3240
3241       if (*other_choice == 0)
3242         {
3243           /* Conflict only if the installed option is not off */
3244           if (value_is_off (other_option->defchoice))
3245             continue;
3246         }
3247       /* Conflict if the installed option has the specified default */
3248       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
3249         continue;
3250
3251       if (*choice == 0)
3252         {
3253           /* Conflict with all non-off choices */
3254           for (j = 0; j < option->num_choices; j++)
3255             {
3256               if (!value_is_off (option->choices[j].choice))
3257                 conflicts[j] = 1;
3258             }
3259         }
3260       else
3261         {
3262           for (j = 0; j < option->num_choices; j++)
3263             {
3264               if (strcasecmp (option->choices[j].choice, choice) == 0)
3265                 conflicts[j] = 1;
3266             }
3267         }
3268     }
3269
3270   num_conflicts = 0;
3271   all_default = TRUE;
3272   for (j = 0; j < option->num_choices; j++)
3273     {
3274       if (conflicts[j])
3275         num_conflicts++;
3276       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
3277         all_default = FALSE;
3278     }
3279
3280   if ((all_default && !keep_if_only_one_option) ||
3281       (num_conflicts == option->num_choices))
3282     {
3283       g_free (conflicts);
3284
3285       return 0;
3286     }
3287
3288   /* Some ppds don't have a "use printer default" option for
3289    * InputSlot. This means you always have to select a particular slot,
3290    * and you can't auto-pick source based on the paper size. To support
3291    * this we always add an auto option if there isn't one already. If
3292    * the user chooses the generated option we don't send any InputSlot
3293    * value when printing. The way we detect existing auto-cases is based
3294    * on feedback from Michael Sweet of cups fame.
3295    */
3296   add_auto = 0;
3297   if (strcmp (option->keyword, "InputSlot") == 0)
3298     {
3299       gboolean found_auto = FALSE;
3300       for (j = 0; j < option->num_choices; j++)
3301         {
3302           if (!conflicts[j])
3303             {
3304               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
3305                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
3306                   strcmp (option->choices[j].choice, "Default") == 0 ||
3307                   strcmp (option->choices[j].choice, "None") == 0 ||
3308                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
3309                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
3310                   option->choices[j].code == NULL ||
3311                   option->choices[j].code[0] == 0)
3312                 {
3313                   found_auto = TRUE;
3314                   break;
3315                 }
3316             }
3317         }
3318
3319       if (!found_auto)
3320         add_auto = 1;
3321     }
3322
3323   if (available)
3324     {
3325       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
3326
3327       i = 0;
3328       for (j = 0; j < option->num_choices; j++)
3329         {
3330           if (!conflicts[j])
3331             (*available)[i++] = &option->choices[j];
3332         }
3333
3334       if (add_auto)
3335         (*available)[i++] = NULL;
3336     }
3337
3338   g_free (conflicts);
3339
3340   return option->num_choices - num_conflicts + add_auto;
3341 }
3342
3343 static GtkPrinterOption *
3344 create_pickone_option (ppd_file_t   *ppd_file,
3345                        ppd_option_t *ppd_option,
3346                        const gchar  *gtk_name)
3347 {
3348   GtkPrinterOption *option;
3349   ppd_choice_t **available;
3350   char *label;
3351   int n_choices;
3352   int i;
3353   ppd_coption_t *coption;
3354
3355   g_assert (ppd_option->ui == PPD_UI_PICKONE);
3356
3357   option = NULL;
3358
3359   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3360   if (n_choices > 0)
3361     {
3362
3363       /* right now only support one parameter per custom option
3364        * if more than one print warning and only offer the default choices
3365        */
3366
3367       label = get_option_text (ppd_file, ppd_option);
3368
3369       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
3370
3371       if (coption)
3372         {
3373           ppd_cparam_t *cparam;
3374
3375           cparam = ppdFirstCustomParam (coption);
3376
3377           if (ppdNextCustomParam (coption) == NULL)
3378             {
3379               switch (cparam->type)
3380                 {
3381                 case PPD_CUSTOM_INT:
3382                   option = gtk_printer_option_new (gtk_name, label,
3383                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
3384                   break;
3385                 case PPD_CUSTOM_PASSCODE:
3386                   option = gtk_printer_option_new (gtk_name, label,
3387                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
3388                   break;
3389                 case PPD_CUSTOM_PASSWORD:
3390                     option = gtk_printer_option_new (gtk_name, label,
3391                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
3392                   break;
3393                case PPD_CUSTOM_REAL:
3394                     option = gtk_printer_option_new (gtk_name, label,
3395                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
3396                   break;
3397                 case PPD_CUSTOM_STRING:
3398                   option = gtk_printer_option_new (gtk_name, label,
3399                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
3400                   break;
3401 #ifdef PRINT_IGNORED_OPTIONS
3402                 case PPD_CUSTOM_POINTS:
3403                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
3404                   break;
3405                 case PPD_CUSTOM_CURVE:
3406                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
3407                   break;
3408                 case PPD_CUSTOM_INVCURVE:
3409                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
3410                   break;
3411 #endif
3412                 default:
3413                   break;
3414                 }
3415             }
3416 #ifdef PRINT_IGNORED_OPTIONS
3417           else
3418             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
3419 #endif
3420         }
3421
3422       if (!option)
3423         option = gtk_printer_option_new (gtk_name, label,
3424                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
3425       g_free (label);
3426
3427       gtk_printer_option_allocate_choices (option, n_choices);
3428       for (i = 0; i < n_choices; i++)
3429         {
3430           if (available[i] == NULL)
3431             {
3432               /* This was auto-added */
3433               option->choices[i] = g_strdup ("gtk-ignore-value");
3434               option->choices_display[i] = g_strdup (_("Printer Default"));
3435             }
3436           else
3437             {
3438               option->choices[i] = g_strdup (available[i]->choice);
3439               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
3440             }
3441         }
3442
3443       if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
3444         {
3445           if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
3446             gtk_printer_option_set (option, ppd_option->defchoice + 7);
3447           else
3448             gtk_printer_option_set (option, ppd_option->defchoice);
3449         }
3450       else
3451         {
3452           gtk_printer_option_set (option, ppd_option->defchoice);
3453         }
3454     }
3455 #ifdef PRINT_IGNORED_OPTIONS
3456   else
3457     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
3458 #endif
3459   g_free (available);
3460
3461   return option;
3462 }
3463
3464 static GtkPrinterOption *
3465 create_boolean_option (ppd_file_t   *ppd_file,
3466                        ppd_option_t *ppd_option,
3467                        const gchar  *gtk_name)
3468 {
3469   GtkPrinterOption *option;
3470   ppd_choice_t **available;
3471   char *label;
3472   int n_choices;
3473
3474   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
3475
3476   option = NULL;
3477
3478   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3479   if (n_choices == 2)
3480     {
3481       label = get_option_text (ppd_file, ppd_option);
3482       option = gtk_printer_option_new (gtk_name, label,
3483                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
3484       g_free (label);
3485
3486       gtk_printer_option_allocate_choices (option, 2);
3487       option->choices[0] = g_strdup ("True");
3488       option->choices_display[0] = g_strdup ("True");
3489       option->choices[1] = g_strdup ("False");
3490       option->choices_display[1] = g_strdup ("False");
3491
3492       gtk_printer_option_set (option, ppd_option->defchoice);
3493     }
3494 #ifdef PRINT_IGNORED_OPTIONS
3495   else
3496     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
3497 #endif
3498   g_free (available);
3499
3500   return option;
3501 }
3502
3503 static gchar *
3504 get_ppd_option_name (const gchar *keyword)
3505 {
3506   int i;
3507
3508   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3509     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
3510       return g_strdup (ppd_option_names[i].name);
3511
3512   return g_strdup_printf ("cups-%s", keyword);
3513 }
3514
3515 static gchar *
3516 get_lpoption_name (const gchar *lpoption)
3517 {
3518   int i;
3519
3520   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3521     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
3522       return g_strdup (ppd_option_names[i].name);
3523
3524   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
3525     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
3526       return g_strdup (lpoption_names[i].name);
3527
3528   return g_strdup_printf ("cups-%s", lpoption);
3529 }
3530
3531 static int
3532 strptr_cmp (const void *a,
3533             const void *b)
3534 {
3535   char **aa = (char **)a;
3536   char **bb = (char **)b;
3537   return strcmp (*aa, *bb);
3538 }
3539
3540
3541 static gboolean
3542 string_in_table (gchar       *str,
3543                  const gchar *table[],
3544                  gint         table_len)
3545 {
3546   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
3547 }
3548
3549 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
3550
3551 static void
3552 handle_option (GtkPrinterOptionSet *set,
3553                ppd_file_t          *ppd_file,
3554                ppd_option_t        *ppd_option,
3555                ppd_group_t         *toplevel_group,
3556                GtkPrintSettings    *settings)
3557 {
3558   GtkPrinterOption *option;
3559   char *name;
3560   int i;
3561
3562   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
3563     return;
3564
3565   name = get_ppd_option_name (ppd_option->keyword);
3566
3567   option = NULL;
3568   if (ppd_option->ui == PPD_UI_PICKONE)
3569     {
3570       option = create_pickone_option (ppd_file, ppd_option, name);
3571     }
3572   else if (ppd_option->ui == PPD_UI_BOOLEAN)
3573     {
3574       option = create_boolean_option (ppd_file, ppd_option, name);
3575     }
3576 #ifdef PRINT_IGNORED_OPTIONS
3577   else
3578     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
3579 #endif
3580
3581   if (option)
3582     {
3583       char *name;
3584
3585       name = ppd_group_name (toplevel_group);
3586       if (STRING_IN_TABLE (name,
3587                            color_group_whitelist) ||
3588           STRING_IN_TABLE (ppd_option->keyword,
3589                            color_option_whitelist))
3590         {
3591           option->group = g_strdup ("ColorPage");
3592         }
3593       else if (STRING_IN_TABLE (name,
3594                                 image_quality_group_whitelist) ||
3595                STRING_IN_TABLE (ppd_option->keyword,
3596                                 image_quality_option_whitelist))
3597         {
3598           option->group = g_strdup ("ImageQualityPage");
3599         }
3600       else if (STRING_IN_TABLE (name,
3601                                 finishing_group_whitelist) ||
3602                STRING_IN_TABLE (ppd_option->keyword,
3603                                 finishing_option_whitelist))
3604         {
3605           option->group = g_strdup ("FinishingPage");
3606         }
3607       else
3608         {
3609           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
3610             {
3611               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
3612                 {
3613                   option->group = g_strdup (_(cups_group_translations[i].translation));
3614                   break;
3615                 }
3616             }
3617
3618           if (i == G_N_ELEMENTS (cups_group_translations))
3619             option->group = g_strdup (toplevel_group->text);
3620         }
3621
3622       set_option_from_settings (option, settings);
3623
3624       gtk_printer_option_set_add (set, option);
3625     }
3626
3627   g_free (name);
3628 }
3629
3630 static void
3631 handle_group (GtkPrinterOptionSet *set,
3632               ppd_file_t          *ppd_file,
3633               ppd_group_t         *group,
3634               ppd_group_t         *toplevel_group,
3635               GtkPrintSettings    *settings)
3636 {
3637   gint i;
3638   gchar *name;
3639
3640   /* Ignore installable options */
3641   name = ppd_group_name (toplevel_group);
3642   if (strcmp (name, "InstallableOptions") == 0)
3643     return;
3644
3645   for (i = 0; i < group->num_options; i++)
3646     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
3647
3648   for (i = 0; i < group->num_subgroups; i++)
3649     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
3650
3651 }
3652
3653 #ifdef HAVE_COLORD
3654
3655 typedef struct {
3656         GtkPrintSettings     *settings;
3657         GtkPrinter           *printer;
3658 } GtkPrintBackendCupsColordHelper;
3659
3660 static void
3661 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
3662                                       GtkPrintBackendCupsColordHelper *helper)
3663 {
3664   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
3665                                     helper->settings,
3666                                     set);
3667 }
3668 #endif
3669
3670 static GtkPrinterOptionSet *
3671 cups_printer_get_options (GtkPrinter           *printer,
3672                           GtkPrintSettings     *settings,
3673                           GtkPageSetup         *page_setup,
3674                           GtkPrintCapabilities  capabilities)
3675 {
3676   GtkPrinterOptionSet *set;
3677   GtkPrinterOption *option;
3678   ppd_file_t *ppd_file;
3679   int i;
3680   char *print_at[] = { "now", "at", "on-hold" };
3681   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
3682   char *prio[] = {"100", "80", "50", "30" };
3683   /* Translators: These strings name the possible values of the
3684    * job priority option in the print dialog
3685    */
3686   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
3687   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
3688   /* Translators: These strings name the possible arrangements of
3689    * multiple pages on a sheet when printing
3690    */
3691   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
3692                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
3693                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
3694                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
3695   char *name;
3696   int num_opts;
3697   cups_option_t *opts = NULL;
3698   GtkPrintBackendCups *backend;
3699   GtkTextDirection text_direction;
3700   GtkPrinterCups *cups_printer = NULL;
3701 #ifdef HAVE_COLORD
3702   GtkPrintBackendCupsColordHelper *helper;
3703 #endif
3704   char *default_number_up;
3705
3706   set = gtk_printer_option_set_new ();
3707
3708   /* Cups specific, non-ppd related settings */
3709
3710   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
3711     prio_display[i] = _(prio_display[i]);
3712
3713   /* Translators, this string is used to label the job priority option
3714    * in the print dialog
3715    */
3716   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3717   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
3718                                          prio, prio_display);
3719   gtk_printer_option_set (option, "50");
3720   set_option_from_settings (option, settings);
3721   gtk_printer_option_set_add (set, option);
3722   g_object_unref (option);
3723
3724   /* Translators, this string is used to label the billing info entry
3725    * in the print dialog
3726    */
3727   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
3728   gtk_printer_option_set (option, "");
3729   set_option_from_settings (option, settings);
3730   gtk_printer_option_set_add (set, option);
3731   g_object_unref (option);
3732
3733   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
3734   cups_printer = GTK_PRINTER_CUPS (printer);
3735
3736   if (backend != NULL && printer != NULL)
3737     {
3738       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
3739       /* Translators, these strings are names for various 'standard' cover
3740        * pages that the printing system may support.
3741        */
3742       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
3743       char **cover = NULL;
3744       char **cover_display = NULL;
3745       char **cover_display_translated = NULL;
3746       gint num_of_covers = 0;
3747       gpointer value;
3748       gint j;
3749
3750        /* Translators, this string is used to label the pages-per-sheet option
3751         * in the print dialog
3752         */
3753       option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3754       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
3755                                              n_up, n_up);
3756       default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
3757       gtk_printer_option_set (option, default_number_up);
3758       g_free (default_number_up);
3759       set_option_from_settings (option, settings);
3760       gtk_printer_option_set_add (set, option);
3761       g_object_unref (option);
3762
3763       if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
3764         {
3765           for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
3766             n_up_layout_display[i] = _(n_up_layout_display[i]);
3767
3768            /* Translators, this string is used to label the option in the print
3769             * dialog that controls in what order multiple pages are arranged
3770             */
3771           option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3772           gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
3773                                                  n_up_layout, n_up_layout_display);
3774
3775           text_direction = gtk_widget_get_default_direction ();
3776           if (text_direction == GTK_TEXT_DIR_LTR)
3777             gtk_printer_option_set (option, "lrtb");
3778           else
3779             gtk_printer_option_set (option, "rltb");
3780
3781           set_option_from_settings (option, settings);
3782           gtk_printer_option_set_add (set, option);
3783           g_object_unref (option);
3784         }
3785
3786       num_of_covers = backend->number_of_covers;
3787       cover = g_new (char *, num_of_covers + 1);
3788       cover[num_of_covers] = NULL;
3789       cover_display = g_new (char *, num_of_covers + 1);
3790       cover_display[num_of_covers] = NULL;
3791       cover_display_translated = g_new (char *, num_of_covers + 1);
3792       cover_display_translated[num_of_covers] = NULL;
3793
3794       for (i = 0; i < num_of_covers; i++)
3795         {
3796           cover[i] = g_strdup (backend->covers[i]);
3797           value = NULL;
3798           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
3799             if (strcmp (cover_default[j], cover[i]) == 0)
3800               {
3801                 value = cover_display_default[j];
3802                 break;
3803               }
3804           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
3805         }
3806
3807       for (i = 0; i < num_of_covers; i++)
3808         cover_display_translated[i] = _(cover_display[i]);
3809
3810       /* Translators, this is the label used for the option in the print
3811        * dialog that controls the front cover page.
3812        */
3813       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3814       gtk_printer_option_choices_from_array (option, num_of_covers,
3815                                          cover, cover_display_translated);
3816
3817       if (cups_printer->default_cover_before != NULL)
3818         gtk_printer_option_set (option, cups_printer->default_cover_before);
3819       else
3820         gtk_printer_option_set (option, "none");
3821       set_option_from_settings (option, settings);
3822       gtk_printer_option_set_add (set, option);
3823       g_object_unref (option);
3824
3825       /* Translators, this is the label used for the option in the print
3826        * dialog that controls the back cover page.
3827        */
3828       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3829       gtk_printer_option_choices_from_array (option, num_of_covers,
3830                                          cover, cover_display_translated);
3831       if (cups_printer->default_cover_after != NULL)
3832         gtk_printer_option_set (option, cups_printer->default_cover_after);
3833       else
3834         gtk_printer_option_set (option, "none");
3835       set_option_from_settings (option, settings);
3836       gtk_printer_option_set_add (set, option);
3837       g_object_unref (option);
3838
3839       g_strfreev (cover);
3840       g_strfreev (cover_display);
3841       g_free (cover_display_translated);
3842     }
3843
3844   /* Translators: this is the name of the option that controls when
3845    * a print job is printed. Possible values are 'now', a specified time,
3846    * or 'on hold'
3847    */
3848   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3849   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
3850                                          print_at, print_at);
3851   gtk_printer_option_set (option, "now");
3852   set_option_from_settings (option, settings);
3853   gtk_printer_option_set_add (set, option);
3854   g_object_unref (option);
3855
3856   /* Translators: this is the name of the option that allows the user
3857    * to specify a time when a print job will be printed.
3858    */
3859   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
3860   gtk_printer_option_set (option, "");
3861   set_option_from_settings (option, settings);
3862   gtk_printer_option_set_add (set, option);
3863   g_object_unref (option);
3864
3865   /* Printer (ppd) specific settings */
3866   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3867   if (ppd_file)
3868     {
3869       GtkPaperSize *paper_size;
3870       ppd_option_t *option;
3871       const gchar  *ppd_name;
3872
3873       ppdMarkDefaults (ppd_file);
3874
3875       paper_size = gtk_page_setup_get_paper_size (page_setup);
3876
3877       option = ppdFindOption (ppd_file, "PageSize");
3878       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
3879
3880       if (ppd_name)
3881         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
3882       else
3883         {
3884           gchar *custom_name;
3885           char width[G_ASCII_DTOSTR_BUF_SIZE];
3886           char height[G_ASCII_DTOSTR_BUF_SIZE];
3887
3888           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3889           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3890           /* Translators: this format is used to display a custom paper
3891            * size. The two placeholders are replaced with the width and height
3892            * in points. E.g: "Custom 230.4x142.9"
3893            */
3894           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
3895           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
3896           g_free (custom_name);
3897         }
3898
3899       for (i = 0; i < ppd_file->num_groups; i++)
3900         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
3901     }
3902
3903   /* Now honor the user set defaults for this printer */
3904   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
3905
3906   for (i = 0; i < num_opts; i++)
3907     {
3908       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
3909         continue;
3910
3911       name = get_lpoption_name (opts[i].name);
3912       if (strcmp (name, "cups-job-sheets") == 0)
3913         {
3914           gchar **values;
3915           gint    num_values;
3916
3917           values = g_strsplit (opts[i].value, ",", 2);
3918           num_values = g_strv_length (values);
3919
3920           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
3921           if (option && num_values > 0)
3922             gtk_printer_option_set (option, g_strstrip (values[0]));
3923
3924           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
3925           if (option && num_values > 1)
3926             gtk_printer_option_set (option, g_strstrip (values[1]));
3927
3928           g_strfreev (values);
3929         }
3930       else if (strcmp (name, "cups-job-hold-until") == 0)
3931         {
3932           GtkPrinterOption *option2 = NULL;
3933
3934           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
3935           if (option && opts[i].value)
3936             {
3937               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
3938               if (option2)
3939                 {
3940                   if (strcmp (opts[i].value, "indefinite") == 0)
3941                     gtk_printer_option_set (option2, "on-hold");
3942                   else
3943                     {
3944                       gtk_printer_option_set (option2, "at");
3945                       gtk_printer_option_set (option, opts[i].value);
3946                     }
3947                 }
3948             }
3949         }
3950       else if (strcmp (name, "cups-sides") == 0)
3951         {
3952           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
3953           if (option && opts[i].value)
3954             {
3955               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
3956                 gtk_printer_option_set (option, "DuplexTumble");
3957               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
3958                 gtk_printer_option_set (option, "DuplexNoTumble");
3959             }
3960         }
3961       else
3962         {
3963           option = gtk_printer_option_set_lookup (set, name);
3964           if (option)
3965             gtk_printer_option_set (option, opts[i].value);
3966         }
3967       g_free (name);
3968     }
3969
3970   cupsFreeOptions (num_opts, opts);
3971
3972 #ifdef HAVE_COLORD
3973   /* TRANSLATORS: this this the ICC color profile to use for this job */
3974   option = gtk_printer_option_new ("colord-profile",
3975                                    _("Printer Profile"),
3976                                    GTK_PRINTER_OPTION_TYPE_INFO);
3977
3978   /* assign it to the color page */
3979   option->group = g_strdup ("ColorPage");
3980
3981   /* TRANSLATORS: this is when color profile information is unavailable */
3982   gtk_printer_option_set (option, _("Unavailable"));
3983   gtk_printer_option_set_add (set, option);
3984
3985   /* watch to see if the user changed the options */
3986   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
3987   helper->printer = printer;
3988   helper->settings = settings;
3989   g_signal_connect_data (set, "changed",
3990                          G_CALLBACK (colord_printer_option_set_changed_cb),
3991                          helper,
3992                          (GClosureNotify) g_free,
3993                          0);
3994
3995   /* initial coldplug */
3996   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
3997                                     settings, set);
3998   g_object_bind_property (printer, "profile-title",
3999                           option, "value",
4000                           G_BINDING_DEFAULT);
4001
4002 #endif
4003
4004   return set;
4005 }
4006
4007
4008 static void
4009 mark_option_from_set (GtkPrinterOptionSet *set,
4010                       ppd_file_t          *ppd_file,
4011                       ppd_option_t        *ppd_option)
4012 {
4013   GtkPrinterOption *option;
4014   char *name = get_ppd_option_name (ppd_option->keyword);
4015
4016   option = gtk_printer_option_set_lookup (set, name);
4017
4018   if (option)
4019     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
4020
4021   g_free (name);
4022 }
4023
4024
4025 static void
4026 mark_group_from_set (GtkPrinterOptionSet *set,
4027                      ppd_file_t          *ppd_file,
4028                      ppd_group_t         *group)
4029 {
4030   int i;
4031
4032   for (i = 0; i < group->num_options; i++)
4033     mark_option_from_set (set, ppd_file, &group->options[i]);
4034
4035   for (i = 0; i < group->num_subgroups; i++)
4036     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
4037 }
4038
4039 static void
4040 set_conflicts_from_option (GtkPrinterOptionSet *set,
4041                            ppd_file_t          *ppd_file,
4042                            ppd_option_t        *ppd_option)
4043 {
4044   GtkPrinterOption *option;
4045   char *name;
4046
4047   if (ppd_option->conflicted)
4048     {
4049       name = get_ppd_option_name (ppd_option->keyword);
4050       option = gtk_printer_option_set_lookup (set, name);
4051
4052       if (option)
4053         gtk_printer_option_set_has_conflict (option, TRUE);
4054 #ifdef PRINT_IGNORED_OPTIONS
4055       else
4056         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
4057 #endif
4058
4059       g_free (name);
4060     }
4061 }
4062
4063 static void
4064 set_conflicts_from_group (GtkPrinterOptionSet *set,
4065                           ppd_file_t          *ppd_file,
4066                           ppd_group_t         *group)
4067 {
4068   int i;
4069
4070   for (i = 0; i < group->num_options; i++)
4071     set_conflicts_from_option (set, ppd_file, &group->options[i]);
4072
4073   for (i = 0; i < group->num_subgroups; i++)
4074     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
4075 }
4076
4077 static gboolean
4078 cups_printer_mark_conflicts (GtkPrinter          *printer,
4079                              GtkPrinterOptionSet *options)
4080 {
4081   ppd_file_t *ppd_file;
4082   int num_conflicts;
4083   int i;
4084
4085   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4086
4087   if (ppd_file == NULL)
4088     return FALSE;
4089
4090   ppdMarkDefaults (ppd_file);
4091
4092   for (i = 0; i < ppd_file->num_groups; i++)
4093     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
4094
4095   num_conflicts = ppdConflicts (ppd_file);
4096
4097   if (num_conflicts > 0)
4098     {
4099       for (i = 0; i < ppd_file->num_groups; i++)
4100         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
4101     }
4102
4103   return num_conflicts > 0;
4104 }
4105
4106 struct OptionData {
4107   GtkPrinter *printer;
4108   GtkPrinterOptionSet *options;
4109   GtkPrintSettings *settings;
4110   ppd_file_t *ppd_file;
4111 };
4112
4113 typedef struct {
4114   const char *cups;
4115   const char *standard;
4116 } NameMapping;
4117
4118 static void
4119 map_settings_to_option (GtkPrinterOption  *option,
4120                         const NameMapping  table[],
4121                         gint               n_elements,
4122                         GtkPrintSettings  *settings,
4123                         const gchar       *standard_name,
4124                         const gchar       *cups_name)
4125 {
4126   int i;
4127   char *name;
4128   const char *cups_value;
4129   const char *standard_value;
4130
4131   /* If the cups-specific setting is set, always use that */
4132   name = g_strdup_printf ("cups-%s", cups_name);
4133   cups_value = gtk_print_settings_get (settings, name);
4134   g_free (name);
4135
4136   if (cups_value != NULL)
4137     {
4138       gtk_printer_option_set (option, cups_value);
4139       return;
4140     }
4141
4142   /* Otherwise we try to convert from the general setting */
4143   standard_value = gtk_print_settings_get (settings, standard_name);
4144   if (standard_value == NULL)
4145     return;
4146
4147   for (i = 0; i < n_elements; i++)
4148     {
4149       if (table[i].cups == NULL && table[i].standard == NULL)
4150         {
4151           gtk_printer_option_set (option, standard_value);
4152           break;
4153         }
4154       else if (table[i].cups == NULL &&
4155                strcmp (table[i].standard, standard_value) == 0)
4156         {
4157           set_option_off (option);
4158           break;
4159         }
4160       else if (strcmp (table[i].standard, standard_value) == 0)
4161         {
4162           gtk_printer_option_set (option, table[i].cups);
4163           break;
4164         }
4165     }
4166 }
4167
4168 static void
4169 map_option_to_settings (const gchar       *value,
4170                         const NameMapping  table[],
4171                         gint               n_elements,
4172                         GtkPrintSettings  *settings,
4173                         const gchar       *standard_name,
4174                         const gchar       *cups_name)
4175 {
4176   int i;
4177   char *name;
4178
4179   for (i = 0; i < n_elements; i++)
4180     {
4181       if (table[i].cups == NULL && table[i].standard == NULL)
4182         {
4183           gtk_print_settings_set (settings,
4184                                   standard_name,
4185                                   value);
4186           break;
4187         }
4188       else if (table[i].cups == NULL && table[i].standard != NULL)
4189         {
4190           if (value_is_off (value))
4191             {
4192               gtk_print_settings_set (settings,
4193                                       standard_name,
4194                                       table[i].standard);
4195               break;
4196             }
4197         }
4198       else if (strcmp (table[i].cups, value) == 0)
4199         {
4200           gtk_print_settings_set (settings,
4201                                   standard_name,
4202                                   table[i].standard);
4203           break;
4204         }
4205     }
4206
4207   /* Always set the corresponding cups-specific setting */
4208   name = g_strdup_printf ("cups-%s", cups_name);
4209   gtk_print_settings_set (settings, name, value);
4210   g_free (name);
4211 }
4212
4213
4214 static const NameMapping paper_source_map[] = {
4215   { "Lower", "lower"},
4216   { "Middle", "middle"},
4217   { "Upper", "upper"},
4218   { "Rear", "rear"},
4219   { "Envelope", "envelope"},
4220   { "Cassette", "cassette"},
4221   { "LargeCapacity", "large-capacity"},
4222   { "AnySmallFormat", "small-format"},
4223   { "AnyLargeFormat", "large-format"},
4224   { NULL, NULL}
4225 };
4226
4227 static const NameMapping output_tray_map[] = {
4228   { "Upper", "upper"},
4229   { "Lower", "lower"},
4230   { "Rear", "rear"},
4231   { NULL, NULL}
4232 };
4233
4234 static const NameMapping duplex_map[] = {
4235   { "DuplexTumble", "vertical" },
4236   { "DuplexNoTumble", "horizontal" },
4237   { NULL, "simplex" }
4238 };
4239
4240 static const NameMapping output_mode_map[] = {
4241   { "Standard", "normal" },
4242   { "Normal", "normal" },
4243   { "Draft", "draft" },
4244   { "Fast", "draft" },
4245 };
4246
4247 static const NameMapping media_type_map[] = {
4248   { "Transparency", "transparency"},
4249   { "Standard", "stationery"},
4250   { NULL, NULL}
4251 };
4252
4253 static const NameMapping all_map[] = {
4254   { NULL, NULL}
4255 };
4256
4257
4258 static void
4259 set_option_from_settings (GtkPrinterOption *option,
4260                           GtkPrintSettings *settings)
4261 {
4262   const char *cups_value;
4263   char *value;
4264
4265   if (settings == NULL)
4266     return;
4267
4268   if (strcmp (option->name, "gtk-paper-source") == 0)
4269     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
4270                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4271   else if (strcmp (option->name, "gtk-output-tray") == 0)
4272     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
4273                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4274   else if (strcmp (option->name, "gtk-duplex") == 0)
4275     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
4276                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4277   else if (strcmp (option->name, "cups-OutputMode") == 0)
4278     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
4279                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4280   else if (strcmp (option->name, "cups-Resolution") == 0)
4281     {
4282       cups_value = gtk_print_settings_get (settings, option->name);
4283       if (cups_value)
4284         gtk_printer_option_set (option, cups_value);
4285       else
4286         {
4287           if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
4288               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
4289               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
4290               option->value == NULL || option->value[0] == '\0')
4291             {
4292               int res = gtk_print_settings_get_resolution (settings);
4293               int res_x = gtk_print_settings_get_resolution_x (settings);
4294               int res_y = gtk_print_settings_get_resolution_y (settings);
4295
4296               if (res_x != res_y)
4297                 {
4298                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
4299                   gtk_printer_option_set (option, value);
4300                   g_free (value);
4301                 }
4302               else if (res != 0)
4303                 {
4304                   value = g_strdup_printf ("%ddpi", res);
4305                   gtk_printer_option_set (option, value);
4306                   g_free (value);
4307                 }
4308             }
4309         }
4310     }
4311   else if (strcmp (option->name, "gtk-paper-type") == 0)
4312     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
4313                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4314   else if (strcmp (option->name, "gtk-n-up") == 0)
4315     {
4316       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4317                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4318     }
4319   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4320     {
4321       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4322                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4323     }
4324   else if (strcmp (option->name, "gtk-billing-info") == 0)
4325     {
4326       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
4327       if (cups_value)
4328         gtk_printer_option_set (option, cups_value);
4329     }
4330   else if (strcmp (option->name, "gtk-job-prio") == 0)
4331     {
4332       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
4333       if (cups_value)
4334         gtk_printer_option_set (option, cups_value);
4335     }
4336   else if (strcmp (option->name, "gtk-cover-before") == 0)
4337     {
4338       cups_value = gtk_print_settings_get (settings, "cover-before");
4339       if (cups_value)
4340         gtk_printer_option_set (option, cups_value);
4341     }
4342   else if (strcmp (option->name, "gtk-cover-after") == 0)
4343     {
4344       cups_value = gtk_print_settings_get (settings, "cover-after");
4345       if (cups_value)
4346         gtk_printer_option_set (option, cups_value);
4347     }
4348   else if (strcmp (option->name, "gtk-print-time") == 0)
4349     {
4350       cups_value = gtk_print_settings_get (settings, "print-at");
4351       if (cups_value)
4352         gtk_printer_option_set (option, cups_value);
4353     }
4354   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4355     {
4356       cups_value = gtk_print_settings_get (settings, "print-at-time");
4357       if (cups_value)
4358         gtk_printer_option_set (option, cups_value);
4359     }
4360   else if (g_str_has_prefix (option->name, "cups-"))
4361     {
4362       cups_value = gtk_print_settings_get (settings, option->name);
4363       if (cups_value)
4364         gtk_printer_option_set (option, cups_value);
4365     }
4366 }
4367
4368 static void
4369 foreach_option_get_settings (GtkPrinterOption *option,
4370                              gpointer          user_data)
4371 {
4372   struct OptionData *data = user_data;
4373   GtkPrintSettings *settings = data->settings;
4374   const char *value;
4375
4376   value = option->value;
4377
4378   if (strcmp (option->name, "gtk-paper-source") == 0)
4379     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
4380                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4381   else if (strcmp (option->name, "gtk-output-tray") == 0)
4382     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
4383                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4384   else if (strcmp (option->name, "gtk-duplex") == 0)
4385     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
4386                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4387   else if (strcmp (option->name, "cups-OutputMode") == 0)
4388     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
4389                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4390   else if (strcmp (option->name, "cups-Resolution") == 0)
4391     {
4392       int res, res_x, res_y;
4393
4394       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
4395         {
4396           if (res_x > 0 && res_y > 0)
4397             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
4398         }
4399       else if (sscanf (value, "%ddpi", &res) == 1)
4400         {
4401           if (res > 0)
4402             gtk_print_settings_set_resolution (settings, res);
4403         }
4404
4405       gtk_print_settings_set (settings, option->name, value);
4406     }
4407   else if (strcmp (option->name, "gtk-paper-type") == 0)
4408     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
4409                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4410   else if (strcmp (option->name, "gtk-n-up") == 0)
4411     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4412                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4413   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4414     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4415                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4416   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
4417     gtk_print_settings_set (settings, "cups-job-billing", value);
4418   else if (strcmp (option->name, "gtk-job-prio") == 0)
4419     gtk_print_settings_set (settings, "cups-job-priority", value);
4420   else if (strcmp (option->name, "gtk-cover-before") == 0)
4421     gtk_print_settings_set (settings, "cover-before", value);
4422   else if (strcmp (option->name, "gtk-cover-after") == 0)
4423     gtk_print_settings_set (settings, "cover-after", value);
4424   else if (strcmp (option->name, "gtk-print-time") == 0)
4425     gtk_print_settings_set (settings, "print-at", value);
4426   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4427     gtk_print_settings_set (settings, "print-at-time", value);
4428   else if (g_str_has_prefix (option->name, "cups-"))
4429     gtk_print_settings_set (settings, option->name, value);
4430 }
4431
4432 static gboolean
4433 supports_am_pm (void)
4434 {
4435   struct tm tmp_tm = { 0 };
4436   char   time[8];
4437   int    length;
4438
4439   length = strftime (time, sizeof (time), "%p", &tmp_tm);
4440
4441   return length != 0;
4442 }
4443
4444 /* Converts local time to UTC time. Local time has to be in one of these
4445  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
4446  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
4447  * Returns a newly allocated string holding UTC time in HH:MM:SS format
4448  * or NULL.
4449  */
4450 gchar *
4451 localtime_to_utctime (const char *local_time)
4452 {
4453   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
4454                              " %H : %M : %S ",
4455                              " %I : %M %p ", " %p %I : %M ",
4456                              " %H : %M ",
4457                              " %I %p ", " %p %I "};
4458   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
4459   const char *end = NULL;
4460   struct tm  *actual_local_time;
4461   struct tm  *actual_utc_time;
4462   struct tm   local_print_time;
4463   struct tm   utc_print_time;
4464   struct tm   diff_time;
4465   gchar      *utc_time = NULL;
4466   int         i, n;
4467
4468   if (local_time == NULL || local_time[0] == '\0')
4469     return NULL;
4470
4471   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
4472
4473   for (i = 0; i < n; i++)
4474     {
4475       local_print_time.tm_hour = 0;
4476       local_print_time.tm_min  = 0;
4477       local_print_time.tm_sec  = 0;
4478
4479       if (supports_am_pm ())
4480         end = strptime (local_time, formats_0[i], &local_print_time);
4481       else
4482         end = strptime (local_time, formats_1[i], &local_print_time);
4483
4484       if (end != NULL && end[0] == '\0')
4485         break;
4486     }
4487
4488   if (end != NULL && end[0] == '\0')
4489     {
4490       time_t rawtime;
4491       time (&rawtime);
4492
4493       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
4494       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
4495
4496       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
4497       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
4498       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
4499
4500       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
4501       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
4502       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
4503
4504       utc_time = g_strdup_printf ("%02d:%02d:%02d",
4505                                   utc_print_time.tm_hour,
4506                                   utc_print_time.tm_min,
4507                                   utc_print_time.tm_sec);
4508     }
4509
4510   return utc_time;
4511 }
4512
4513 static void
4514 cups_printer_get_settings_from_options (GtkPrinter          *printer,
4515                                         GtkPrinterOptionSet *options,
4516                                         GtkPrintSettings    *settings)
4517 {
4518   struct OptionData data;
4519   const char *print_at, *print_at_time;
4520
4521   data.printer = printer;
4522   data.options = options;
4523   data.settings = settings;
4524   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4525
4526   if (data.ppd_file != NULL)
4527     {
4528       GtkPrinterOption *cover_before, *cover_after;
4529
4530       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
4531
4532       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
4533       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
4534       if (cover_before && cover_after)
4535         {
4536           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
4537           gtk_print_settings_set (settings, "cups-job-sheets", value);
4538           g_free (value);
4539         }
4540
4541       print_at = gtk_print_settings_get (settings, "print-at");
4542       print_at_time = gtk_print_settings_get (settings, "print-at-time");
4543
4544       if (strcmp (print_at, "at") == 0)
4545         {
4546           gchar *utc_time = NULL;
4547
4548           utc_time = localtime_to_utctime (print_at_time);
4549
4550           if (utc_time != NULL)
4551             {
4552               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
4553               g_free (utc_time);
4554             }
4555           else
4556             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
4557         }
4558       else if (strcmp (print_at, "on-hold") == 0)
4559         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
4560     }
4561 }
4562
4563 static void
4564 cups_printer_prepare_for_print (GtkPrinter       *printer,
4565                                 GtkPrintJob      *print_job,
4566                                 GtkPrintSettings *settings,
4567                                 GtkPageSetup     *page_setup)
4568 {
4569   GtkPrintPages pages;
4570   GtkPageRange *ranges;
4571   gint n_ranges;
4572   GtkPageSet page_set;
4573   GtkPaperSize *paper_size;
4574   const char *ppd_paper_name;
4575   double scale;
4576
4577   pages = gtk_print_settings_get_print_pages (settings);
4578   gtk_print_job_set_pages (print_job, pages);
4579
4580   if (pages == GTK_PRINT_PAGES_RANGES)
4581     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
4582   else
4583     {
4584       ranges = NULL;
4585       n_ranges = 0;
4586     }
4587
4588   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
4589   if (gtk_print_settings_get_collate (settings))
4590     gtk_print_settings_set (settings, "cups-Collate", "True");
4591   gtk_print_job_set_collate (print_job, FALSE);
4592
4593   if (gtk_print_settings_get_reverse (settings))
4594     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
4595   gtk_print_job_set_reverse (print_job, FALSE);
4596
4597   if (gtk_print_settings_get_n_copies (settings) > 1)
4598     gtk_print_settings_set_int (settings, "cups-copies",
4599                                 gtk_print_settings_get_n_copies (settings));
4600   gtk_print_job_set_num_copies (print_job, 1);
4601
4602   scale = gtk_print_settings_get_scale (settings);
4603   if (scale != 100.0)
4604     gtk_print_job_set_scale (print_job, scale / 100.0);
4605
4606   page_set = gtk_print_settings_get_page_set (settings);
4607   if (page_set == GTK_PAGE_SET_EVEN)
4608     gtk_print_settings_set (settings, "cups-page-set", "even");
4609   else if (page_set == GTK_PAGE_SET_ODD)
4610     gtk_print_settings_set (settings, "cups-page-set", "odd");
4611   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
4612
4613   paper_size = gtk_page_setup_get_paper_size (page_setup);
4614   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
4615   if (ppd_paper_name != NULL)
4616     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
4617   else
4618     {
4619       char width[G_ASCII_DTOSTR_BUF_SIZE];
4620       char height[G_ASCII_DTOSTR_BUF_SIZE];
4621       char *custom_name;
4622
4623       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4624       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4625       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
4626       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
4627       g_free (custom_name);
4628     }
4629
4630   if (gtk_print_settings_get_number_up (settings) > 1)
4631     {
4632       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
4633       GEnumClass        *enum_class;
4634       GEnumValue        *enum_value;
4635
4636       switch (gtk_page_setup_get_orientation (page_setup))
4637         {
4638           case GTK_PAGE_ORIENTATION_PORTRAIT:
4639             break;
4640           case GTK_PAGE_ORIENTATION_LANDSCAPE:
4641             if (layout < 4)
4642               layout = layout + 2 + 4 * (1 - layout / 2);
4643             else
4644               layout = layout - 3 - 2 * (layout % 2);
4645             break;
4646           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4647             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
4648             break;
4649           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4650             if (layout < 4)
4651               layout = layout + 5 - 2 * (layout % 2);
4652             else
4653               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
4654             break;
4655         }
4656
4657       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
4658       enum_value = g_enum_get_value (enum_class, layout);
4659       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
4660       g_type_class_unref (enum_class);
4661     }
4662
4663   gtk_print_job_set_rotate (print_job, TRUE);
4664 }
4665
4666 static GtkPageSetup *
4667 create_page_setup (ppd_file_t *ppd_file,
4668                    ppd_size_t *size)
4669  {
4670    char *display_name;
4671    GtkPageSetup *page_setup;
4672    GtkPaperSize *paper_size;
4673    ppd_option_t *option;
4674    ppd_choice_t *choice;
4675
4676   display_name = NULL;
4677   option = ppdFindOption (ppd_file, "PageSize");
4678   if (option)
4679     {
4680       choice = ppdFindChoice (option, size->name);
4681       if (choice)
4682         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
4683     }
4684
4685   if (display_name == NULL)
4686     display_name = g_strdup (size->name);
4687
4688   page_setup = gtk_page_setup_new ();
4689   paper_size = gtk_paper_size_new_from_ppd (size->name,
4690                                             display_name,
4691                                             size->width,
4692                                             size->length);
4693   gtk_page_setup_set_paper_size (page_setup, paper_size);
4694   gtk_paper_size_free (paper_size);
4695
4696   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
4697   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
4698   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
4699   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
4700
4701   g_free (display_name);
4702
4703   return page_setup;
4704 }
4705
4706 static GList *
4707 cups_printer_list_papers (GtkPrinter *printer)
4708 {
4709   ppd_file_t *ppd_file;
4710   ppd_size_t *size;
4711   GtkPageSetup *page_setup;
4712   GList *l;
4713   int i;
4714
4715   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4716   if (ppd_file == NULL)
4717     return NULL;
4718
4719   l = NULL;
4720
4721   for (i = 0; i < ppd_file->num_sizes; i++)
4722     {
4723       size = &ppd_file->sizes[i];
4724
4725       page_setup = create_page_setup (ppd_file, size);
4726
4727       l = g_list_prepend (l, page_setup);
4728     }
4729
4730   return g_list_reverse (l);
4731 }
4732
4733 static GtkPageSetup *
4734 cups_printer_get_default_page_size (GtkPrinter *printer)
4735 {
4736   ppd_file_t *ppd_file;
4737   ppd_size_t *size;
4738   ppd_option_t *option;
4739
4740
4741   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4742   if (ppd_file == NULL)
4743     return NULL;
4744
4745   option = ppdFindOption (ppd_file, "PageSize");
4746   if (option == NULL)
4747     return NULL;
4748
4749   size = ppdPageSize (ppd_file, option->defchoice);
4750   if (size == NULL)
4751     return NULL;
4752
4753   return create_page_setup (ppd_file, size);
4754 }
4755
4756 static gboolean
4757 cups_printer_get_hard_margins (GtkPrinter *printer,
4758                                gdouble    *top,
4759                                gdouble    *bottom,
4760                                gdouble    *left,
4761                                gdouble    *right)
4762 {
4763   ppd_file_t *ppd_file;
4764
4765   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4766   if (ppd_file == NULL)
4767     return FALSE;
4768
4769   *left = ppd_file->custom_margins[0];
4770   *bottom = ppd_file->custom_margins[1];
4771   *right = ppd_file->custom_margins[2];
4772   *top = ppd_file->custom_margins[3];
4773
4774   return TRUE;
4775 }
4776
4777 static GtkPrintCapabilities
4778 cups_printer_get_capabilities (GtkPrinter *printer)
4779 {
4780   return
4781     GTK_PRINT_CAPABILITY_COPIES |
4782     GTK_PRINT_CAPABILITY_COLLATE |
4783     GTK_PRINT_CAPABILITY_REVERSE |
4784     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
4785     GTK_PRINT_CAPABILITY_NUMBER_UP;
4786 }