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