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