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