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