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