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