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