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