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