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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 #include <ctype.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <stdlib.h>
26 #include <time.h>
27 
28 /* Cups 1.6 deprecates ppdFindAttr(), ppdFindCustomOption(),
29  * ppdFirstCustomParam(), and ppdNextCustomParam() among others.
30  * The replacement is to use the Job Ticket API, but that requires
31  * a larger refactoring of this backend.
32  */
33 
34 #include <cups/cups.h>
35 #include <cups/language.h>
36 #include <cups/http.h>
37 #include <cups/ipp.h>
38 #include <errno.h>
39 #include <cairo.h>
40 #include <cairo-pdf.h>
41 #include <cairo-ps.h>
42 
43 #include <glib/gstdio.h>
44 #include <glib/gi18n-lib.h>
45 #include <gmodule.h>
46 
47 #include <gtk/gtk.h>
48 #include <gtk/gtkprintbackendprivate.h>
49 #include <gtk/gtkunixprint.h>
50 #include <gtk/gtkprinterprivate.h>
51 
52 #include "gtkprintbackendcups.h"
53 #include "gtkprintercups.h"
54 
55 #include "gtkcupsutils.h"
56 #include "gtkcupssecretsutils.h"
57 
58 #include <gtkprintutils.h>
59 #include "gtkprivate.h"
60 
61 #ifdef HAVE_COLORD
62 #include <colord.h>
63 #endif
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 #define AVAHI_IF_UNSPEC -1
75 #define AVAHI_PROTO_INET 0
76 #define AVAHI_PROTO_INET6 1
77 #define AVAHI_PROTO_UNSPEC -1
78 
79 #define AVAHI_BUS "org.freedesktop.Avahi"
80 #define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server"
81 #define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
82 #define AVAHI_SERVICE_RESOLVER_IFACE "org.freedesktop.Avahi.ServiceResolver"
83 
84 #define PRINTER_NAME_ALLOWED_CHARACTERS "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
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 #if !GLIB_CHECK_VERSION (2, 67, 3)
92 # define g_memdup2(mem,size)    g_memdup((mem), (size))
93 #endif
94 
95 typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
96                                                    GtkCupsResult   *result,
97                                                    gpointer         user_data);
98 
99 typedef enum
100 {
101   DISPATCH_SETUP,
102   DISPATCH_REQUEST,
103   DISPATCH_SEND,
104   DISPATCH_CHECK,
105   DISPATCH_READ,
106   DISPATCH_ERROR
107 } GtkPrintCupsDispatchState;
108 
109 typedef struct
110 {
111   GSource source;
112 
113   http_t *http;
114   GtkCupsRequest *request;
115   GtkCupsPollState poll_state;
116   GPollFD *data_poll;
117   GtkPrintBackendCups *backend;
118   GtkPrintCupsResponseCallbackFunc callback;
119   gpointer                         callback_data;
120 
121 } GtkPrintCupsDispatchWatch;
122 
123 struct _GtkPrintBackendCupsClass
124 {
125   GtkPrintBackendClass parent_class;
126 };
127 
128 struct _GtkPrintBackendCups
129 {
130   GtkPrintBackend parent_instance;
131 
132   char *default_printer;
133 
134   guint list_printers_poll;
135   guint list_printers_pending : 1;
136   int   list_printers_attempts;
137   guint got_default_printer   : 1;
138   guint default_printer_poll;
139   GtkCupsConnectionTest *cups_connection_test;
140   int   reading_ppds;
141 
142   GList      *requests;
143   GHashTable *auth;
144   char       *username;
145   gboolean    authentication_lock;
146 #ifdef HAVE_COLORD
147   CdClient   *colord_client;
148 #endif
149 
150   GDBusConnection *dbus_connection;
151   char *avahi_default_printer;
152   guint avahi_service_browser_subscription_id;
153   guint avahi_service_browser_subscription_ids[2];
154   char *avahi_service_browser_paths[2];
155   GCancellable *avahi_cancellable;
156   guint unsubscribe_general_subscription_id;
157 
158   gboolean      secrets_service_available;
159   guint         secrets_service_watch_id;
160   GCancellable *secrets_service_cancellable;
161 
162   GList *temporary_queues_in_construction;
163   GList *temporary_queues_removed;
164 };
165 
166 static GObjectClass *backend_parent_class;
167 
168 static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
169 static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
170 static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
171 static void                 cups_get_default_printer               (GtkPrintBackendCups               *print_backend);
172 static void                 cups_get_local_default_printer         (GtkPrintBackendCups               *print_backend);
173 static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
174 								    GtkCupsRequest                    *request,
175 								    GtkPrintCupsResponseCallbackFunc   callback,
176 								    gpointer                           user_data,
177 								    GDestroyNotify                     notify);
178 static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
179 								    GtkPrinterOptionSet               *options,
180 								    GtkPrintSettings                  *settings);
181 static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
182 								    GtkPrinterOptionSet               *options);
183 static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
184 								    GtkPrintSettings                  *settings,
185 								    GtkPageSetup                      *page_setup,
186                                                                     GtkPrintCapabilities               capabilities);
187 static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
188 								    GtkPrintJob                       *print_job,
189 								    GtkPrintSettings                  *settings,
190 								    GtkPageSetup                      *page_setup);
191 static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
192 static GtkPageSetup *       cups_printer_get_default_page_size     (GtkPrinter                        *printer);
193 static void                 cups_printer_request_details           (GtkPrinter                        *printer);
194 static gboolean             cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
195 static gboolean             cups_request_ppd                       (GtkPrinter                        *printer);
196 static gboolean             cups_printer_get_hard_margins          (GtkPrinter                        *printer,
197 								    double                            *top,
198 								    double                            *bottom,
199 								    double                            *left,
200 								    double                            *right);
201 static gboolean             cups_printer_get_hard_margins_for_paper_size (GtkPrinter                  *printer,
202 									  GtkPaperSize                *paper_size,
203 									  double                      *top,
204 									  double                      *bottom,
205 									  double                      *left,
206 									  double                      *right);
207 static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
208 static void                 set_option_from_settings               (GtkPrinterOption                  *option,
209 								    GtkPrintSettings                  *setting);
210 static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
211 								    GtkPrintJob                       *job,
212 								    int                                job_id);
213 static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
214 static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
215 								    GtkPrintJob                       *job,
216 								    GIOChannel                        *data_io,
217 								    GtkPrintJobCompleteFunc            callback,
218 								    gpointer                           user_data,
219 								    GDestroyNotify                     dnotify);
220 static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
221 								    GtkPrintSettings                  *settings,
222 								    double                             width,
223 								    double                             height,
224 								    GIOChannel                        *cache_io);
225 
226 static void                 gtk_print_backend_cups_set_password    (GtkPrintBackend                   *backend,
227                                                                     char                             **auth_info_required,
228                                                                     char                             **auth_info,
229                                                                     gboolean                           store_auth_info);
230 
231 void                        overwrite_and_free                      (gpointer                          data);
232 static gboolean             is_address_local                        (const char                       *address);
233 static gboolean             request_auth_info                       (gpointer                          data);
234 static void                 lookup_auth_info                        (gpointer                          data);
235 
236 static void                 avahi_request_printer_list              (GtkPrintBackendCups              *cups_backend);
237 
238 static void                 secrets_service_appeared_cb             (GDBusConnection *connection,
239                                                                      const char *name,
240                                                                      const char *name_owner,
241                                                                      gpointer user_data);
242 static void                 secrets_service_vanished_cb             (GDBusConnection *connection,
243                                                                      const char *name,
244                                                                      gpointer user_data);
245 
G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendCups,gtk_print_backend_cups,GTK_TYPE_PRINT_BACKEND)246 G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendCups, gtk_print_backend_cups, GTK_TYPE_PRINT_BACKEND)
247 
248 void
249 g_io_module_load (GIOModule *module)
250 {
251   g_type_module_use (G_TYPE_MODULE (module));
252 
253   gtk_print_backend_cups_register_type (G_TYPE_MODULE (module));
254   gtk_printer_cups_register_type (G_TYPE_MODULE (module));
255 
256   g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
257                                   GTK_TYPE_PRINT_BACKEND_CUPS,
258                                   "cups",
259                                   10);
260 }
261 
262 void
g_io_module_unload(GIOModule * module)263 g_io_module_unload (GIOModule *module)
264 {
265 }
266 
267 char **
g_io_module_query(void)268 g_io_module_query (void)
269 {
270   char *eps[] = {
271     (char *)GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
272     NULL
273   };
274 
275   return g_strdupv (eps);
276 }
277 
278 /*
279  * GtkPrintBackendCups
280  */
281 
282 /**
283  * gtk_print_backend_cups_new:
284  *
285  * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
286  * implements the #GtkPrintBackend interface with direct access to
287  * the filesystem using Unix/Linux API calls
288  *
289  * Returns: the new #GtkPrintBackendCups object
290  */
291 GtkPrintBackend *
gtk_print_backend_cups_new(void)292 gtk_print_backend_cups_new (void)
293 {
294   GTK_NOTE (PRINTING,
295             g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
296 
297   return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
298 }
299 
300 static void                 create_temporary_queue                  (GtkPrintBackendCups *backend,
301                                                                      const gchar         *printer_name,
302                                                                      const gchar         *printer_uri,
303                                                                      const gchar         *device_uri);
304 
305 static void
gtk_print_backend_cups_class_init(GtkPrintBackendCupsClass * class)306 gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
307 {
308   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
309   GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
310 
311   backend_parent_class = g_type_class_peek_parent (class);
312 
313   gobject_class->finalize = gtk_print_backend_cups_finalize;
314   gobject_class->dispose = gtk_print_backend_cups_dispose;
315 
316   backend_class->request_printer_list = cups_get_printer_list;
317   backend_class->print_stream = gtk_print_backend_cups_print_stream;
318   backend_class->printer_request_details = cups_printer_request_details;
319   backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
320   backend_class->printer_get_options = cups_printer_get_options;
321   backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
322   backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
323   backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
324   backend_class->printer_list_papers = cups_printer_list_papers;
325   backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
326   backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
327   backend_class->printer_get_hard_margins_for_paper_size = cups_printer_get_hard_margins_for_paper_size;
328   backend_class->printer_get_capabilities = cups_printer_get_capabilities;
329   backend_class->set_password = gtk_print_backend_cups_set_password;
330 }
331 
332 static void
gtk_print_backend_cups_class_finalize(GtkPrintBackendCupsClass * class)333 gtk_print_backend_cups_class_finalize (GtkPrintBackendCupsClass *class)
334 {
335 }
336 
337 static gboolean
option_is_ipp_option(GtkPrinterOption * option)338 option_is_ipp_option (GtkPrinterOption *option)
339 {
340   gpointer data = g_object_get_data (G_OBJECT (option), "is-ipp-option");
341 
342   if (data != NULL)
343     return GPOINTER_TO_UINT (data) != 0;
344   else
345     return FALSE;
346 }
347 
348 static void
option_set_is_ipp_option(GtkPrinterOption * option,gboolean is_ipp_option)349 option_set_is_ipp_option (GtkPrinterOption *option,
350                           gboolean          is_ipp_option)
351 {
352   g_object_set_data (G_OBJECT (option),
353                      "is-ipp-option",
354                      GUINT_TO_POINTER (is_ipp_option ? 1 : 0));
355 }
356 
357 static cairo_status_t
_cairo_write_to_cups(void * closure,const unsigned char * data,unsigned int length)358 _cairo_write_to_cups (void                *closure,
359                       const unsigned char *data,
360                       unsigned int         length)
361 {
362   GIOChannel *io = (GIOChannel *)closure;
363   gsize written;
364   GError *error;
365 
366   error = NULL;
367 
368   GTK_NOTE (PRINTING,
369             g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
370 
371   while (length > 0)
372     {
373       g_io_channel_write_chars (io, (char *)data, length, &written, &error);
374 
375       if (error != NULL)
376 	{
377 	  GTK_NOTE (PRINTING,
378                     g_print ("CUPS Backend: Error writing to temp file, %s\n",
379                              error->message));
380 
381           g_error_free (error);
382 	  return CAIRO_STATUS_WRITE_ERROR;
383 	}
384 
385       GTK_NOTE (PRINTING,
386                 g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written));
387 
388       data += written;
389       length -= written;
390     }
391 
392   return CAIRO_STATUS_SUCCESS;
393 }
394 
395 static cairo_surface_t *
cups_printer_create_cairo_surface(GtkPrinter * printer,GtkPrintSettings * settings,double width,double height,GIOChannel * cache_io)396 cups_printer_create_cairo_surface (GtkPrinter       *printer,
397 				   GtkPrintSettings *settings,
398 				   double            width,
399 				   double            height,
400 				   GIOChannel       *cache_io)
401 {
402   cairo_surface_t *surface;
403   ppd_file_t      *ppd_file = NULL;
404   ppd_attr_t      *ppd_attr = NULL;
405   ppd_attr_t      *ppd_attr_res = NULL;
406   ppd_attr_t      *ppd_attr_screen_freq = NULL;
407   ppd_attr_t      *ppd_attr_res_screen_freq = NULL;
408   char            *res_string = NULL;
409   int              level = 2;
410 
411   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
412 
413   if (gtk_printer_accepts_pdf (printer))
414     surface = cairo_pdf_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height);
415   else
416     surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
417 
418   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
419 
420   if (ppd_file != NULL)
421     {
422       ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
423 
424       if (ppd_attr != NULL)
425         level = atoi (ppd_attr->value);
426 
427       if (gtk_print_settings_get_resolution (settings) == 0)
428         {
429           ppd_attr_res = ppdFindAttr (ppd_file, "DefaultResolution", NULL);
430 
431           if (ppd_attr_res != NULL)
432             {
433               int res, res_x, res_y;
434 
435               if (sscanf (ppd_attr_res->value, "%dx%ddpi", &res_x, &res_y) == 2)
436                 {
437                   if (res_x > 0 && res_y > 0)
438                     gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
439                 }
440               else if (sscanf (ppd_attr_res->value, "%ddpi", &res) == 1)
441                 {
442                   if (res > 0)
443                     gtk_print_settings_set_resolution (settings, res);
444                 }
445             }
446         }
447 
448       res_string = g_strdup_printf ("%ddpi",
449                                     gtk_print_settings_get_resolution (settings));
450       ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
451       g_free (res_string);
452 
453       if (ppd_attr_res_screen_freq == NULL)
454         {
455           res_string = g_strdup_printf ("%dx%ddpi",
456                                         gtk_print_settings_get_resolution_x (settings),
457                                         gtk_print_settings_get_resolution_y (settings));
458           ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
459           g_free (res_string);
460         }
461 
462       ppd_attr_screen_freq = ppdFindAttr (ppd_file, "ScreenFreq", NULL);
463 
464       if (ppd_attr_res_screen_freq != NULL && atof (ppd_attr_res_screen_freq->value) > 0.0)
465         gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_res_screen_freq->value));
466       else if (ppd_attr_screen_freq != NULL && atof (ppd_attr_screen_freq->value) > 0.0)
467         gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_screen_freq->value));
468     }
469 
470   if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PS)
471     {
472       if (level == 2)
473         cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
474 
475       if (level == 3)
476         cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
477     }
478 
479   cairo_surface_set_fallback_resolution (surface,
480                                          2.0 * gtk_print_settings_get_printer_lpi (settings),
481                                          2.0 * gtk_print_settings_get_printer_lpi (settings));
482 
483   G_GNUC_END_IGNORE_DEPRECATIONS
484 
485   return surface;
486 }
487 
488 typedef struct {
489   GtkPrintJobCompleteFunc callback;
490   GtkPrintJob *job;
491   gpointer user_data;
492   GDestroyNotify dnotify;
493   http_t *http;
494 } CupsPrintStreamData;
495 
496 static void
cups_free_print_stream_data(CupsPrintStreamData * data)497 cups_free_print_stream_data (CupsPrintStreamData *data)
498 {
499   GTK_NOTE (PRINTING,
500             g_print ("CUPS Backend: %s\n", G_STRFUNC));
501 
502   if (data->dnotify)
503     data->dnotify (data->user_data);
504   g_object_unref (data->job);
505   if (data->http != NULL)
506     httpClose (data->http);
507   g_free (data);
508 }
509 
510 static void
cups_print_cb(GtkPrintBackendCups * print_backend,GtkCupsResult * result,gpointer user_data)511 cups_print_cb (GtkPrintBackendCups *print_backend,
512                GtkCupsResult       *result,
513                gpointer             user_data)
514 {
515   GError *error = NULL;
516   CupsPrintStreamData *ps = user_data;
517 
518   GTK_NOTE (PRINTING,
519             g_print ("CUPS Backend: %s\n", G_STRFUNC));
520 
521   if (gtk_cups_result_is_error (result))
522     error = g_error_new_literal (gtk_print_error_quark (),
523                                  GTK_PRINT_ERROR_INTERNAL_ERROR,
524                                  gtk_cups_result_get_error_string (result));
525 
526   if (ps->callback)
527     ps->callback (ps->job, ps->user_data, error);
528 
529   if (error == NULL)
530     {
531       int job_id = 0;
532       ipp_attribute_t *attr;		/* IPP job-id attribute */
533       ipp_t *response = gtk_cups_result_get_response (result);
534 
535       if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
536 	job_id = ippGetInteger (attr, 0);
537 
538       if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
539 	gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
540       else
541 	{
542 	  gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
543 	  cups_begin_polling_info (print_backend, ps->job, job_id);
544 	}
545     }
546   else
547     gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
548 
549 
550   if (error)
551     g_error_free (error);
552 }
553 
554 typedef struct {
555   GtkCupsRequest *request;
556   GtkPageSetup *page_setup;
557   GtkPrinterCups *printer;
558 } CupsOptionsData;
559 
560 #define UNSIGNED_FLOAT_REGEX "([0-9]+([.,][0-9]*)?|[.,][0-9]+)([e][+-]?[0-9]+)?"
561 #define SIGNED_FLOAT_REGEX "[+-]?"UNSIGNED_FLOAT_REGEX
562 #define SIGNED_INTEGER_REGEX "[+-]?([0-9]+)"
563 
564 static void
add_cups_options(const char * key,const char * value,gpointer user_data)565 add_cups_options (const char *key,
566 		  const char *value,
567 		  gpointer     user_data)
568 {
569   CupsOptionsData *data = (CupsOptionsData *) user_data;
570   GtkCupsRequest *request = data->request;
571   GtkPrinterCups *printer = data->printer;
572   gboolean custom_value = FALSE;
573   char *new_value = NULL;
574   int i;
575 
576   if (!key || !value)
577     return;
578 
579   if (!g_str_has_prefix (key, "cups-"))
580     return;
581 
582   if (strcmp (value, "gtk-ignore-value") == 0)
583     return;
584 
585   key = key + strlen ("cups-");
586 
587   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
588 
589   if (printer && printer->ppd_file && !g_str_has_prefix (value, "Custom."))
590     {
591       ppd_coption_t *coption;
592       gboolean       found = FALSE;
593       gboolean       custom_values_enabled = FALSE;
594 
595       coption = ppdFindCustomOption (printer->ppd_file, key);
596       if (coption && coption->option)
597         {
598           for (i = 0; i < coption->option->num_choices; i++)
599             {
600               /* Are custom values enabled ? */
601               if (g_str_equal (coption->option->choices[i].choice, "Custom"))
602                 custom_values_enabled = TRUE;
603 
604               /* Is the value among available choices ? */
605               if (g_str_equal (coption->option->choices[i].choice, value))
606                 found = TRUE;
607             }
608 
609           if (custom_values_enabled && !found)
610             {
611               /* Check syntax of the invalid choice to see whether
612                  it could be a custom value */
613               if (g_str_equal (key, "PageSize") ||
614                   g_str_equal (key, "PageRegion"))
615                 {
616                   /* Handle custom page sizes... */
617                   if (g_regex_match_simple ("^" UNSIGNED_FLOAT_REGEX "x" UNSIGNED_FLOAT_REGEX "(cm|mm|m|in|ft|pt)?$", value, G_REGEX_CASELESS, 0))
618                     custom_value = TRUE;
619                   else
620                     {
621                       if (data->page_setup != NULL)
622                         {
623                           custom_value = TRUE;
624                           new_value =
625                             g_strdup_printf ("Custom.%.2fx%.2fmm",
626                                              gtk_paper_size_get_width (gtk_page_setup_get_paper_size (data->page_setup), GTK_UNIT_MM),
627                                              gtk_paper_size_get_height (gtk_page_setup_get_paper_size (data->page_setup), GTK_UNIT_MM));
628                         }
629                     }
630                 }
631               else
632                 {
633                   /* Handle other custom options... */
634                   ppd_cparam_t  *cparam;
635 
636                   cparam = (ppd_cparam_t *) cupsArrayFirst (coption->params);
637                   if (cparam != NULL)
638                     {
639                       switch (cparam->type)
640                         {
641                         case PPD_CUSTOM_CURVE :
642                         case PPD_CUSTOM_INVCURVE :
643                         case PPD_CUSTOM_REAL :
644                           if (g_regex_match_simple ("^" SIGNED_FLOAT_REGEX "$", value, G_REGEX_CASELESS, 0))
645                             custom_value = TRUE;
646                           break;
647 
648                         case PPD_CUSTOM_POINTS :
649                           if (g_regex_match_simple ("^" SIGNED_FLOAT_REGEX "(cm|mm|m|in|ft|pt)?$", value, G_REGEX_CASELESS, 0))
650                             custom_value = TRUE;
651                           break;
652 
653                         case PPD_CUSTOM_INT :
654                           if (g_regex_match_simple ("^" SIGNED_INTEGER_REGEX "$", value, G_REGEX_CASELESS, 0))
655                             custom_value = TRUE;
656                           break;
657 
658                         case PPD_CUSTOM_PASSCODE :
659                         case PPD_CUSTOM_PASSWORD :
660                         case PPD_CUSTOM_STRING :
661                           custom_value = TRUE;
662                           break;
663 
664 #if (CUPS_VERSION_MAJOR >= 3) || \
665     (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR >= 3) || \
666     (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR == 2 && CUPS_VERSION_PATCH >= 12)
667                         case PPD_CUSTOM_UNKNOWN:
668 #endif
669                         default :
670                           custom_value = FALSE;
671                         }
672                     }
673                 }
674             }
675         }
676     }
677 
678   G_GNUC_END_IGNORE_DEPRECATIONS
679 
680   /* Add "Custom." prefix to custom values if not already added. */
681   if (custom_value)
682     {
683       if (new_value == NULL)
684         new_value = g_strdup_printf ("Custom.%s", value);
685       gtk_cups_request_encode_option (request, key, new_value);
686       g_free (new_value);
687     }
688   else
689     gtk_cups_request_encode_option (request, key, value);
690 }
691 
692 static void
gtk_print_backend_cups_print_stream(GtkPrintBackend * print_backend,GtkPrintJob * job,GIOChannel * data_io,GtkPrintJobCompleteFunc callback,gpointer user_data,GDestroyNotify dnotify)693 gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
694                                      GtkPrintJob             *job,
695 				     GIOChannel              *data_io,
696 				     GtkPrintJobCompleteFunc  callback,
697 				     gpointer                 user_data,
698 				     GDestroyNotify           dnotify)
699 {
700   GtkPrinterCups *cups_printer;
701   CupsPrintStreamData *ps;
702   CupsOptionsData *options_data;
703   GtkPageSetup *page_setup;
704   GtkCupsRequest *request = NULL;
705   GtkPrintSettings *settings;
706   const char *title;
707   char  printer_absolute_uri[HTTP_MAX_URI];
708   http_t *http = NULL;
709 
710   GTK_NOTE (PRINTING,
711             g_print ("CUPS Backend: %s\n", G_STRFUNC));
712 
713   cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
714   settings = gtk_print_job_get_settings (job);
715 
716   if (cups_printer->avahi_browsed)
717     {
718       http = httpConnect2 (cups_printer->hostname, cups_printer->port,
719                            NULL, AF_UNSPEC,
720                            HTTP_ENCRYPTION_IF_REQUESTED,
721                            1, 30000,
722                            NULL);
723       if (http)
724         {
725           request = gtk_cups_request_new_with_username (http,
726                                                         GTK_CUPS_POST,
727                                                         IPP_PRINT_JOB,
728                                                         data_io,
729                                                         cups_printer->hostname,
730                                                         cups_printer->device_uri,
731                                                         GTK_PRINT_BACKEND_CUPS (print_backend)->username);
732           g_snprintf (printer_absolute_uri, HTTP_MAX_URI, "%s", cups_printer->printer_uri);
733         }
734       else
735         {
736           GError *error = NULL;
737 
738           GTK_NOTE (PRINTING,
739                     g_warning ("CUPS Backend: Error connecting to %s:%d",
740                                cups_printer->hostname,
741                                cups_printer->port));
742 
743           error = g_error_new (gtk_print_error_quark (),
744                                GTK_CUPS_ERROR_GENERAL,
745                                "Error connecting to %s",
746                                cups_printer->hostname);
747 
748           gtk_print_job_set_status (job, GTK_PRINT_STATUS_FINISHED_ABORTED);
749 
750           if (callback)
751             {
752               callback (job, user_data, error);
753             }
754 
755           g_clear_error (&error);
756 
757           return;
758         }
759     }
760   else
761     {
762       request = gtk_cups_request_new_with_username (NULL,
763                                                     GTK_CUPS_POST,
764                                                     IPP_PRINT_JOB,
765                                                     data_io,
766                                                     NULL,
767                                                     cups_printer->device_uri,
768                                                     GTK_PRINT_BACKEND_CUPS (print_backend)->username);
769 
770       httpAssembleURIf (HTTP_URI_CODING_ALL,
771                         printer_absolute_uri,
772                         sizeof (printer_absolute_uri),
773                         "ipp",
774                         NULL,
775                         "localhost",
776                         ippPort (),
777                         "/printers/%s",
778                         gtk_printer_get_name (gtk_print_job_get_printer (job)));
779     }
780 
781   gtk_cups_request_set_ipp_version (request,
782                                     cups_printer->ipp_version_major,
783                                     cups_printer->ipp_version_minor);
784 
785   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
786                                    IPP_TAG_URI, "printer-uri",
787                                    NULL, printer_absolute_uri);
788 
789   title = gtk_print_job_get_title (job);
790   if (title) {
791     char *title_truncated = NULL;
792     size_t title_bytes = strlen (title);
793 
794     if (title_bytes >= IPP_MAX_NAME)
795       {
796         char *end;
797 
798         end = g_utf8_find_prev_char (title, title + IPP_MAX_NAME - 1);
799         title_truncated = g_utf8_substring (title,
800                                             0,
801                                             g_utf8_pointer_to_offset (title, end));
802       }
803 
804     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
805                                      IPP_TAG_NAME, "job-name",
806                                      NULL,
807                                      title_truncated ? title_truncated : title);
808     g_free (title_truncated);
809   }
810 
811   g_object_get (job,
812                 "page-setup", &page_setup,
813                 NULL);
814 
815   options_data = g_new0 (CupsOptionsData, 1);
816   options_data->request = request;
817   options_data->printer = cups_printer;
818   options_data->page_setup = page_setup;
819   gtk_print_settings_foreach (settings, add_cups_options, options_data);
820   g_clear_object (&page_setup);
821   g_free (options_data);
822 
823   ps = g_new0 (CupsPrintStreamData, 1);
824   ps->callback = callback;
825   ps->user_data = user_data;
826   ps->dnotify = dnotify;
827   ps->job = g_object_ref (job);
828   ps->http = http;
829 
830   request->need_auth_info = FALSE;
831   request->auth_info_required = NULL;
832 
833   /* Check if auth_info_required is set and if it should be handled.
834    * The cups libraries handle the ticket exchange for "negotiate". */
835   if (cups_printer->auth_info_required != NULL &&
836       g_strv_length (cups_printer->auth_info_required) == 1 &&
837       g_strcmp0 (cups_printer->auth_info_required[0], "negotiate") == 0)
838     {
839       GTK_NOTE (PRINTING,
840                 g_print ("CUPS Backend: Ignoring auth-info-required \"%s\"\n",
841                          cups_printer->auth_info_required[0]));
842     }
843   else if (cups_printer->auth_info_required != NULL)
844     {
845       request->need_auth_info = TRUE;
846       request->auth_info_required = g_strdupv (cups_printer->auth_info_required);
847     }
848 
849   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
850                         request,
851                         (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
852                         ps,
853                         (GDestroyNotify)cups_free_print_stream_data);
854 }
855 
overwrite_and_free(gpointer data)856 void overwrite_and_free (gpointer data)
857 {
858   char *password = (char *) data;
859 
860   if (password != NULL)
861     {
862       memset (password, 0, strlen (password));
863       g_free (password);
864     }
865 }
866 
867 static void
gtk_print_backend_cups_init(GtkPrintBackendCups * backend_cups)868 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
869 {
870   int i;
871 
872   backend_cups->list_printers_poll = FALSE;
873   backend_cups->got_default_printer = FALSE;
874   backend_cups->list_printers_pending = FALSE;
875   backend_cups->list_printers_attempts = 0;
876   backend_cups->reading_ppds = 0;
877 
878   backend_cups->requests = NULL;
879   backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
880   backend_cups->authentication_lock = FALSE;
881 
882   backend_cups->default_printer_poll = 0;
883   backend_cups->cups_connection_test = NULL;
884 
885   backend_cups->username = NULL;
886 
887 #ifdef HAVE_COLORD
888   backend_cups->colord_client = cd_client_new ();
889 #endif
890 
891   backend_cups->dbus_connection = NULL;
892   backend_cups->avahi_default_printer = NULL;
893   backend_cups->avahi_service_browser_subscription_id = 0;
894   for (i = 0; i < 2; i++)
895     {
896       backend_cups->avahi_service_browser_paths[i] = NULL;
897       backend_cups->avahi_service_browser_subscription_ids[i] = 0;
898     }
899 
900   cups_get_local_default_printer (backend_cups);
901 
902   backend_cups->secrets_service_available = FALSE;
903   backend_cups->secrets_service_cancellable = g_cancellable_new ();
904   backend_cups->secrets_service_watch_id =
905     gtk_cups_secrets_service_watch (secrets_service_appeared_cb,
906                                     secrets_service_vanished_cb,
907                                     backend_cups);
908 
909   backend_cups->temporary_queues_in_construction = NULL;
910   backend_cups->temporary_queues_removed = NULL;
911 }
912 
913 static void
gtk_print_backend_cups_finalize(GObject * object)914 gtk_print_backend_cups_finalize (GObject *object)
915 {
916   GtkPrintBackendCups *backend_cups;
917 
918   GTK_NOTE (PRINTING,
919             g_print ("CUPS Backend: finalizing CUPS backend module\n"));
920 
921   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
922 
923   g_free (backend_cups->default_printer);
924   backend_cups->default_printer = NULL;
925 
926   gtk_cups_connection_test_free (backend_cups->cups_connection_test);
927   backend_cups->cups_connection_test = NULL;
928 
929   g_hash_table_destroy (backend_cups->auth);
930 
931   g_free (backend_cups->username);
932 
933 #ifdef HAVE_COLORD
934   g_object_unref (backend_cups->colord_client);
935 #endif
936 
937   g_clear_object (&backend_cups->avahi_cancellable);
938   g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
939   g_clear_object (&backend_cups->dbus_connection);
940 
941   g_clear_object (&backend_cups->secrets_service_cancellable);
942   if (backend_cups->secrets_service_watch_id != 0)
943     {
944       g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
945     }
946 
947   g_list_free_full (backend_cups->temporary_queues_in_construction, g_free);
948   backend_cups->temporary_queues_in_construction = NULL;
949 
950   g_list_free_full (backend_cups->temporary_queues_removed, g_free);
951   backend_cups->temporary_queues_removed = NULL;
952 
953   backend_parent_class->finalize (object);
954 }
955 
956 static void
gtk_print_backend_cups_dispose(GObject * object)957 gtk_print_backend_cups_dispose (GObject *object)
958 {
959   GtkPrintBackendCups *backend_cups;
960   int i;
961 
962   GTK_NOTE (PRINTING,
963             g_print ("CUPS Backend: %s\n", G_STRFUNC));
964 
965   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
966 
967   if (backend_cups->list_printers_poll > 0)
968     g_source_remove (backend_cups->list_printers_poll);
969   backend_cups->list_printers_poll = 0;
970   backend_cups->list_printers_attempts = 0;
971 
972   if (backend_cups->default_printer_poll > 0)
973     g_source_remove (backend_cups->default_printer_poll);
974   backend_cups->default_printer_poll = 0;
975 
976   g_cancellable_cancel (backend_cups->avahi_cancellable);
977 
978   for (i = 0; i < 2; i++)
979     {
980       if (backend_cups->avahi_service_browser_subscription_ids[i] > 0)
981         {
982           g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
983                                                 backend_cups->avahi_service_browser_subscription_ids[i]);
984           backend_cups->avahi_service_browser_subscription_ids[i] = 0;
985         }
986 
987       if (backend_cups->avahi_service_browser_paths[i])
988         {
989           g_dbus_connection_call (backend_cups->dbus_connection,
990                                   AVAHI_BUS,
991                                   backend_cups->avahi_service_browser_paths[i],
992                                   AVAHI_SERVICE_BROWSER_IFACE,
993                                   "Free",
994                                   NULL,
995                                   NULL,
996                                   G_DBUS_CALL_FLAGS_NONE,
997                                   -1,
998                                   NULL,
999                                   NULL,
1000                                   NULL);
1001           g_clear_pointer (&backend_cups->avahi_service_browser_paths[i], g_free);
1002         }
1003     }
1004 
1005   if (backend_cups->avahi_service_browser_subscription_id > 0)
1006     {
1007       g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
1008                                             backend_cups->avahi_service_browser_subscription_id);
1009       backend_cups->avahi_service_browser_subscription_id = 0;
1010     }
1011 
1012   if (backend_cups->unsubscribe_general_subscription_id > 0)
1013     {
1014       g_source_remove (backend_cups->unsubscribe_general_subscription_id);
1015       backend_cups->unsubscribe_general_subscription_id = 0;
1016     }
1017 
1018   backend_parent_class->dispose (object);
1019 }
1020 
1021 static gboolean
is_address_local(const char * address)1022 is_address_local (const char *address)
1023 {
1024   if (address[0] == '/' ||
1025       strcmp (address, "127.0.0.1") == 0 ||
1026       strcmp (address, "[::1]") == 0)
1027     return TRUE;
1028   else
1029     return FALSE;
1030 }
1031 
1032 static void
gtk_print_backend_cups_set_password(GtkPrintBackend * backend,char ** auth_info_required,char ** auth_info,gboolean store_auth_info)1033 gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
1034                                      char            **auth_info_required,
1035                                      char            **auth_info,
1036                                      gboolean          store_auth_info)
1037 {
1038   GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
1039   GList *l;
1040   char   dispatch_hostname[HTTP_MAX_URI];
1041   char *username = NULL;
1042   char *hostname = NULL;
1043   char *password = NULL;
1044   int    length;
1045   int    i;
1046 
1047   length = g_strv_length (auth_info_required);
1048 
1049   if (auth_info != NULL)
1050     for (i = 0; i < length; i++)
1051       {
1052         if (g_strcmp0 (auth_info_required[i], "username") == 0)
1053           username = g_strdup (auth_info[i]);
1054         else if (g_strcmp0 (auth_info_required[i], "hostname") == 0)
1055           hostname = g_strdup (auth_info[i]);
1056         else if (g_strcmp0 (auth_info_required[i], "password") == 0)
1057           password = g_strdup (auth_info[i]);
1058       }
1059 
1060   if (hostname != NULL && username != NULL && password != NULL)
1061     {
1062       char *key = g_strconcat (username, "@", hostname, NULL);
1063       g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
1064       GTK_NOTE (PRINTING,
1065                 g_print ("CUPS backend: caching password for %s\n", key));
1066     }
1067 
1068   g_free (cups_backend->username);
1069   cups_backend->username = g_strdup (username);
1070 
1071 
1072   for (l = cups_backend->requests; l; l = l->next)
1073     {
1074       GtkPrintCupsDispatchWatch *dispatch = l->data;
1075 
1076       httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
1077       if (is_address_local (dispatch_hostname))
1078         strcpy (dispatch_hostname, "localhost");
1079 
1080       if (dispatch->request->need_auth_info)
1081         {
1082           if (auth_info != NULL)
1083             {
1084               dispatch->request->auth_info = g_new0 (char *, length + 1);
1085               for (i = 0; i < length; i++)
1086                 dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
1087             }
1088           /* Save the password if the user requested it */
1089           if (password != NULL && store_auth_info)
1090             {
1091               const char *printer_uri =
1092                   gtk_cups_request_ipp_get_string (dispatch->request,
1093                                                    IPP_TAG_URI,
1094                                                    "printer-uri");
1095 
1096               gtk_cups_secrets_service_store (auth_info, auth_info_required,
1097                                               printer_uri);
1098             }
1099           dispatch->backend->authentication_lock = FALSE;
1100           dispatch->request->need_auth_info = FALSE;
1101         }
1102       else if (dispatch->request->password_state == GTK_CUPS_PASSWORD_REQUESTED || auth_info == NULL)
1103         {
1104           overwrite_and_free (dispatch->request->password);
1105           dispatch->request->password = g_strdup (password);
1106           g_free (dispatch->request->username);
1107           dispatch->request->username = g_strdup (username);
1108           dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
1109           dispatch->backend->authentication_lock = FALSE;
1110         }
1111     }
1112 }
1113 
1114 static gboolean
request_password(gpointer data)1115 request_password (gpointer data)
1116 {
1117   GtkPrintCupsDispatchWatch *dispatch = data;
1118   const char                *username;
1119   char                      *password;
1120   char                      *prompt = NULL;
1121   char                      *key = NULL;
1122   char                       hostname[HTTP_MAX_URI];
1123   char                     **auth_info_required;
1124   char                     **auth_info_default;
1125   char                     **auth_info_display;
1126   gboolean                  *auth_info_visible;
1127   int                        length = 3;
1128   int                        i;
1129 
1130   if (dispatch->backend->authentication_lock)
1131     return G_SOURCE_REMOVE;
1132 
1133   httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
1134   if (is_address_local (hostname))
1135     strcpy (hostname, "localhost");
1136 
1137   if (dispatch->backend->username != NULL)
1138     username = dispatch->backend->username;
1139   else
1140     username = cupsUser ();
1141 
1142   auth_info_required = g_new0 (char *, length + 1);
1143   auth_info_required[0] = g_strdup ("hostname");
1144   auth_info_required[1] = g_strdup ("username");
1145   auth_info_required[2] = g_strdup ("password");
1146 
1147   auth_info_default = g_new0 (char *, length + 1);
1148   auth_info_default[0] = g_strdup (hostname);
1149   auth_info_default[1] = g_strdup (username);
1150 
1151   auth_info_display = g_new0 (char *, length + 1);
1152   auth_info_display[1] = g_strdup (_("Username:"));
1153   auth_info_display[2] = g_strdup (_("Password:"));
1154 
1155   auth_info_visible = g_new0 (gboolean, length + 1);
1156   auth_info_visible[1] = TRUE;
1157 
1158   key = g_strconcat (username, "@", hostname, NULL);
1159   password = g_hash_table_lookup (dispatch->backend->auth, key);
1160 
1161   if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
1162     {
1163       GTK_NOTE (PRINTING,
1164                 g_print ("CUPS backend: using stored password for %s\n", key));
1165 
1166       overwrite_and_free (dispatch->request->password);
1167       dispatch->request->password = g_strdup (password);
1168       g_free (dispatch->request->username);
1169       dispatch->request->username = g_strdup (username);
1170       dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
1171     }
1172   else
1173     {
1174       const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
1175       const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
1176       char *printer_name = NULL;
1177 
1178       if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
1179         printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
1180 
1181       if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
1182         g_hash_table_remove (dispatch->backend->auth, key);
1183 
1184       dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
1185 
1186       dispatch->backend->authentication_lock = TRUE;
1187 
1188       switch ((guint)ippGetOperation (dispatch->request->ipp_request))
1189         {
1190           case IPP_PRINT_JOB:
1191             if (job_title != NULL && printer_name != NULL)
1192               prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
1193             else
1194               prompt = g_strdup_printf ( _("Authentication is required to print a document on %s"), hostname);
1195             break;
1196           case IPP_GET_JOB_ATTRIBUTES:
1197             if (job_title != NULL)
1198               prompt = g_strdup_printf ( _("Authentication is required to get attributes of job “%s”"), job_title);
1199             else
1200               prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
1201             break;
1202           case IPP_GET_PRINTER_ATTRIBUTES:
1203             if (printer_name != NULL)
1204               prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
1205             else
1206               prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
1207             break;
1208           case CUPS_GET_DEFAULT:
1209             prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
1210             break;
1211           case CUPS_GET_PRINTERS:
1212             prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
1213             break;
1214           default:
1215             /* work around gcc warning about 0 not being a value for this enum */
1216             if (ippGetOperation (dispatch->request->ipp_request) == 0)
1217               prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
1218             else
1219               prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
1220             break;
1221         }
1222 
1223       g_free (printer_name);
1224 
1225       g_signal_emit_by_name (dispatch->backend, "request-password",
1226                              auth_info_required, auth_info_default,
1227                              auth_info_display, auth_info_visible, prompt,
1228                              FALSE); /* Cups password is only cached not stored. */
1229 
1230       g_free (prompt);
1231     }
1232 
1233   for (i = 0; i < length; i++)
1234     {
1235       g_free (auth_info_required[i]);
1236       g_free (auth_info_default[i]);
1237       g_free (auth_info_display[i]);
1238     }
1239 
1240   g_free (auth_info_required);
1241   g_free (auth_info_default);
1242   g_free (auth_info_display);
1243   g_free (auth_info_visible);
1244   g_free (key);
1245 
1246   return G_SOURCE_REMOVE;
1247 }
1248 
1249 static void
cups_dispatch_add_poll(GSource * source)1250 cups_dispatch_add_poll (GSource *source)
1251 {
1252   GtkPrintCupsDispatchWatch *dispatch;
1253   GtkCupsPollState poll_state;
1254 
1255   dispatch = (GtkPrintCupsDispatchWatch *) source;
1256 
1257   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
1258 
1259   /* Remove the old source if the poll state changed. */
1260   if (poll_state != dispatch->poll_state && dispatch->data_poll != NULL)
1261     {
1262       g_source_remove_poll (source, dispatch->data_poll);
1263       g_free (dispatch->data_poll);
1264       dispatch->data_poll = NULL;
1265     }
1266 
1267   if (dispatch->request->http != NULL)
1268     {
1269       if (dispatch->data_poll == NULL)
1270         {
1271 	  dispatch->data_poll = g_new0 (GPollFD, 1);
1272 	  dispatch->poll_state = poll_state;
1273 
1274 	  if (poll_state == GTK_CUPS_HTTP_READ)
1275 	    dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
1276 	  else if (poll_state == GTK_CUPS_HTTP_WRITE)
1277 	    dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
1278 	  else
1279 	    dispatch->data_poll->events = 0;
1280 
1281           dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
1282           g_source_add_poll (source, dispatch->data_poll);
1283         }
1284     }
1285 }
1286 
1287 static gboolean
check_auth_info(gpointer user_data)1288 check_auth_info (gpointer user_data)
1289 {
1290   GtkPrintCupsDispatchWatch *dispatch;
1291   dispatch = (GtkPrintCupsDispatchWatch *) user_data;
1292 
1293   if (!dispatch->request->need_auth_info)
1294     {
1295       if (dispatch->request->auth_info == NULL)
1296         {
1297           dispatch->callback (GTK_PRINT_BACKEND (dispatch->backend),
1298                               gtk_cups_request_get_result (dispatch->request),
1299                               dispatch->callback_data);
1300           g_source_destroy ((GSource *) dispatch);
1301         }
1302       else
1303         {
1304           int length;
1305           int i;
1306 
1307           length = g_strv_length (dispatch->request->auth_info_required);
1308 
1309           gtk_cups_request_ipp_add_strings (dispatch->request,
1310                                             IPP_TAG_JOB,
1311                                             IPP_TAG_TEXT,
1312                                             "auth-info",
1313                                             length,
1314                                             NULL,
1315                                             (const char * const *) dispatch->request->auth_info);
1316 
1317           g_source_attach ((GSource *) dispatch, NULL);
1318           g_source_unref ((GSource *) dispatch);
1319 
1320           for (i = 0; i < length; i++)
1321             overwrite_and_free (dispatch->request->auth_info[i]);
1322           g_free (dispatch->request->auth_info);
1323           dispatch->request->auth_info = NULL;
1324         }
1325 
1326       return G_SOURCE_REMOVE;
1327     }
1328 
1329   return G_SOURCE_CONTINUE;
1330 }
1331 
1332 static void
lookup_auth_info_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1333 lookup_auth_info_cb (GObject      *source_object,
1334                      GAsyncResult *res,
1335                      gpointer      user_data)
1336 {
1337   GTask                      *task;
1338   GtkPrintCupsDispatchWatch  *dispatch;
1339   char                      **auth_info;
1340   GError                     *error = NULL;
1341   int                         i;
1342 
1343   task = (GTask *) res;
1344   dispatch = user_data;
1345   auth_info = g_task_propagate_pointer (task, &error);
1346 
1347   if (auth_info == NULL)
1348     {
1349       if (error != NULL)
1350         {
1351           GTK_NOTE (PRINTING,
1352                     g_print ("Failed to look up auth info: %s\n", error->message));
1353           g_error_free (error);
1354         }
1355       else
1356         {
1357           /* Error note should have been shown by the function causing this */
1358           GTK_NOTE (PRINTING, g_print ("Failed to look up auth info.\n"));
1359         }
1360       dispatch->backend->authentication_lock = FALSE;
1361       g_object_unref (task);
1362       request_auth_info (dispatch);
1363       return;
1364     }
1365 
1366   gtk_print_backend_cups_set_password (GTK_PRINT_BACKEND (dispatch->backend),
1367                                        dispatch->request->auth_info_required, auth_info,
1368                                        FALSE);
1369   for (i = 0; auth_info[i] != NULL; i++)
1370     {
1371       overwrite_and_free (auth_info[i]);
1372       auth_info[i] = NULL;
1373     }
1374   g_clear_pointer (auth_info, g_free);
1375 
1376   g_object_unref (task);
1377 }
1378 
1379 static void
lookup_auth_info(gpointer user_data)1380 lookup_auth_info (gpointer user_data)
1381 {
1382   GtkPrintCupsDispatchWatch  *dispatch;
1383   gsize                       length,
1384                               i;
1385   gboolean                    need_secret_auth_info = FALSE;
1386   const char                 *printer_uri;
1387 
1388   dispatch = user_data;
1389 
1390   if (dispatch->backend->authentication_lock)
1391     return;
1392 
1393   length = g_strv_length (dispatch->request->auth_info_required);
1394 
1395   for (i = 0; i < length; i++)
1396     {
1397       if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
1398         {
1399           need_secret_auth_info = TRUE;
1400           break;
1401         }
1402     }
1403 
1404   g_idle_add (check_auth_info, user_data);
1405 
1406   if (dispatch->backend->secrets_service_available && need_secret_auth_info)
1407     {
1408       dispatch->backend->authentication_lock = TRUE;
1409       printer_uri = gtk_cups_request_ipp_get_string (dispatch->request,
1410                                                      IPP_TAG_URI,
1411                                                      "printer-uri");
1412       gtk_cups_secrets_service_query_task (dispatch->backend,
1413                                            dispatch->backend->secrets_service_cancellable,
1414                                            lookup_auth_info_cb,
1415                                            dispatch,
1416                                            printer_uri,
1417                                            dispatch->request->auth_info_required);
1418       return;
1419     }
1420 
1421   request_auth_info (user_data);
1422 }
1423 
1424 static gboolean
request_auth_info(gpointer user_data)1425 request_auth_info (gpointer user_data)
1426 {
1427   GtkPrintCupsDispatchWatch  *dispatch;
1428   const char                 *job_title;
1429   const char                 *printer_uri;
1430   char                       *prompt = NULL;
1431   char                       *printer_name = NULL;
1432   int                         length;
1433   int                         i;
1434   gboolean                   *auth_info_visible = NULL;
1435   char                      **auth_info_default = NULL;
1436   char                      **auth_info_display = NULL;
1437 
1438   dispatch = (GtkPrintCupsDispatchWatch *) user_data;
1439 
1440   if (dispatch->backend->authentication_lock)
1441     return FALSE;
1442 
1443   job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
1444   printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
1445   length = g_strv_length (dispatch->request->auth_info_required);
1446 
1447   auth_info_visible = g_new0 (gboolean, length);
1448   auth_info_default = g_new0 (char *, length + 1);
1449   auth_info_display = g_new0 (char *, length + 1);
1450 
1451   for (i = 0; i < length; i++)
1452     {
1453       if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0)
1454         {
1455           auth_info_display[i] = g_strdup (_("Domain:"));
1456           auth_info_default[i] = g_strdup ("WORKGROUP");
1457           auth_info_visible[i] = TRUE;
1458         }
1459       else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0)
1460         {
1461           auth_info_display[i] = g_strdup (_("Username:"));
1462           if (dispatch->backend->username != NULL)
1463             auth_info_default[i] = g_strdup (dispatch->backend->username);
1464           else
1465             auth_info_default[i] = g_strdup (cupsUser ());
1466           auth_info_visible[i] = TRUE;
1467         }
1468       else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
1469         {
1470           auth_info_display[i] = g_strdup (_("Password:"));
1471           auth_info_visible[i] = FALSE;
1472         }
1473     }
1474 
1475   if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
1476     printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
1477 
1478   dispatch->backend->authentication_lock = TRUE;
1479 
1480   if (job_title != NULL)
1481     {
1482       if (printer_name != NULL)
1483         prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
1484       else
1485         prompt = g_strdup_printf ( _("Authentication is required to print document “%s”"), job_title);
1486     }
1487   else
1488     {
1489       if (printer_name != NULL)
1490         prompt = g_strdup_printf ( _("Authentication is required to print this document on printer %s"), printer_name);
1491       else
1492         prompt = g_strdup ( _("Authentication is required to print this document"));
1493     }
1494 
1495   g_signal_emit_by_name (dispatch->backend, "request-password",
1496                          dispatch->request->auth_info_required,
1497                          auth_info_default,
1498                          auth_info_display,
1499                          auth_info_visible,
1500                          prompt,
1501                          dispatch->backend->secrets_service_available);
1502 
1503   for (i = 0; i < length; i++)
1504     {
1505       g_free (auth_info_default[i]);
1506       g_free (auth_info_display[i]);
1507     }
1508 
1509   g_free (auth_info_default);
1510   g_free (auth_info_display);
1511   g_free (printer_name);
1512   g_free (prompt);
1513 
1514   return FALSE;
1515 }
1516 
1517 static gboolean
cups_dispatch_watch_check(GSource * source)1518 cups_dispatch_watch_check (GSource *source)
1519 {
1520   GtkPrintCupsDispatchWatch *dispatch;
1521   GtkCupsPollState poll_state;
1522   gboolean result;
1523 
1524   GTK_NOTE (PRINTING,
1525             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1526 
1527   dispatch = (GtkPrintCupsDispatchWatch *) source;
1528 
1529   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
1530 
1531   if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
1532     if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
1533        return FALSE;
1534 
1535   result = gtk_cups_request_read_write (dispatch->request, FALSE);
1536   if (result && dispatch->data_poll != NULL)
1537     {
1538       g_source_remove_poll (source, dispatch->data_poll);
1539       g_free (dispatch->data_poll);
1540       dispatch->data_poll = NULL;
1541     }
1542 
1543   if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
1544     {
1545       dispatch->request->need_password = FALSE;
1546       g_idle_add (request_password, dispatch);
1547       result = FALSE;
1548     }
1549 
1550   return result;
1551 }
1552 
1553 static gboolean
cups_dispatch_watch_prepare(GSource * source,int * timeout_)1554 cups_dispatch_watch_prepare (GSource *source,
1555 			     int     *timeout_)
1556 {
1557   GtkPrintCupsDispatchWatch *dispatch;
1558   gboolean result;
1559 
1560   dispatch = (GtkPrintCupsDispatchWatch *) source;
1561 
1562   GTK_NOTE (PRINTING,
1563             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1564 
1565   *timeout_ = -1;
1566 
1567   result = gtk_cups_request_read_write (dispatch->request, TRUE);
1568 
1569   cups_dispatch_add_poll (source);
1570 
1571   return result;
1572 }
1573 
1574 static gboolean
cups_dispatch_watch_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)1575 cups_dispatch_watch_dispatch (GSource     *source,
1576 			      GSourceFunc  callback,
1577 			      gpointer     user_data)
1578 {
1579   GtkPrintCupsDispatchWatch *dispatch;
1580   GtkPrintCupsResponseCallbackFunc ep_callback;
1581   GtkCupsResult *result;
1582 
1583   g_assert (callback != NULL);
1584 
1585   ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
1586 
1587   dispatch = (GtkPrintCupsDispatchWatch *) source;
1588 
1589   result = gtk_cups_request_get_result (dispatch->request);
1590 
1591   GTK_NOTE (PRINTING,
1592             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1593 
1594   if (gtk_cups_result_is_error (result))
1595     {
1596       GTK_NOTE (PRINTING,
1597                 g_print("Error result: %s (type %i, status %i, code %i)\n",
1598                         gtk_cups_result_get_error_string (result),
1599                         gtk_cups_result_get_error_type (result),
1600                         gtk_cups_result_get_error_status (result),
1601                         gtk_cups_result_get_error_code (result)));
1602      }
1603 
1604   ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
1605 
1606   return FALSE;
1607 }
1608 
1609 static void
cups_dispatch_watch_finalize(GSource * source)1610 cups_dispatch_watch_finalize (GSource *source)
1611 {
1612   GtkPrintCupsDispatchWatch *dispatch;
1613   GtkCupsResult *result;
1614 
1615   GTK_NOTE (PRINTING,
1616             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1617 
1618   dispatch = (GtkPrintCupsDispatchWatch *) source;
1619 
1620   result = gtk_cups_request_get_result (dispatch->request);
1621   if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
1622     {
1623       const char *username;
1624       char         hostname[HTTP_MAX_URI];
1625       char        *key;
1626 
1627       httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
1628       if (is_address_local (hostname))
1629         strcpy (hostname, "localhost");
1630 
1631       if (dispatch->backend->username != NULL)
1632         username = dispatch->backend->username;
1633       else
1634         username = cupsUser ();
1635 
1636       key = g_strconcat (username, "@", hostname, NULL);
1637       GTK_NOTE (PRINTING,
1638                 g_print ("CUPS backend: removing stored password for %s\n", key));
1639       g_hash_table_remove (dispatch->backend->auth, key);
1640       g_free (key);
1641 
1642       if (dispatch->backend)
1643         dispatch->backend->authentication_lock = FALSE;
1644     }
1645 
1646   gtk_cups_request_free (dispatch->request);
1647 
1648   if (dispatch->backend)
1649     {
1650       /* We need to unref this at idle time, because it might be the
1651        * last reference to this module causing the code to be
1652        * unloaded (including this particular function!)
1653        * Update: Doing this at idle caused a deadlock taking the
1654        * mainloop context lock while being in a GSource callout for
1655        * multithreaded apps. So, for now we just disable unloading
1656        * of print backends. See _gtk_print_backend_create for the
1657        * disabling.
1658        */
1659 
1660       dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
1661 
1662 
1663       g_object_unref (dispatch->backend);
1664       dispatch->backend = NULL;
1665     }
1666 
1667   if (dispatch->data_poll)
1668     {
1669       g_source_remove_poll (source, dispatch->data_poll);
1670       g_free (dispatch->data_poll);
1671       dispatch->data_poll = NULL;
1672     }
1673 }
1674 
1675 static GSourceFuncs _cups_dispatch_watch_funcs = {
1676   cups_dispatch_watch_prepare,
1677   cups_dispatch_watch_check,
1678   cups_dispatch_watch_dispatch,
1679   cups_dispatch_watch_finalize
1680 };
1681 
1682 
1683 static void
cups_request_execute(GtkPrintBackendCups * print_backend,GtkCupsRequest * request,GtkPrintCupsResponseCallbackFunc callback,gpointer user_data,GDestroyNotify notify)1684 cups_request_execute (GtkPrintBackendCups              *print_backend,
1685                       GtkCupsRequest                   *request,
1686                       GtkPrintCupsResponseCallbackFunc  callback,
1687                       gpointer                          user_data,
1688                       GDestroyNotify                    notify)
1689 {
1690   GtkPrintCupsDispatchWatch *dispatch;
1691 
1692   dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
1693                                                          sizeof (GtkPrintCupsDispatchWatch));
1694   g_source_set_static_name (&dispatch->source, "GTK CUPS backend");
1695 
1696   GTK_NOTE (PRINTING,
1697             g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
1698 
1699   dispatch->request = request;
1700   dispatch->backend = g_object_ref (print_backend);
1701   dispatch->poll_state = GTK_CUPS_HTTP_IDLE;
1702   dispatch->data_poll = NULL;
1703   dispatch->callback = NULL;
1704   dispatch->callback_data = NULL;
1705 
1706   print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
1707 
1708   g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
1709 
1710   if (request->need_auth_info)
1711     {
1712       dispatch->callback = callback;
1713       dispatch->callback_data = user_data;
1714       lookup_auth_info (dispatch);
1715     }
1716   else
1717     {
1718       g_source_attach ((GSource *) dispatch, NULL);
1719       g_source_unref ((GSource *) dispatch);
1720     }
1721 }
1722 
1723 typedef struct {
1724   GtkPrintBackendCups *print_backend;
1725   GtkPrintJob *job;
1726   int job_id;
1727   int counter;
1728 } CupsJobPollData;
1729 
1730 static void
job_object_died(gpointer user_data,GObject * where_the_object_was)1731 job_object_died	(gpointer  user_data,
1732 		 GObject  *where_the_object_was)
1733 {
1734   CupsJobPollData *data = user_data;
1735   data->job = NULL;
1736 }
1737 
1738 static void
cups_job_poll_data_free(CupsJobPollData * data)1739 cups_job_poll_data_free (CupsJobPollData *data)
1740 {
1741   if (data->job)
1742     g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
1743 
1744   g_free (data);
1745 }
1746 
1747 static void
cups_request_job_info_cb(GtkPrintBackendCups * print_backend,GtkCupsResult * result,gpointer user_data)1748 cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
1749                           GtkCupsResult       *result,
1750                           gpointer             user_data)
1751 {
1752   CupsJobPollData *data = user_data;
1753   ipp_attribute_t *attr;
1754   ipp_t *response;
1755   int state;
1756   gboolean done;
1757 
1758   if (data->job == NULL)
1759     {
1760       cups_job_poll_data_free (data);
1761       return;
1762     }
1763 
1764   data->counter++;
1765 
1766   response = gtk_cups_result_get_response (result);
1767 
1768   attr = ippFindAttribute (response, "job-state", IPP_TAG_ENUM);
1769   state = ippGetInteger (attr, 0);
1770 
1771   done = FALSE;
1772   switch (state)
1773     {
1774     case IPP_JOB_PENDING:
1775     case IPP_JOB_HELD:
1776     case IPP_JOB_STOPPED:
1777       gtk_print_job_set_status (data->job, GTK_PRINT_STATUS_PENDING);
1778       break;
1779     case IPP_JOB_PROCESSING:
1780       gtk_print_job_set_status (data->job, GTK_PRINT_STATUS_PRINTING);
1781       break;
1782     default:
1783     case IPP_JOB_CANCELLED:
1784     case IPP_JOB_ABORTED:
1785       gtk_print_job_set_status (data->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
1786       done = TRUE;
1787       break;
1788     case 0:
1789     case IPP_JOB_COMPLETED:
1790       gtk_print_job_set_status (data->job, GTK_PRINT_STATUS_FINISHED);
1791       done = TRUE;
1792       break;
1793     }
1794 
1795   if (!done && data->job != NULL)
1796     {
1797       guint32 timeout;
1798       guint id;
1799 
1800       if (data->counter < 5)
1801         timeout = 100;
1802       else if (data->counter < 10)
1803         timeout = 500;
1804       else
1805         timeout = 1000;
1806 
1807       id = g_timeout_add (timeout, cups_job_info_poll_timeout, data);
1808       g_source_set_name_by_id (id, "[gtk] cups_job_info_poll_timeout");
1809     }
1810   else
1811     cups_job_poll_data_free (data);
1812 }
1813 
1814 static void
cups_request_job_info(CupsJobPollData * data)1815 cups_request_job_info (CupsJobPollData *data)
1816 {
1817   GtkCupsRequest *request;
1818   char *job_uri;
1819 
1820   request = gtk_cups_request_new_with_username (NULL,
1821                                                 GTK_CUPS_POST,
1822                                                 IPP_GET_JOB_ATTRIBUTES,
1823                                                 NULL,
1824                                                 NULL,
1825                                                 NULL,
1826                                                 data->print_backend->username);
1827 
1828   job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
1829   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
1830                                    "job-uri", NULL, job_uri);
1831   g_free (job_uri);
1832 
1833   cups_request_execute (data->print_backend,
1834                         request,
1835                         (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
1836                         data,
1837                         NULL);
1838 }
1839 
1840 static gboolean
cups_job_info_poll_timeout(gpointer user_data)1841 cups_job_info_poll_timeout (gpointer user_data)
1842 {
1843   CupsJobPollData *data = user_data;
1844 
1845   if (data->job == NULL)
1846     cups_job_poll_data_free (data);
1847   else
1848     cups_request_job_info (data);
1849 
1850   return G_SOURCE_REMOVE;
1851 }
1852 
1853 static void
cups_begin_polling_info(GtkPrintBackendCups * print_backend,GtkPrintJob * job,int job_id)1854 cups_begin_polling_info (GtkPrintBackendCups *print_backend,
1855 			 GtkPrintJob         *job,
1856 			 int                  job_id)
1857 {
1858   CupsJobPollData *data;
1859 
1860   data = g_new0 (CupsJobPollData, 1);
1861 
1862   data->print_backend = print_backend;
1863   data->job = job;
1864   data->job_id = job_id;
1865   data->counter = 0;
1866 
1867   g_object_weak_ref (G_OBJECT (job), job_object_died, data);
1868 
1869   cups_request_job_info (data);
1870 }
1871 
1872 static void
mark_printer_inactive(GtkPrinter * printer,GtkPrintBackend * backend)1873 mark_printer_inactive (GtkPrinter      *printer,
1874                        GtkPrintBackend *backend)
1875 {
1876   GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
1877   GList          *iter;
1878 
1879   if (cups_printer->is_temporary)
1880     {
1881       /* Do not recreate printers which disappeared from Avahi. */
1882       iter = g_list_find_custom (GTK_PRINT_BACKEND_CUPS (backend)->temporary_queues_removed,
1883                                  gtk_printer_get_name (printer), (GCompareFunc) g_strcmp0);
1884       if (iter == NULL)
1885         {
1886           /* Recreate temporary queue since they are created for 60 seconds only. */
1887           create_temporary_queue (GTK_PRINT_BACKEND_CUPS (backend),
1888                                   gtk_printer_get_name (printer),
1889                                   cups_printer->printer_uri,
1890                                   cups_printer->temporary_queue_device_uri);
1891         }
1892     }
1893   else
1894     {
1895       gtk_printer_set_is_active (printer, FALSE);
1896       g_signal_emit_by_name (backend, "printer-removed", printer);
1897     }
1898 }
1899 
1900 static int
find_printer(GtkPrinter * printer,const char * find_name)1901 find_printer (GtkPrinter  *printer,
1902 	      const char *find_name)
1903 {
1904   const char *printer_name;
1905 
1906   printer_name = gtk_printer_get_name (printer);
1907   return g_ascii_strcasecmp (printer_name, find_name);
1908 }
1909 /* Printer messages we're interested in */
1910 static const char * const printer_messages[] =
1911   {
1912     "toner-low",
1913     "toner-empty",
1914     "developer-low",
1915     "developer-empty",
1916     "marker-supply-low",
1917     "marker-supply-empty",
1918     "cover-open",
1919     "door-open",
1920     "media-low",
1921     "media-empty",
1922     "offline",
1923     "other"
1924   };
1925 
1926 /* Attributes we're interested in for printers */
1927 static const char * const printer_attrs[] =
1928   {
1929     "printer-name",
1930     "printer-uri-supported",
1931     "member-uris",
1932     "printer-location",
1933     "printer-info",
1934     "printer-state-message",
1935     "printer-state-reasons",
1936     "printer-state",
1937     "queued-job-count",
1938     "printer-is-accepting-jobs",
1939     "job-sheets-supported",
1940     "job-sheets-default",
1941     "printer-type",
1942     "auth-info-required",
1943     "number-up-default",
1944     "ipp-versions-supported",
1945     "multiple-document-handling-supported",
1946     "copies-supported",
1947     "number-up-supported",
1948     "device-uri",
1949     "printer-is-temporary"
1950   };
1951 
1952 /* Attributes we're interested in for printers without PPD */
1953 static const char * const printer_attrs_detailed[] =
1954   {
1955     "printer-name",
1956     "printer-uri-supported",
1957     "member-uris",
1958     "printer-location",
1959     "printer-info",
1960     "printer-state-message",
1961     "printer-state-reasons",
1962     "printer-state",
1963     "queued-job-count",
1964     "printer-is-accepting-jobs",
1965     "job-sheets-supported",
1966     "job-sheets-default",
1967     "printer-type",
1968     "auth-info-required",
1969     "number-up-default",
1970     "ipp-versions-supported",
1971     "multiple-document-handling-supported",
1972     "copies-supported",
1973     "number-up-supported",
1974     "media-col-default",
1975     "media-col-supported",
1976     "media-default",
1977     "media-size-supported",
1978     "media-supported",
1979     "media-left-margin-supported",
1980     "media-right-margin-supported",
1981     "media-bottom-margin-supported",
1982     "media-top-margin-supported",
1983     "sides-default",
1984     "sides-supported",
1985     "output-bin-default",
1986     "output-bin-supported",
1987   };
1988 
1989 typedef enum
1990   {
1991     GTK_PRINTER_STATE_LEVEL_NONE = 0,
1992     GTK_PRINTER_STATE_LEVEL_INFO = 1,
1993     GTK_PRINTER_STATE_LEVEL_WARNING = 2,
1994     GTK_PRINTER_STATE_LEVEL_ERROR = 3
1995   } PrinterStateLevel;
1996 
1997 typedef struct
1998 {
1999   float x_dimension;
2000   float y_dimension;
2001 } MediaSize;
2002 
2003 typedef struct
2004 {
2005   const char *printer_name;
2006   const char *printer_uri;
2007   const char *member_uris;
2008   const char *location;
2009   const char *description;
2010   char *state_msg;
2011   const char *reason_msg;
2012   PrinterStateLevel reason_level;
2013   int state;
2014   int job_count;
2015   gboolean is_paused;
2016   gboolean is_accepting_jobs;
2017   const char *default_cover_before;
2018   const char *default_cover_after;
2019   gboolean default_printer;
2020   gboolean got_printer_type;
2021   gboolean remote_printer;
2022   gboolean avahi_printer;
2023   char    *avahi_resource_path;
2024   char   **auth_info_required;
2025   int      default_number_up;
2026   guchar   ipp_version_major;
2027   guchar   ipp_version_minor;
2028   gboolean supports_copies;
2029   gboolean supports_collate;
2030   gboolean supports_number_up;
2031   char     *media_default;
2032   GList    *media_supported;
2033   GList    *media_size_supported;
2034   float     media_bottom_margin_default;
2035   float     media_top_margin_default;
2036   float     media_left_margin_default;
2037   float     media_right_margin_default;
2038   gboolean  media_margin_default_set;
2039   char     *sides_default;
2040   GList    *sides_supported;
2041   char    **covers;
2042   int       number_of_covers;
2043   char     *output_bin_default;
2044   GList    *output_bin_supported;
2045   char     *original_device_uri;
2046   gboolean  is_temporary;
2047 } PrinterSetupInfo;
2048 
2049 static void
printer_setup_info_free(PrinterSetupInfo * info)2050 printer_setup_info_free (PrinterSetupInfo *info)
2051 {
2052   g_free (info->original_device_uri);
2053   g_free (info->state_msg);
2054   g_strfreev (info->covers);
2055   g_slice_free (PrinterSetupInfo, info);
2056 }
2057 
2058 static void
get_ipp_version(const char * ipp_version_string,guchar * ipp_version_major,guchar * ipp_version_minor)2059 get_ipp_version (const char *ipp_version_string,
2060                  guchar     *ipp_version_major,
2061                  guchar     *ipp_version_minor)
2062 {
2063   char **ipp_version_strv;
2064   char   *endptr;
2065 
2066   *ipp_version_major = 1;
2067   *ipp_version_minor = 1;
2068 
2069   if (ipp_version_string)
2070     {
2071       ipp_version_strv = g_strsplit (ipp_version_string, ".", 0);
2072 
2073       if (ipp_version_strv)
2074         {
2075           if (g_strv_length (ipp_version_strv) == 2)
2076             {
2077               *ipp_version_major = (guchar) g_ascii_strtoull (ipp_version_strv[0], &endptr, 10);
2078               if (endptr == ipp_version_strv[0])
2079                 *ipp_version_major = 1;
2080 
2081               *ipp_version_minor = (guchar) g_ascii_strtoull (ipp_version_strv[1], &endptr, 10);
2082               if (endptr == ipp_version_strv[1])
2083                 *ipp_version_minor = 1;
2084             }
2085 
2086           g_strfreev (ipp_version_strv);
2087         }
2088     }
2089 }
2090 
2091 static void
get_server_ipp_version(guchar * ipp_version_major,guchar * ipp_version_minor)2092 get_server_ipp_version (guchar *ipp_version_major,
2093                         guchar *ipp_version_minor)
2094 {
2095   *ipp_version_major = 1;
2096   *ipp_version_minor = 1;
2097 
2098   if (IPP_VERSION && strlen (IPP_VERSION) == 2)
2099     {
2100       *ipp_version_major = (unsigned char) IPP_VERSION[0];
2101       *ipp_version_minor = (unsigned char) IPP_VERSION[1];
2102     }
2103 }
2104 
2105 static int
ipp_version_cmp(guchar ipp_version_major1,guchar ipp_version_minor1,guchar ipp_version_major2,guchar ipp_version_minor2)2106 ipp_version_cmp (guchar ipp_version_major1,
2107                  guchar ipp_version_minor1,
2108                  guchar ipp_version_major2,
2109                  guchar ipp_version_minor2)
2110 {
2111   if (ipp_version_major1 == ipp_version_major2 &&
2112       ipp_version_minor1 == ipp_version_minor2)
2113     {
2114       return 0;
2115     }
2116   else if (ipp_version_major1 < ipp_version_major2 ||
2117            (ipp_version_major1 == ipp_version_major2 &&
2118             ipp_version_minor1 < ipp_version_minor2))
2119     {
2120       return -1;
2121     }
2122   else
2123     {
2124       return 1;
2125     }
2126 }
2127 
2128 static void
cups_printer_handle_attribute(GtkPrintBackendCups * cups_backend,ipp_attribute_t * attr,PrinterSetupInfo * info)2129 cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
2130 			       ipp_attribute_t *attr,
2131 			       PrinterSetupInfo *info)
2132 {
2133   int i, j;
2134   if (strcmp (ippGetName (attr), "printer-name") == 0 &&
2135       ippGetValueTag (attr) == IPP_TAG_NAME)
2136     info->printer_name = ippGetString (attr, 0, NULL);
2137   else if (strcmp (ippGetName (attr), "printer-uri-supported") == 0 &&
2138 	   ippGetValueTag (attr) == IPP_TAG_URI)
2139     info->printer_uri = ippGetString (attr, 0, NULL);
2140   else if (strcmp (ippGetName (attr), "member-uris") == 0 &&
2141 	   ippGetValueTag (attr) == IPP_TAG_URI)
2142     info->member_uris = ippGetString (attr, 0, NULL);
2143   else if (strcmp (ippGetName (attr), "printer-location") == 0)
2144     info->location = ippGetString (attr, 0, NULL);
2145   else if (strcmp (ippGetName (attr), "printer-info") == 0)
2146     info->description = ippGetString (attr, 0, NULL);
2147   else if (strcmp (ippGetName (attr), "printer-state-message") == 0)
2148     info->state_msg = g_strdup (ippGetString (attr, 0, NULL));
2149   else if (strcmp (ippGetName (attr), "printer-state-reasons") == 0)
2150     /* Store most important reason to reason_msg and set
2151        its importance at printer_state_reason_level */
2152     {
2153       for (i = 0; i < ippGetCount (attr); i++)
2154 	{
2155 	  if (strcmp (ippGetString (attr, i, NULL), "none") != 0)
2156 	    {
2157 	      gboolean interested_in = FALSE;
2158 	      /* Sets is_paused flag for paused printer. */
2159 	      if (strcmp (ippGetString (attr, i, NULL), "paused") == 0)
2160 		{
2161 		  info->is_paused = TRUE;
2162 		}
2163 
2164 	      for (j = 0; j < G_N_ELEMENTS (printer_messages); j++)
2165 		if (strncmp (ippGetString (attr, i, NULL), printer_messages[j],
2166 			     strlen (printer_messages[j])) == 0)
2167 		  {
2168 		    interested_in = TRUE;
2169 		    break;
2170 		  }
2171 
2172 	      if (interested_in)
2173 		{
2174 		  if (g_str_has_suffix (ippGetString (attr, i, NULL), "-report"))
2175 		    {
2176 		      if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_INFO)
2177 			{
2178 			  info->reason_msg = ippGetString (attr, i, NULL);
2179 			  info->reason_level = GTK_PRINTER_STATE_LEVEL_INFO;
2180 			}
2181 		    }
2182 		  else if (g_str_has_suffix (ippGetString (attr, i, NULL), "-warning"))
2183 		    {
2184 		      if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_WARNING)
2185 			{
2186 			  info->reason_msg = ippGetString (attr, i, NULL);
2187 			  info->reason_level = GTK_PRINTER_STATE_LEVEL_WARNING;
2188 			}
2189 		    }
2190 		  else  /* It is error in the case of no suffix. */
2191 		    {
2192 		      info->reason_msg = ippGetString (attr, i, NULL);
2193 		      info->reason_level = GTK_PRINTER_STATE_LEVEL_ERROR;
2194 		    }
2195 		}
2196 	    }
2197 	}
2198     }
2199   else if (strcmp (ippGetName (attr), "printer-state") == 0)
2200     info->state = ippGetInteger (attr, 0);
2201   else if (strcmp (ippGetName (attr), "queued-job-count") == 0)
2202     info->job_count = ippGetInteger (attr, 0);
2203   else if (strcmp (ippGetName (attr), "printer-is-accepting-jobs") == 0)
2204     {
2205       if (ippGetBoolean (attr, 0) == 1)
2206 	info->is_accepting_jobs = TRUE;
2207       else
2208 	info->is_accepting_jobs = FALSE;
2209     }
2210   else if (strcmp (ippGetName (attr), "job-sheets-supported") == 0)
2211     {
2212       info->number_of_covers = ippGetCount (attr);
2213       info->covers = g_new (char *, info->number_of_covers + 1);
2214       for (i = 0; i < info->number_of_covers; i++)
2215         info->covers[i] = g_strdup (ippGetString (attr, i, NULL));
2216       info->covers[info->number_of_covers] = NULL;
2217     }
2218   else if (strcmp (ippGetName (attr), "job-sheets-default") == 0)
2219     {
2220       if (ippGetCount (attr) == 2)
2221 	{
2222 	  info->default_cover_before = ippGetString (attr, 0, NULL);
2223 	  info->default_cover_after = ippGetString (attr, 1, NULL);
2224 	}
2225     }
2226   else if (strcmp (ippGetName (attr), "printer-type") == 0)
2227     {
2228       info->got_printer_type = TRUE;
2229       if (ippGetInteger (attr, 0) & 0x00020000)
2230 	info->default_printer = TRUE;
2231       else
2232 	info->default_printer = FALSE;
2233 
2234       if (ippGetInteger (attr, 0) & 0x00000002)
2235 	info->remote_printer = TRUE;
2236       else
2237 	info->remote_printer = FALSE;
2238     }
2239   else if (strcmp (ippGetName (attr), "auth-info-required") == 0)
2240     {
2241       if (strcmp (ippGetString (attr, 0, NULL), "none") != 0)
2242 	{
2243 	  info->auth_info_required = g_new0 (char *, ippGetCount (attr) + 1);
2244 	  for (i = 0; i < ippGetCount (attr); i++)
2245 	    info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
2246 	}
2247     }
2248   else if (strcmp (ippGetName (attr), "number-up-default") == 0)
2249     {
2250       info->default_number_up = ippGetInteger (attr, 0);
2251     }
2252   else if (g_strcmp0 (ippGetName (attr), "ipp-versions-supported") == 0)
2253     {
2254       guchar server_ipp_version_major;
2255       guchar server_ipp_version_minor;
2256       guchar ipp_version_major;
2257       guchar ipp_version_minor;
2258 
2259       get_server_ipp_version (&server_ipp_version_major,
2260                               &server_ipp_version_minor);
2261 
2262       for (i = 0; i < ippGetCount (attr); i++)
2263         {
2264           get_ipp_version (ippGetString (attr, i, NULL),
2265                            &ipp_version_major,
2266                            &ipp_version_minor);
2267 
2268           if (ipp_version_cmp (ipp_version_major,
2269                                ipp_version_minor,
2270                                info->ipp_version_major,
2271                                info->ipp_version_minor) > 0 &&
2272               ipp_version_cmp (ipp_version_major,
2273                                ipp_version_minor,
2274                                server_ipp_version_major,
2275                                server_ipp_version_minor) <= 0)
2276             {
2277               info->ipp_version_major = ipp_version_major;
2278               info->ipp_version_minor = ipp_version_minor;
2279             }
2280         }
2281     }
2282   else if (g_strcmp0 (ippGetName (attr), "number-up-supported") == 0)
2283     {
2284       if (ippGetCount (attr) == 6)
2285         {
2286           info->supports_number_up = TRUE;
2287         }
2288     }
2289   else if (g_strcmp0 (ippGetName (attr), "copies-supported") == 0)
2290     {
2291       int upper = 1;
2292 
2293       ippGetRange (attr, 0, &upper);
2294       if (upper > 1)
2295         {
2296           info->supports_copies = TRUE;
2297         }
2298     }
2299   else if (g_strcmp0 (ippGetName (attr), "multiple-document-handling-supported") == 0)
2300     {
2301       for (i = 0; i < ippGetCount (attr); i++)
2302         {
2303           if (g_strcmp0 (ippGetString (attr, i, NULL), "separate-documents-collated-copies") == 0)
2304             {
2305               info->supports_collate = TRUE;
2306             }
2307         }
2308     }
2309   else if (g_strcmp0 (ippGetName (attr), "sides-default") == 0)
2310     {
2311       info->sides_default = g_strdup (ippGetString (attr, 0, NULL));
2312     }
2313   else if (g_strcmp0 (ippGetName (attr), "sides-supported") == 0)
2314     {
2315       for (i = 0; i < ippGetCount (attr); i++)
2316         info->sides_supported = g_list_prepend (info->sides_supported, g_strdup (ippGetString (attr, i, NULL)));
2317 
2318       info->sides_supported = g_list_reverse (info->sides_supported);
2319     }
2320   else if (g_strcmp0 (ippGetName (attr), "media-default") == 0)
2321     {
2322       if (ippGetValueTag (attr) == IPP_TAG_KEYWORD ||
2323           ippGetValueTag (attr) == IPP_TAG_NAME)
2324         info->media_default = g_strdup (ippGetString (attr, 0, NULL));
2325     }
2326   else if (g_strcmp0 (ippGetName (attr), "media-col-default") == 0)
2327     {
2328       ipp_attribute_t *iter;
2329       ipp_t           *col;
2330       int              num_of_margins = 0;
2331 
2332       for (i = 0; i < ippGetCount (attr); i++)
2333         {
2334           col = ippGetCollection (attr, i);
2335           for (iter = ippFirstAttribute (col); iter != NULL; iter = ippNextAttribute (col))
2336             {
2337               switch ((guint)ippGetValueTag (iter))
2338                 {
2339                   case IPP_TAG_INTEGER:
2340                     if (g_strcmp0 (ippGetName (iter), "media-bottom-margin") == 0)
2341                       {
2342                         info->media_bottom_margin_default = ippGetInteger (iter, 0) / 100.0;
2343                         num_of_margins++;
2344                       }
2345                     else if (g_strcmp0 (ippGetName (iter), "media-top-margin") == 0)
2346                       {
2347                         info->media_top_margin_default = ippGetInteger (iter, 0) / 100.0;
2348                         num_of_margins++;
2349                       }
2350                     else if (g_strcmp0 (ippGetName (iter), "media-left-margin") == 0)
2351                       {
2352                         info->media_left_margin_default = ippGetInteger (iter, 0) / 100.0;
2353                         num_of_margins++;
2354                       }
2355                     else if (g_strcmp0 (ippGetName (iter), "media-right-margin") == 0)
2356                       {
2357                         info->media_right_margin_default = ippGetInteger (iter, 0) / 100.0;
2358                         num_of_margins++;
2359                       }
2360                     break;
2361 
2362                   default:
2363                     break;
2364                 }
2365             }
2366         }
2367 
2368       if (num_of_margins == 4)
2369         info->media_margin_default_set = TRUE;
2370     }
2371   else if (g_strcmp0 (ippGetName (attr), "media-supported") == 0)
2372     {
2373       for (i = 0; i < ippGetCount (attr); i++)
2374         info->media_supported = g_list_prepend (info->media_supported, g_strdup (ippGetString (attr, i, NULL)));
2375 
2376       info->media_supported = g_list_reverse (info->media_supported);
2377     }
2378   else if (g_strcmp0 (ippGetName (attr), "media-size-supported") == 0)
2379     {
2380       ipp_attribute_t *iter;
2381       MediaSize       *media_size;
2382       gboolean         number_of_dimensions;
2383       ipp_t           *media_size_collection;
2384 
2385       for (i = 0; i < ippGetCount (attr); i++)
2386         {
2387           media_size_collection = ippGetCollection (attr, i);
2388           media_size = g_new0 (MediaSize, 1);
2389           number_of_dimensions = 0;
2390 
2391           for (iter = ippFirstAttribute (media_size_collection);
2392                iter != NULL;
2393                iter = ippNextAttribute (media_size_collection))
2394             {
2395               if (g_strcmp0 (ippGetName (iter), "x-dimension") == 0 &&
2396                   ippGetValueTag (iter) == IPP_TAG_INTEGER)
2397                 {
2398                   media_size->x_dimension = ippGetInteger (iter, 0) / 100.0;
2399                   number_of_dimensions++;
2400                 }
2401               else if (g_strcmp0 (ippGetName (iter), "y-dimension") == 0 &&
2402                   ippGetValueTag (iter) == IPP_TAG_INTEGER)
2403                 {
2404                   media_size->y_dimension = ippGetInteger (iter, 0) / 100.0;
2405                   number_of_dimensions++;
2406                 }
2407             }
2408 
2409           if (number_of_dimensions == 2)
2410             info->media_size_supported = g_list_prepend (info->media_size_supported, media_size);
2411           else
2412             g_free (media_size);
2413         }
2414 
2415       info->media_size_supported = g_list_reverse (info->media_size_supported);
2416     }
2417   else if (g_strcmp0 (ippGetName (attr), "output-bin-default") == 0)
2418     {
2419       info->output_bin_default = g_strdup (ippGetString (attr, 0, NULL));
2420     }
2421   else if (g_strcmp0 (ippGetName (attr), "output-bin-supported") == 0)
2422     {
2423       for (i = 0; i < ippGetCount (attr); i++)
2424         info->output_bin_supported = g_list_prepend (info->output_bin_supported, g_strdup (ippGetString (attr, i, NULL)));
2425 
2426       info->output_bin_supported = g_list_reverse (info->output_bin_supported);
2427     }
2428   else if (g_strcmp0 (ippGetName (attr), "device-uri") == 0)
2429     {
2430       info->original_device_uri = g_strdup (ippGetString (attr, 0, NULL));
2431     }
2432   else if (strcmp (ippGetName (attr), "printer-is-temporary") == 0)
2433     {
2434       if (ippGetBoolean (attr, 0) == 1)
2435         info->is_temporary = TRUE;
2436       else
2437         info->is_temporary = FALSE;
2438     }
2439   else
2440     {
2441       GTK_NOTE (PRINTING,
2442 		g_print ("CUPS Backend: Attribute %s ignored\n", ippGetName (attr)));
2443     }
2444 }
2445 
2446 static GtkPrinter*
cups_create_printer(GtkPrintBackendCups * cups_backend,PrinterSetupInfo * info)2447 cups_create_printer (GtkPrintBackendCups *cups_backend,
2448 		     PrinterSetupInfo *info)
2449 {
2450   GtkPrinterCups *cups_printer;
2451   GtkPrinter *printer;
2452   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
2453   char uri[HTTP_MAX_URI];	/* Printer URI */
2454   char method[HTTP_MAX_URI];	/* Method/scheme name */
2455   char username[HTTP_MAX_URI];	/* Username:password */
2456   char hostname[HTTP_MAX_URI];	/* Hostname */
2457   char resource[HTTP_MAX_URI];	/* Resource name */
2458   int  port;			/* Port number */
2459   char *cups_server;            /* CUPS server */
2460 
2461 #ifdef HAVE_COLORD
2462   if (info->avahi_printer)
2463     cups_printer = gtk_printer_cups_new (info->printer_name,
2464 					 backend,
2465 					 NULL);
2466   else
2467     cups_printer = gtk_printer_cups_new (info->printer_name,
2468 					 backend,
2469 					 cups_backend->colord_client);
2470 #else
2471   cups_printer = gtk_printer_cups_new (info->printer_name, backend, NULL);
2472 #endif
2473 
2474   if (!info->avahi_printer)
2475     {
2476       cups_printer->device_uri = g_strdup_printf ("/printers/%s",
2477                                                   info->printer_name);
2478     }
2479 
2480   /* Check to see if we are looking at a class */
2481   if (info->member_uris)
2482     {
2483       cups_printer->printer_uri = g_strdup (info->member_uris);
2484       /* TODO if member_uris is a class we need to recursively find a printer */
2485       GTK_NOTE (PRINTING,
2486 		g_print ("CUPS Backend: Found class with printer %s\n",
2487 			 info->member_uris));
2488     }
2489   else
2490     {
2491       cups_printer->printer_uri = g_strdup (info->printer_uri);
2492       GTK_NOTE (PRINTING,
2493 		g_print ("CUPS Backend: Found printer %s\n",
2494 			 info->printer_uri));
2495     }
2496 
2497   httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
2498 		   method, sizeof (method),
2499 		   username, sizeof (username),
2500 		   hostname, sizeof (hostname),
2501 		   &port,
2502 		   resource, sizeof (resource));
2503 
2504   if (strncmp (resource, "/printers/", 10) == 0)
2505     {
2506       cups_printer->ppd_name = g_strdup (resource + 10);
2507       GTK_NOTE (PRINTING,
2508 		g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, info->printer_name));
2509     }
2510 
2511   gethostname (uri, sizeof (uri));
2512   cups_server = g_strdup (cupsServer());
2513 
2514   if (strcasecmp (uri, hostname) == 0)
2515     strcpy (hostname, "localhost");
2516 
2517   /* if the cups server is local and listening at a unix domain socket
2518    * then use the socket connection
2519    */
2520   if ((strstr (hostname, "localhost") != NULL) &&
2521       (cups_server[0] == '/'))
2522     strcpy (hostname, cups_server);
2523 
2524   g_free (cups_server);
2525 
2526   cups_printer->default_cover_before = g_strdup (info->default_cover_before);
2527   cups_printer->default_cover_after = g_strdup (info->default_cover_after);
2528   cups_printer->original_device_uri = g_strdup (info->original_device_uri);
2529   cups_printer->hostname = g_strdup (hostname);
2530   cups_printer->port = port;
2531 
2532   if (cups_printer->original_device_uri != NULL)
2533     {
2534       httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->original_device_uri,
2535                        method, sizeof (method),
2536                        username, sizeof (username),
2537                        hostname, sizeof (hostname),
2538                        &port,
2539                        resource, sizeof (resource));
2540       cups_printer->original_hostname = g_strdup (hostname);
2541       cups_printer->original_resource = g_strdup (resource);
2542       cups_printer->original_port = port;
2543     }
2544 
2545   if (info->default_number_up > 0)
2546     cups_printer->default_number_up = info->default_number_up;
2547 
2548   cups_printer->auth_info_required = g_strdupv (info->auth_info_required);
2549   g_strfreev (info->auth_info_required);
2550 
2551   printer = GTK_PRINTER (cups_printer);
2552 
2553   if (cups_backend->default_printer != NULL &&
2554       strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
2555     gtk_printer_set_is_default (printer, TRUE);
2556 
2557   cups_printer->avahi_browsed = info->avahi_printer;
2558 
2559   gtk_print_backend_add_printer (backend, printer);
2560   return printer;
2561 }
2562 
2563 static void
set_printer_icon_name_from_info(GtkPrinter * printer,PrinterSetupInfo * info)2564 set_printer_icon_name_from_info (GtkPrinter       *printer,
2565                                  PrinterSetupInfo *info)
2566 {
2567   /* Set printer icon according to importance
2568      (none, report, warning, error - report is omitted). */
2569   if (info->reason_level == GTK_PRINTER_STATE_LEVEL_ERROR)
2570     gtk_printer_set_icon_name (printer, "printer-error");
2571   else if (info->reason_level == GTK_PRINTER_STATE_LEVEL_WARNING)
2572     gtk_printer_set_icon_name (printer, "printer-warning");
2573   else if (gtk_printer_is_paused (printer))
2574     gtk_printer_set_icon_name (printer, "printer-paused");
2575   else
2576     gtk_printer_set_icon_name (printer, "printer");
2577 }
2578 
2579 static char *
get_reason_msg_desc(guint i,const char * printer_name)2580 get_reason_msg_desc (guint i,
2581                      const char *printer_name)
2582 {
2583   char *reason_msg_desc;
2584 
2585   /* The numbers must match the indices in the printer_messages array */
2586   switch (i)
2587     {
2588       case 0:
2589         reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on toner."),
2590                                            printer_name);
2591         break;
2592       case 1:
2593         reason_msg_desc = g_strdup_printf (_("Printer “%s” has no toner left."),
2594                                            printer_name);
2595         break;
2596       case 2:
2597         /* Translators: "Developer" like on photo development context */
2598         reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on developer."),
2599                                            printer_name);
2600         break;
2601       case 3:
2602         /* Translators: "Developer" like on photo development context */
2603         reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of developer."),
2604                                            printer_name);
2605         break;
2606       case 4:
2607         /* Translators: "marker" is one color bin of the printer */
2608         reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on at least one marker supply."),
2609                                            printer_name);
2610         break;
2611       case 5:
2612         /* Translators: "marker" is one color bin of the printer */
2613         reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of at least one marker supply."),
2614                                            printer_name);
2615         break;
2616       case 6:
2617         reason_msg_desc = g_strdup_printf (_("The cover is open on printer “%s”."),
2618                                            printer_name);
2619         break;
2620       case 7:
2621         reason_msg_desc = g_strdup_printf (_("The door is open on printer “%s”."),
2622                                            printer_name);
2623         break;
2624       case 8:
2625         reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on paper."),
2626                                            printer_name);
2627         break;
2628       case 9:
2629         reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of paper."),
2630                                            printer_name);
2631         break;
2632       case 10:
2633         reason_msg_desc = g_strdup_printf (_("Printer “%s” is currently offline."),
2634                                            printer_name);
2635         break;
2636       case 11:
2637         reason_msg_desc = g_strdup_printf (_("There is a problem on printer “%s”."),
2638                                            printer_name);
2639         break;
2640       default:
2641         g_assert_not_reached ();
2642     }
2643 
2644   return reason_msg_desc;
2645 }
2646 
2647 static void
set_info_state_message(PrinterSetupInfo * info)2648 set_info_state_message (PrinterSetupInfo *info)
2649 {
2650   int i;
2651 
2652   if (info->state_msg == NULL || strlen (info->state_msg) == 0)
2653     {
2654       char *tmp_msg2 = NULL;
2655       if (info->is_paused && !info->is_accepting_jobs)
2656         /* Translators: this is a printer status. */
2657         tmp_msg2 = g_strdup ( _("Paused; Rejecting Jobs"));
2658       if (info->is_paused && info->is_accepting_jobs)
2659         /* Translators: this is a printer status. */
2660         tmp_msg2 = g_strdup ( _("Paused"));
2661       if (!info->is_paused && !info->is_accepting_jobs)
2662         /* Translators: this is a printer status. */
2663         tmp_msg2 = g_strdup ( _("Rejecting Jobs"));
2664 
2665       if (tmp_msg2 != NULL)
2666         {
2667           g_free (info->state_msg);
2668           info->state_msg = tmp_msg2;
2669         }
2670     }
2671 
2672   /* Set description of the reason and combine it with printer-state-message. */
2673   if (info->reason_msg)
2674     {
2675       char *reason_msg_desc = NULL;
2676       gboolean found = FALSE;
2677 
2678       for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
2679         {
2680           if (strncmp (info->reason_msg, printer_messages[i],
2681                        strlen (printer_messages[i])) == 0)
2682             {
2683               reason_msg_desc = get_reason_msg_desc (i, info->printer_name);
2684               found = TRUE;
2685               break;
2686             }
2687         }
2688 
2689       if (!found)
2690         info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
2691 
2692       if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
2693         {
2694           if (info->state_msg == NULL || info->state_msg[0] == '\0')
2695             {
2696               g_free (info->state_msg);
2697               info->state_msg = reason_msg_desc;
2698               reason_msg_desc = NULL;
2699             }
2700           else
2701             {
2702               char *tmp_msg = NULL;
2703               /* Translators: this string connects multiple printer states together. */
2704               tmp_msg = g_strjoin ( _("; "), info->state_msg,
2705                                    reason_msg_desc, NULL);
2706               g_free (info->state_msg);
2707               info->state_msg = tmp_msg;
2708             }
2709         }
2710 
2711       g_free (reason_msg_desc);
2712     }
2713 }
2714 
2715 static void
set_default_printer(GtkPrintBackendCups * cups_backend,const char * default_printer_name)2716 set_default_printer (GtkPrintBackendCups *cups_backend,
2717                      const char          *default_printer_name)
2718 {
2719   cups_backend->default_printer = g_strdup (default_printer_name);
2720   cups_backend->got_default_printer = TRUE;
2721 
2722   if (cups_backend->default_printer != NULL)
2723     {
2724       GtkPrinter *default_printer = NULL;
2725       default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
2726                                                         cups_backend->default_printer);
2727       if (default_printer != NULL)
2728         {
2729           gtk_printer_set_is_default (default_printer, TRUE);
2730           g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
2731                                  "printer-status-changed", default_printer);
2732         }
2733     }
2734 }
2735 
2736 typedef struct {
2737   GtkPrinterCups *printer;
2738   http_t         *http;
2739 } RequestPrinterInfoData;
2740 
2741 static void
request_printer_info_data_free(RequestPrinterInfoData * data)2742 request_printer_info_data_free (RequestPrinterInfoData *data)
2743 {
2744   GTK_NOTE (PRINTING,
2745             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2746   httpClose (data->http);
2747   g_object_unref (data->printer);
2748   g_free (data);
2749 }
2750 
2751 static void
cups_request_printer_info_cb(GtkPrintBackendCups * cups_backend,GtkCupsResult * result,gpointer user_data)2752 cups_request_printer_info_cb (GtkPrintBackendCups *cups_backend,
2753                               GtkCupsResult       *result,
2754                               gpointer             user_data)
2755 {
2756   RequestPrinterInfoData *data = (RequestPrinterInfoData *) user_data;
2757   PrinterSetupInfo       *info = g_slice_new0 (PrinterSetupInfo);
2758   GtkPrintBackend        *backend = GTK_PRINT_BACKEND (cups_backend);
2759   ipp_attribute_t        *attr;
2760   GtkPrinter             *printer = g_object_ref (GTK_PRINTER (data->printer));
2761   gboolean                status_changed = FALSE;
2762   ipp_t                  *response;
2763 
2764   GTK_NOTE (PRINTING,
2765             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2766 
2767   if (gtk_cups_result_is_error (result))
2768     {
2769       GTK_NOTE (PRINTING,
2770                 g_warning ("CUPS Backend: Error getting printer info: %s %d %d",
2771                            gtk_cups_result_get_error_string (result),
2772                            gtk_cups_result_get_error_type (result),
2773                            gtk_cups_result_get_error_code (result)));
2774 
2775       goto done;
2776     }
2777 
2778   response = gtk_cups_result_get_response (result);
2779   attr = ippFirstAttribute (response);
2780   while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
2781     attr = ippNextAttribute (response);
2782 
2783   if (attr)
2784     {
2785       while (attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
2786         {
2787           cups_printer_handle_attribute (cups_backend, attr, info);
2788           attr = ippNextAttribute (response);
2789         }
2790 
2791       if (info->printer_name && info->printer_uri)
2792         {
2793           set_info_state_message (info);
2794 
2795           if (info->got_printer_type &&
2796               info->default_printer &&
2797               cups_backend->avahi_default_printer == NULL)
2798             cups_backend->avahi_default_printer = g_strdup (info->printer_name);
2799 
2800           gtk_printer_set_is_paused (printer, info->is_paused);
2801           gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
2802 
2803           GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
2804           GTK_PRINTER_CUPS (printer)->state = info->state;
2805           GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
2806           GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
2807           GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
2808           GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
2809           GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
2810           GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
2811           GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
2812           status_changed = gtk_printer_set_job_count (printer, info->job_count);
2813           status_changed |= gtk_printer_set_location (printer, info->location);
2814           status_changed |= gtk_printer_set_description (printer, info->description);
2815           status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
2816           status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
2817 
2818           set_printer_icon_name_from_info (printer, info);
2819 
2820           GTK_PRINTER_CUPS (printer)->media_default = info->media_default;
2821           GTK_PRINTER_CUPS (printer)->media_supported = info->media_supported;
2822           GTK_PRINTER_CUPS (printer)->media_size_supported = info->media_size_supported;
2823           if (info->media_margin_default_set)
2824             {
2825               GTK_PRINTER_CUPS (printer)->media_margin_default_set = TRUE;
2826               GTK_PRINTER_CUPS (printer)->media_bottom_margin_default = info->media_bottom_margin_default;
2827               GTK_PRINTER_CUPS (printer)->media_top_margin_default = info->media_top_margin_default;
2828               GTK_PRINTER_CUPS (printer)->media_left_margin_default = info->media_left_margin_default;
2829               GTK_PRINTER_CUPS (printer)->media_right_margin_default = info->media_right_margin_default;
2830             }
2831           GTK_PRINTER_CUPS (printer)->sides_default = info->sides_default;
2832           GTK_PRINTER_CUPS (printer)->sides_supported = info->sides_supported;
2833           GTK_PRINTER_CUPS (printer)->output_bin_default = info->output_bin_default;
2834           GTK_PRINTER_CUPS (printer)->output_bin_supported = info->output_bin_supported;
2835 
2836           GTK_PRINTER_CUPS (printer)->is_temporary = info->is_temporary;
2837 
2838           gtk_printer_set_has_details (printer, TRUE);
2839           g_signal_emit_by_name (printer, "details-acquired", TRUE);
2840 
2841           if (status_changed)
2842             g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
2843                                    "printer-status-changed", printer);
2844         }
2845     }
2846 
2847 done:
2848   g_object_unref (printer);
2849 
2850   if (!cups_backend->got_default_printer &&
2851       gtk_print_backend_printer_list_is_done (backend) &&
2852       cups_backend->avahi_default_printer != NULL)
2853     {
2854       set_default_printer (cups_backend, cups_backend->avahi_default_printer);
2855     }
2856 
2857   printer_setup_info_free (info);
2858 }
2859 
2860 static void
cups_request_printer_info(GtkPrinterCups * printer)2861 cups_request_printer_info (GtkPrinterCups *printer)
2862 {
2863   RequestPrinterInfoData *data;
2864   GtkPrintBackendCups    *backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (GTK_PRINTER (printer)));
2865   GtkCupsRequest         *request;
2866   http_t                 *http;
2867 
2868   http = httpConnect2 (printer->hostname, printer->port, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
2869   if (http)
2870     {
2871       data = g_new0 (RequestPrinterInfoData, 1);
2872       data->http = http;
2873       data->printer = g_object_ref (printer);
2874 
2875       request = gtk_cups_request_new_with_username (http,
2876                                                     GTK_CUPS_POST,
2877                                                     IPP_GET_PRINTER_ATTRIBUTES,
2878                                                     NULL,
2879                                                     NULL,
2880                                                     NULL,
2881                                                     backend->username);
2882 
2883       gtk_cups_request_set_ipp_version (request, 1, 1);
2884 
2885       gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
2886                                        "printer-uri", NULL, printer->printer_uri);
2887 
2888       gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2889                                         "requested-attributes", G_N_ELEMENTS (printer_attrs_detailed),
2890                                         NULL, printer_attrs_detailed);
2891 
2892       cups_request_execute (backend,
2893                             request,
2894                             (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
2895                             data,
2896                             (GDestroyNotify) request_printer_info_data_free);
2897     }
2898 }
2899 
2900 typedef struct
2901 {
2902   char                *printer_uri;
2903   char                *device_uri;
2904   char                *location;
2905   char                *address;
2906   char                *hostname;
2907   int                  port;
2908   char                *printer_name;
2909   char                *name;
2910   char                *resource_path;
2911   gboolean             got_printer_type;
2912   guint                printer_type;
2913   gboolean             got_printer_state;
2914   guint                printer_state;
2915   char                *type;
2916   char                *domain;
2917   char                *UUID;
2918   GtkPrintBackendCups *backend;
2919 } AvahiConnectionTestData;
2920 
2921 static GtkPrinter *
find_printer_by_uuid(GtkPrintBackendCups * backend,const char * UUID)2922 find_printer_by_uuid (GtkPrintBackendCups *backend,
2923                       const char          *UUID)
2924 {
2925   GtkPrinterCups *printer;
2926   GtkPrinter     *result = NULL;
2927   GList          *printers;
2928   GList          *iter;
2929   char           *printer_uuid;
2930 
2931   printers = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
2932   for (iter = printers; iter != NULL; iter = iter->next)
2933     {
2934       printer = GTK_PRINTER_CUPS (iter->data);
2935       if (printer->original_device_uri != NULL)
2936         {
2937           printer_uuid = g_strrstr (printer->original_device_uri, "uuid=");
2938           if (printer_uuid != NULL && strlen (printer_uuid) >= 41)
2939             {
2940               printer_uuid += 5;
2941               printer_uuid = g_strndup (printer_uuid, 36);
2942 
2943               if (g_uuid_string_is_valid (printer_uuid))
2944                 {
2945                   if (g_strcmp0 (printer_uuid, UUID) == 0)
2946                     {
2947                       result = GTK_PRINTER (printer);
2948                       g_free (printer_uuid);
2949                       break;
2950                     }
2951                 }
2952 
2953               g_free (printer_uuid);
2954             }
2955         }
2956     }
2957 
2958   g_list_free (printers);
2959 
2960   return result;
2961 }
2962 
2963 static void
cups_create_local_printer_cb(GtkPrintBackendCups * print_backend,GtkCupsResult * result,gpointer user_data)2964 cups_create_local_printer_cb (GtkPrintBackendCups *print_backend,
2965                               GtkCupsResult       *result,
2966                               gpointer             user_data)
2967 {
2968   ipp_attribute_t *attr;
2969   gchar           *printer_name = NULL;
2970   ipp_t           *response;
2971   GList           *iter;
2972 
2973   response = gtk_cups_result_get_response (result);
2974 
2975   if (ippGetStatusCode (response) <= IPP_OK_CONFLICT)
2976     {
2977       if ((attr = ippFindAttribute (response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
2978         {
2979           printer_name = g_strdup (g_strrstr (ippGetString (attr, 0, NULL), "/") + 1);
2980         }
2981 
2982       GTK_NOTE (PRINTING,
2983                 g_print ("CUPS Backend: Created local printer %s\n", printer_name));
2984     }
2985   else
2986     {
2987       GTK_NOTE (PRINTING,
2988                 g_print ("CUPS Backend: Creating of local printer failed: %d\n", ippGetStatusCode (response)));
2989     }
2990 
2991   iter = g_list_find_custom (print_backend->temporary_queues_in_construction, printer_name, (GCompareFunc) g_strcmp0);
2992   if (iter != NULL)
2993     {
2994       g_free (iter->data);
2995       print_backend->temporary_queues_in_construction = g_list_delete_link (print_backend->temporary_queues_in_construction, iter);
2996     }
2997 
2998   g_free (printer_name);
2999 }
3000 
3001 /*
3002  *  Create CUPS temporary queue.
3003  */
3004 static void
create_temporary_queue(GtkPrintBackendCups * backend,const gchar * printer_name,const gchar * printer_uri,const gchar * device_uri)3005 create_temporary_queue (GtkPrintBackendCups *backend,
3006                         const gchar         *printer_name,
3007                         const gchar         *printer_uri,
3008                         const gchar         *device_uri)
3009 {
3010   GtkCupsRequest *request;
3011   GList          *iter;
3012 
3013   /* There can be several queues with the same name (ipp and ipps versions of the same printer) */
3014   iter = g_list_find_custom (backend->temporary_queues_in_construction, printer_name, (GCompareFunc) g_strcmp0);
3015   if (iter != NULL)
3016     return;
3017 
3018   GTK_NOTE (PRINTING,
3019             g_print ("CUPS Backend: Creating local printer %s\n", printer_name));
3020 
3021   backend->temporary_queues_in_construction = g_list_prepend (backend->temporary_queues_in_construction, g_strdup (printer_name));
3022 
3023   request = gtk_cups_request_new_with_username (NULL,
3024                                                 GTK_CUPS_POST,
3025                                                 IPP_OP_CUPS_CREATE_LOCAL_PRINTER,
3026                                                 NULL,
3027                                                 NULL,
3028                                                 NULL,
3029                                                 NULL);
3030 
3031   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
3032                                    "printer-uri", NULL, printer_uri);
3033   gtk_cups_request_ipp_add_string (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3034                                    "printer-name", NULL, printer_name);
3035   gtk_cups_request_ipp_add_string (request, IPP_TAG_PRINTER, IPP_TAG_URI,
3036                                    "device-uri", NULL, device_uri);
3037 
3038   cups_request_execute (backend,
3039                         request,
3040                         (GtkPrintCupsResponseCallbackFunc) cups_create_local_printer_cb,
3041                         NULL,
3042                         NULL);
3043 }
3044 
3045 /*
3046  *  Create new GtkPrinter from information included in TXT records.
3047  */
3048 static void
create_cups_printer_from_avahi_data(AvahiConnectionTestData * data)3049 create_cups_printer_from_avahi_data (AvahiConnectionTestData *data)
3050 {
3051   PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
3052   GtkPrinter       *printer;
3053 
3054   printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (data->backend), data->printer_name);
3055   if (printer != NULL)
3056     {
3057       /* A printer with this name is already present in this backend. It is probably the same printer
3058        * on another protocol (IPv4 vs IPv6).
3059        */
3060       return;
3061     }
3062 
3063   info->avahi_printer = TRUE;
3064   info->printer_name = data->printer_name;
3065   info->printer_uri = data->printer_uri;
3066   info->avahi_resource_path = data->resource_path;
3067   info->default_printer = FALSE;
3068   info->remote_printer = TRUE;
3069   info->is_accepting_jobs = TRUE;
3070 
3071   if (data->got_printer_state)
3072     {
3073       info->state = data->printer_state;
3074       info->is_paused = info->state == IPP_PRINTER_STOPPED;
3075     }
3076 
3077   info->got_printer_type = data->got_printer_type;
3078   if (data->got_printer_type)
3079     {
3080       if (data->printer_type & CUPS_PRINTER_DEFAULT)
3081         info->default_printer = TRUE;
3082       else
3083         info->default_printer = FALSE;
3084 
3085       if (data->printer_type & CUPS_PRINTER_REMOTE)
3086         info->remote_printer = TRUE;
3087       else
3088         info->remote_printer = FALSE;
3089 
3090       if (data->printer_type & CUPS_PRINTER_REJECTING)
3091         info->is_accepting_jobs = FALSE;
3092       else
3093         info->is_accepting_jobs = TRUE;
3094 
3095       if (info->default_printer &&
3096           data->backend->avahi_default_printer == NULL)
3097         data->backend->avahi_default_printer = g_strdup (info->printer_name);
3098     }
3099 
3100   set_info_state_message (info);
3101 
3102   printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (data->backend), data->printer_name);
3103   if (printer == NULL && data->UUID != NULL)
3104     printer = find_printer_by_uuid (data->backend, data->UUID);
3105 
3106   if (printer == NULL)
3107     {
3108       printer = cups_create_printer (data->backend, info);
3109 
3110       if (data->got_printer_type)
3111         {
3112           gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
3113           GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
3114 
3115           if (info->default_printer &&
3116               data->backend->avahi_default_printer == NULL)
3117             data->backend->avahi_default_printer = g_strdup (info->printer_name);
3118         }
3119 
3120       if (data->got_printer_state)
3121         GTK_PRINTER_CUPS (printer)->state = info->state;
3122 
3123       GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (data->name);
3124       GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (data->type);
3125       GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (data->domain);
3126       GTK_PRINTER_CUPS (printer)->printer_uri = g_strdup (data->printer_uri);
3127       GTK_PRINTER_CUPS (printer)->temporary_queue_device_uri = g_strdup (data->device_uri);
3128       g_free (GTK_PRINTER_CUPS (printer)->hostname);
3129       GTK_PRINTER_CUPS (printer)->hostname = g_strdup (data->hostname);
3130       GTK_PRINTER_CUPS (printer)->port = data->port;
3131       gtk_printer_set_location (printer, data->location);
3132       gtk_printer_set_state_message (printer, info->state_msg);
3133 
3134       set_printer_icon_name_from_info (printer, info);
3135 
3136       if (!gtk_printer_is_active (printer))
3137         gtk_printer_set_is_active (printer, TRUE);
3138 
3139       g_signal_emit_by_name (data->backend, "printer-added", printer);
3140       gtk_printer_set_is_new (printer, FALSE);
3141       g_signal_emit_by_name (data->backend, "printer-list-changed");
3142 
3143       if (!data->backend->got_default_printer &&
3144           gtk_print_backend_printer_list_is_done (GTK_PRINT_BACKEND (data->backend)) &&
3145           data->backend->avahi_default_printer != NULL)
3146         set_default_printer (data->backend, data->backend->avahi_default_printer);
3147 
3148       /* The ref is held by GtkPrintBackend, in add_printer() */
3149       g_object_unref (printer);
3150     }
3151 
3152   printer_setup_info_free (info);
3153 }
3154 
3155 static void
avahi_connection_test_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3156 avahi_connection_test_cb (GObject      *source_object,
3157                           GAsyncResult *res,
3158                           gpointer      user_data)
3159 {
3160   AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
3161   GSocketConnection       *connection;
3162   GError                  *error = NULL;
3163 
3164   connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
3165                                                        res,
3166                                                        &error);
3167   g_object_unref (source_object);
3168 
3169   if (connection != NULL)
3170     {
3171       g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
3172       g_object_unref (connection);
3173 
3174       create_cups_printer_from_avahi_data (data);
3175     }
3176   else
3177     {
3178       GTK_NOTE (PRINTING,
3179                 g_warning ("CUPS Backend: Can not connect to %s: %s\n",
3180                            data->address,
3181                            error->message));
3182       g_error_free (error);
3183     }
3184 
3185   g_free (data->printer_uri);
3186   g_free (data->location);
3187   g_free (data->address);
3188   g_free (data->hostname);
3189   g_free (data->printer_name);
3190   g_free (data->name);
3191   g_free (data->resource_path);
3192   g_free (data->type);
3193   g_free (data->domain);
3194   g_free (data->device_uri);
3195   g_free (data);
3196 }
3197 
3198 static gboolean
avahi_txt_get_key_value_pair(const char * entry,char ** key,char ** value)3199 avahi_txt_get_key_value_pair (const char   *entry,
3200                               char        **key,
3201                               char        **value)
3202 {
3203   const char *equal_sign;
3204 
3205   *key = NULL;
3206   *value = NULL;
3207 
3208   if (entry != NULL)
3209     {
3210       /* See RFC 6763 section 6.3 */
3211       equal_sign = strstr (entry, "=");
3212 
3213       if (equal_sign != NULL)
3214         {
3215           *key = g_strndup (entry, equal_sign - entry);
3216           *value = g_strdup (equal_sign + 1);
3217 
3218           return TRUE;
3219         }
3220     }
3221 
3222   return FALSE;
3223 }
3224 
3225 static void
avahi_service_resolver_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3226 avahi_service_resolver_cb (GObject      *source_object,
3227                            GAsyncResult *res,
3228                            gpointer      user_data)
3229 {
3230   AvahiConnectionTestData *data;
3231   GtkPrintBackendCups     *backend;
3232   const char              *name;
3233   const char              *hostname;
3234   const char              *type;
3235   const char              *domain;
3236   const char              *address;
3237   GVariant                *output;
3238   GVariant                *txt;
3239   GVariant                *child;
3240   guint32                  flags;
3241   guint16                  port;
3242   GError                  *error = NULL;
3243   GList                   *iter;
3244   char                    *tmp;
3245   char                    *printer_name;
3246   char                   **printer_name_strv;
3247   char                   **printer_name_compressed_strv;
3248   char                    *endptr;
3249   char                    *key;
3250   char                    *value;
3251   gsize                    length;
3252   int                      interface;
3253   int                      protocol;
3254   int                      aprotocol;
3255   int                      i, j;
3256 
3257   output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
3258                                           res,
3259                                           &error);
3260   if (output)
3261     {
3262       backend = GTK_PRINT_BACKEND_CUPS (user_data);
3263 
3264       g_variant_get (output, "(ii&s&s&s&si&sq@aayu)",
3265                      &interface,
3266                      &protocol,
3267                      &name,
3268                      &type,
3269                      &domain,
3270                      &hostname,
3271                      &aprotocol,
3272                      &address,
3273                      &port,
3274                      &txt,
3275                      &flags);
3276 
3277       data = g_new0 (AvahiConnectionTestData, 1);
3278 
3279       for (i = 0; i < g_variant_n_children (txt); i++)
3280         {
3281           child = g_variant_get_child_value (txt, i);
3282 
3283           length = g_variant_get_size (child);
3284           if (length > 0)
3285             {
3286               tmp = g_strndup (g_variant_get_data (child), length);
3287               g_variant_unref (child);
3288 
3289               if (!avahi_txt_get_key_value_pair (tmp, &key, &value))
3290                 {
3291                   g_free (tmp);
3292                   continue;
3293                 }
3294 
3295               if (g_strcmp0 (key, "rp") == 0)
3296                 {
3297                   data->resource_path = g_strdup (value);
3298                 }
3299               else if (g_strcmp0 (key, "note") == 0)
3300                 {
3301                   data->location = g_strdup (value);
3302                 }
3303               else if (g_strcmp0 (key, "printer-type") == 0)
3304                 {
3305                   endptr = NULL;
3306                   data->printer_type = g_ascii_strtoull (value, &endptr, 16);
3307                   if (data->printer_type != 0 || endptr != value)
3308                     data->got_printer_type = TRUE;
3309                 }
3310               else if (g_strcmp0 (key, "printer-state") == 0)
3311                 {
3312                   endptr = NULL;
3313                   data->printer_state = g_ascii_strtoull (value, &endptr, 10);
3314                   if (data->printer_state != 0 || endptr != value)
3315                     data->got_printer_state = TRUE;
3316                 }
3317               else if (g_strcmp0 (key, "UUID") == 0)
3318                 {
3319                   if (*value != '\0')
3320                     data->UUID = g_strdup (value);
3321                 }
3322 
3323               g_clear_pointer (&key, g_free);
3324               g_clear_pointer (&value, g_free);
3325               g_free (tmp);
3326             }
3327           else
3328             {
3329               g_variant_unref (child);
3330             }
3331         }
3332 
3333       if (data->resource_path != NULL)
3334         {
3335           /*
3336            * Create name of temporary queue from the name of the discovered service.
3337            * This emulates the way how CUPS creates the name.
3338            */
3339           printer_name = g_strdup_printf ("%s", name);
3340           g_strcanon (printer_name, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", '_');
3341 
3342           printer_name_strv = g_strsplit_set (printer_name, "_", -1);
3343           printer_name_compressed_strv = g_new0 (gchar *, g_strv_length (printer_name_strv) + 1);
3344           for (i = 0, j = 0; printer_name_strv[i] != NULL; i++)
3345             {
3346               if (printer_name_strv[i][0] != '\0')
3347                 {
3348                   printer_name_compressed_strv[j] = printer_name_strv[i];
3349                   j++;
3350                 }
3351             }
3352 
3353           data->printer_name = g_strjoinv ("_", printer_name_compressed_strv);
3354 
3355 
3356           g_strfreev (printer_name_strv);
3357           g_free (printer_name_compressed_strv);
3358           g_free (printer_name);
3359           iter = g_list_find_custom (backend->temporary_queues_removed, data->printer_name, (GCompareFunc) g_strcmp0);
3360           if (iter != NULL)
3361             {
3362               g_free (iter->data);
3363               backend->temporary_queues_removed = g_list_delete_link (backend->temporary_queues_removed, iter);
3364             }
3365 
3366           if (g_strcmp0 (type, "_ipp._tcp") == 0)
3367             {
3368               data->printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", data->printer_name);
3369               data->device_uri = g_strdup_printf ("ipp://%s:%d/%s", hostname, port, data->resource_path);
3370             }
3371           else
3372             {
3373               data->printer_uri = g_strdup_printf ("ipps://localhost/printers/%s", data->printer_name);
3374               data->device_uri = g_strdup_printf ("ipps://%s:%d/%s", hostname, port, data->resource_path);
3375             }
3376 
3377           data->address = g_strdup (address);
3378           data->hostname = g_strdup (hostname);
3379           data->port = port;
3380 
3381           data->name = g_strdup (name);
3382           data->type = g_strdup (type);
3383           data->domain = g_strdup (domain);
3384           data->backend = backend;
3385 
3386           /* It can happen that the address is not reachable */
3387           g_socket_client_connect_to_host_async (g_socket_client_new (),
3388                                                  address,
3389                                                  port,
3390                                                  backend->avahi_cancellable,
3391                                                  avahi_connection_test_cb,
3392                                                  data);
3393         }
3394       else
3395         {
3396           g_free (data->printer_name);
3397           g_free (data->location);
3398           g_free (data);
3399         }
3400 
3401       g_variant_unref (txt);
3402       g_variant_unref (output);
3403     }
3404   else
3405     {
3406       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
3407         g_warning ("%s", error->message);
3408       g_error_free (error);
3409     }
3410 }
3411 
3412 static void
avahi_service_browser_signal_handler(GDBusConnection * connection,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer user_data)3413 avahi_service_browser_signal_handler (GDBusConnection *connection,
3414                                       const char      *sender_name,
3415                                       const char      *object_path,
3416                                       const char      *interface_name,
3417                                       const char      *signal_name,
3418                                       GVariant        *parameters,
3419                                       gpointer         user_data)
3420 {
3421   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
3422   char                *name;
3423   char                *type;
3424   char                *domain;
3425   guint                flags;
3426   int                  interface;
3427   int                  protocol;
3428 
3429   if (g_strcmp0 (signal_name, "ItemNew") == 0)
3430     {
3431       g_variant_get (parameters, "(ii&s&s&su)",
3432                      &interface,
3433                      &protocol,
3434                      &name,
3435                      &type,
3436                      &domain,
3437                      &flags);
3438 
3439       if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
3440           g_strcmp0 (type, "_ipps._tcp") == 0)
3441         {
3442           g_dbus_connection_call (backend->dbus_connection,
3443                                   AVAHI_BUS,
3444                                   "/",
3445                                   AVAHI_SERVER_IFACE,
3446                                   "ResolveService",
3447                                   g_variant_new ("(iisssiu)",
3448                                                  interface,
3449                                                  protocol,
3450                                                  name,
3451                                                  type,
3452                                                  domain,
3453                                                  AVAHI_PROTO_UNSPEC,
3454                                                  0),
3455                                   G_VARIANT_TYPE ("(iissssisqaayu)"),
3456                                   G_DBUS_CALL_FLAGS_NONE,
3457                                   -1,
3458                                   backend->avahi_cancellable,
3459                                   avahi_service_resolver_cb,
3460                                   user_data);
3461         }
3462     }
3463   else if (g_strcmp0 (signal_name, "ItemRemove") == 0)
3464     {
3465       g_variant_get (parameters, "(ii&s&s&su)",
3466                      &interface,
3467                      &protocol,
3468                      &name,
3469                      &type,
3470                      &domain,
3471                      &flags);
3472 
3473       if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
3474           g_strcmp0 (type, "_ipps._tcp") == 0)
3475         {
3476           GtkPrinterCups *printer;
3477           GList          *list;
3478           GList          *iter;
3479 
3480           list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
3481           for (iter = list; iter; iter = iter->next)
3482             {
3483               printer = GTK_PRINTER_CUPS (iter->data);
3484               if (g_strcmp0 (printer->avahi_name, name) == 0 &&
3485                   g_strcmp0 (printer->avahi_type, type) == 0 &&
3486                   g_strcmp0 (printer->avahi_domain, domain) == 0)
3487                 {
3488                   if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
3489                                  backend->avahi_default_printer) == 0)
3490                     g_clear_pointer (&backend->avahi_default_printer, g_free);
3491 
3492                   backend->temporary_queues_removed = g_list_prepend (backend->temporary_queues_removed,
3493                     g_strdup (gtk_printer_get_name (GTK_PRINTER (printer))));
3494 
3495                   g_signal_emit_by_name (backend, "printer-removed", printer);
3496                   gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
3497                                                     GTK_PRINTER (printer));
3498                   g_signal_emit_by_name (backend, "printer-list-changed");
3499                   break;
3500                 }
3501             }
3502 
3503           g_list_free (list);
3504         }
3505     }
3506 }
3507 
3508 static gboolean
unsubscribe_general_subscription_cb(gpointer user_data)3509 unsubscribe_general_subscription_cb (gpointer user_data)
3510 {
3511   GtkPrintBackendCups *cups_backend = user_data;
3512 
3513   g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
3514                                         cups_backend->avahi_service_browser_subscription_id);
3515   cups_backend->avahi_service_browser_subscription_id = 0;
3516   cups_backend->unsubscribe_general_subscription_id = 0;
3517 
3518   return G_SOURCE_REMOVE;
3519 }
3520 
3521 static void
avahi_service_browser_new_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3522 avahi_service_browser_new_cb (GObject      *source_object,
3523                               GAsyncResult *res,
3524                               gpointer      user_data)
3525 {
3526   GtkPrintBackendCups *cups_backend;
3527   GVariant            *output;
3528   GError              *error = NULL;
3529   int                  i;
3530 
3531   output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
3532                                           res,
3533                                           &error);
3534   if (output)
3535     {
3536       cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
3537       i = cups_backend->avahi_service_browser_paths[0] ? 1 : 0;
3538 
3539       g_variant_get (output, "(o)", &cups_backend->avahi_service_browser_paths[i]);
3540 
3541       cups_backend->avahi_service_browser_subscription_ids[i] =
3542         g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
3543                                             NULL,
3544                                             AVAHI_SERVICE_BROWSER_IFACE,
3545                                             NULL,
3546                                             cups_backend->avahi_service_browser_paths[i],
3547                                             NULL,
3548                                             G_DBUS_SIGNAL_FLAGS_NONE,
3549                                             avahi_service_browser_signal_handler,
3550                                             user_data,
3551                                             NULL);
3552 
3553       /*
3554        * The general subscription for all service browsers is not needed
3555        * now because we are already subscribed to service browsers
3556        * specific to _ipp._tcp and _ipps._tcp services.
3557        */
3558       if (cups_backend->avahi_service_browser_paths[0] &&
3559           cups_backend->avahi_service_browser_paths[1] &&
3560           cups_backend->avahi_service_browser_subscription_id > 0)
3561         {
3562           /* We need to unsubscribe in idle since signals in queue destined for emit
3563            * are emitted in idle and check whether the subscriber is still subscribed.
3564            */
3565           cups_backend->unsubscribe_general_subscription_id = g_idle_add (unsubscribe_general_subscription_cb, cups_backend);
3566         }
3567 
3568       g_variant_unref (output);
3569     }
3570   else
3571     {
3572       /*
3573        * The creation of ServiceBrowser fails with G_IO_ERROR_DBUS_ERROR
3574        * if Avahi is disabled.
3575        */
3576       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) &&
3577           !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
3578         g_warning ("%s", error->message);
3579       g_error_free (error);
3580     }
3581 }
3582 
3583 static void
avahi_create_browsers(GObject * source_object,GAsyncResult * res,gpointer user_data)3584 avahi_create_browsers (GObject      *source_object,
3585                        GAsyncResult *res,
3586                        gpointer      user_data)
3587 {
3588   GDBusConnection     *dbus_connection;
3589   GtkPrintBackendCups *cups_backend;
3590   GError              *error = NULL;
3591 
3592   dbus_connection = g_bus_get_finish (res, &error);
3593   if (!dbus_connection)
3594     {
3595       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
3596         g_message ("Couldn't connect to D-Bus system bus, avahi printers will not be available: %s", error->message);
3597 
3598       g_error_free (error);
3599       return;
3600     }
3601 
3602   cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
3603   cups_backend->dbus_connection = dbus_connection;
3604 
3605   /*
3606    * We need to subscribe to signals of service browser before
3607    * we actually create it because it starts to emit them right
3608    * after its creation.
3609    */
3610   cups_backend->avahi_service_browser_subscription_id =
3611     g_dbus_connection_signal_subscribe  (cups_backend->dbus_connection,
3612                                          NULL,
3613                                          AVAHI_SERVICE_BROWSER_IFACE,
3614                                          NULL,
3615                                          NULL,
3616                                          NULL,
3617                                          G_DBUS_SIGNAL_FLAGS_NONE,
3618                                          avahi_service_browser_signal_handler,
3619                                          cups_backend,
3620                                          NULL);
3621   /*
3622    * Create service browsers for _ipp._tcp and _ipps._tcp services.
3623    */
3624   g_dbus_connection_call (cups_backend->dbus_connection,
3625                           AVAHI_BUS,
3626                           "/",
3627                           AVAHI_SERVER_IFACE,
3628                           "ServiceBrowserNew",
3629                           g_variant_new ("(iissu)",
3630                                          AVAHI_IF_UNSPEC,
3631                                          AVAHI_PROTO_UNSPEC,
3632                                          "_ipp._tcp",
3633                                          "",
3634                                          0),
3635                           G_VARIANT_TYPE ("(o)"),
3636                           G_DBUS_CALL_FLAGS_NONE,
3637                           -1,
3638                           cups_backend->avahi_cancellable,
3639                           avahi_service_browser_new_cb,
3640                           cups_backend);
3641 
3642   g_dbus_connection_call (cups_backend->dbus_connection,
3643                           AVAHI_BUS,
3644                           "/",
3645                           AVAHI_SERVER_IFACE,
3646                           "ServiceBrowserNew",
3647                           g_variant_new ("(iissu)",
3648                                          AVAHI_IF_UNSPEC,
3649                                          AVAHI_PROTO_UNSPEC,
3650                                          "_ipps._tcp",
3651                                          "",
3652                                          0),
3653                           G_VARIANT_TYPE ("(o)"),
3654                           G_DBUS_CALL_FLAGS_NONE,
3655                           -1,
3656                           cups_backend->avahi_cancellable,
3657                           avahi_service_browser_new_cb,
3658                           cups_backend);
3659 }
3660 
3661 static void
avahi_request_printer_list(GtkPrintBackendCups * cups_backend)3662 avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
3663 {
3664   cups_backend->avahi_cancellable = g_cancellable_new ();
3665   g_bus_get (G_BUS_TYPE_SYSTEM, cups_backend->avahi_cancellable, avahi_create_browsers, cups_backend);
3666 }
3667 
3668 static void
cups_request_printer_list_cb(GtkPrintBackendCups * cups_backend,GtkCupsResult * result,gpointer user_data)3669 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
3670                               GtkCupsResult       *result,
3671                               gpointer             user_data)
3672 {
3673   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
3674   ipp_attribute_t *attr;
3675   ipp_t *response;
3676   gboolean list_has_changed;
3677   GList *removed_printer_checklist;
3678   char *remote_default_printer = NULL;
3679   GList *iter;
3680 
3681   list_has_changed = FALSE;
3682 
3683   GTK_NOTE (PRINTING,
3684             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3685 
3686   cups_backend->list_printers_pending = FALSE;
3687 
3688   if (gtk_cups_result_is_error (result))
3689     {
3690       GTK_NOTE (PRINTING,
3691                 g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
3692                            gtk_cups_result_get_error_string (result),
3693                            gtk_cups_result_get_error_type (result),
3694                            gtk_cups_result_get_error_code (result)));
3695 
3696       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
3697           gtk_cups_result_get_error_code (result) == 1)
3698         {
3699           /* Canceled by user, stop popping up more password dialogs */
3700           if (cups_backend->list_printers_poll > 0)
3701             g_source_remove (cups_backend->list_printers_poll);
3702           cups_backend->list_printers_poll = 0;
3703           cups_backend->list_printers_attempts = 0;
3704         }
3705 
3706       goto done;
3707     }
3708 
3709   /* Gather the names of the printers in the current queue
3710    * so we may check to see if they were removed
3711    */
3712   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
3713 
3714   response = gtk_cups_result_get_response (result);
3715   for (attr = ippFirstAttribute (response); attr != NULL;
3716        attr = ippNextAttribute (response))
3717     {
3718       GtkPrinter *printer;
3719       gboolean status_changed = FALSE;
3720       GList *node;
3721       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
3722 
3723       /* Skip leading attributes until we hit a printer...
3724        */
3725       while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
3726         attr = ippNextAttribute (response);
3727 
3728       if (attr == NULL)
3729         break;
3730 
3731       while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
3732         {
3733           cups_printer_handle_attribute (cups_backend, attr, info);
3734           attr = ippNextAttribute (response);
3735         }
3736 
3737       if (info->printer_name == NULL ||
3738 	  (info->printer_uri == NULL && info->member_uris == NULL))
3739         {
3740           if (attr == NULL)
3741             break;
3742           else
3743             continue;
3744         }
3745 
3746       /* Do not show printer for queue which was removed from Avahi. */
3747       iter = g_list_find_custom (GTK_PRINT_BACKEND_CUPS (backend)->temporary_queues_removed,
3748                                  info->printer_name, (GCompareFunc) g_strcmp0);
3749       if (iter != NULL)
3750         continue;
3751 
3752       if (info->got_printer_type)
3753         {
3754           if (info->default_printer && !cups_backend->got_default_printer)
3755             {
3756               if (!info->remote_printer)
3757                 {
3758                   cups_backend->got_default_printer = TRUE;
3759                   cups_backend->default_printer = g_strdup (info->printer_name);
3760                 }
3761               else
3762                 {
3763                   if (remote_default_printer == NULL)
3764                     remote_default_printer = g_strdup (info->printer_name);
3765                 }
3766             }
3767         }
3768       else
3769         {
3770           if (!cups_backend->got_default_printer)
3771             cups_get_default_printer (cups_backend);
3772         }
3773 
3774       /* remove name from checklist if it was found */
3775       node = g_list_find_custom (removed_printer_checklist,
3776 				 info->printer_name,
3777 				 (GCompareFunc) find_printer);
3778       removed_printer_checklist = g_list_delete_link (removed_printer_checklist,
3779 						      node);
3780 
3781       printer = gtk_print_backend_find_printer (backend, info->printer_name);
3782       if (!printer)
3783 	{
3784 	  printer = cups_create_printer (cups_backend, info);
3785 	  list_has_changed = TRUE;
3786 	}
3787       else if (GTK_PRINTER_CUPS (printer)->avahi_browsed && info->is_temporary)
3788         {
3789           /*
3790            * A temporary queue was created for a printer found via Avahi.
3791            * We modify the placeholder GtkPrinter to point to the temporary queue
3792            * instead of removing the placeholder GtkPrinter and creating new GtkPrinter.
3793            */
3794 
3795           g_object_ref (printer);
3796 
3797           GTK_PRINTER_CUPS (printer)->avahi_browsed = FALSE;
3798           GTK_PRINTER_CUPS (printer)->is_temporary = TRUE;
3799           g_free (GTK_PRINTER_CUPS (printer)->device_uri);
3800           GTK_PRINTER_CUPS (printer)->device_uri = g_strdup_printf ("/printers/%s",
3801                                                                     info->printer_name);
3802           gtk_printer_set_has_details (printer, FALSE);
3803           cups_printer_request_details (printer);
3804         }
3805       else
3806 	g_object_ref (printer);
3807 
3808       GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
3809 
3810       gtk_printer_set_is_paused (printer, info->is_paused);
3811       gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
3812 
3813       if (!gtk_printer_is_active (printer))
3814         {
3815 	  gtk_printer_set_is_active (printer, TRUE);
3816 	  gtk_printer_set_is_new (printer, TRUE);
3817           list_has_changed = TRUE;
3818         }
3819 
3820       if (gtk_printer_is_new (printer))
3821         {
3822 	  g_signal_emit_by_name (backend, "printer-added", printer);
3823 
3824 	  gtk_printer_set_is_new (printer, FALSE);
3825         }
3826 
3827       GTK_PRINTER_CUPS (printer)->state = info->state;
3828       GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
3829       GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
3830       GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
3831       GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
3832       GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
3833       GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
3834       g_clear_pointer (&(GTK_PRINTER_CUPS (printer)->covers), g_strfreev);
3835       GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
3836       GTK_PRINTER_CUPS (printer)->is_temporary = info->is_temporary;
3837       status_changed = gtk_printer_set_job_count (printer, info->job_count);
3838       status_changed |= gtk_printer_set_location (printer, info->location);
3839       status_changed |= gtk_printer_set_description (printer,
3840 						     info->description);
3841 
3842       set_info_state_message (info);
3843 
3844       status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
3845       status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
3846 
3847       set_printer_icon_name_from_info (printer, info);
3848 
3849       if (status_changed)
3850         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
3851                                "printer-status-changed", printer);
3852 
3853       /* The ref is held by GtkPrintBackend, in add_printer() */
3854       g_object_unref (printer);
3855       printer_setup_info_free (info);
3856 
3857       if (attr == NULL)
3858         break;
3859     }
3860 
3861   /* look at the removed printers checklist and mark any printer
3862      as inactive if it is in the list, emitting a printer_removed signal */
3863   if (removed_printer_checklist != NULL)
3864     {
3865       for (iter = removed_printer_checklist; iter; iter = iter->next)
3866         {
3867           if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
3868             {
3869               mark_printer_inactive (GTK_PRINTER (iter->data), backend);
3870               list_has_changed = TRUE;
3871             }
3872         }
3873 
3874       g_list_free (removed_printer_checklist);
3875     }
3876 
3877 done:
3878   if (list_has_changed)
3879     g_signal_emit_by_name (backend, "printer-list-changed");
3880 
3881   gtk_print_backend_set_list_done (backend);
3882 
3883   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
3884     {
3885       set_default_printer (cups_backend, remote_default_printer);
3886       g_free (remote_default_printer);
3887     }
3888 
3889   if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
3890     set_default_printer (cups_backend, cups_backend->avahi_default_printer);
3891 }
3892 
3893 static void
update_backend_status(GtkPrintBackendCups * cups_backend,GtkCupsConnectionState state)3894 update_backend_status (GtkPrintBackendCups    *cups_backend,
3895                        GtkCupsConnectionState  state)
3896 {
3897   switch (state)
3898     {
3899     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
3900       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
3901       break;
3902     case GTK_CUPS_CONNECTION_AVAILABLE:
3903       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
3904       break;
3905 
3906     case GTK_CUPS_CONNECTION_IN_PROGRESS:
3907     default: ;
3908     }
3909 }
3910 
3911 static gboolean
cups_request_printer_list(GtkPrintBackendCups * cups_backend)3912 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
3913 {
3914   GtkCupsConnectionState state;
3915   GtkCupsRequest *request;
3916 
3917   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
3918     return TRUE;
3919 
3920   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
3921   update_backend_status (cups_backend, state);
3922 
3923   if (cups_backend->list_printers_attempts == 60)
3924     {
3925       cups_backend->list_printers_attempts = -1;
3926       if (cups_backend->list_printers_poll > 0)
3927         g_source_remove (cups_backend->list_printers_poll);
3928       cups_backend->list_printers_poll = g_timeout_add (200, (GSourceFunc) cups_request_printer_list, cups_backend);
3929       g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk] cups_request_printer_list");
3930     }
3931   else if (cups_backend->list_printers_attempts != -1)
3932     cups_backend->list_printers_attempts++;
3933 
3934   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
3935     return TRUE;
3936   else
3937     if (cups_backend->list_printers_attempts > 0)
3938       cups_backend->list_printers_attempts = 60;
3939 
3940   cups_backend->list_printers_pending = TRUE;
3941 
3942   request = gtk_cups_request_new_with_username (NULL,
3943                                                 GTK_CUPS_POST,
3944                                                 CUPS_GET_PRINTERS,
3945                                                 NULL,
3946                                                 NULL,
3947                                                 NULL,
3948                                                 cups_backend->username);
3949 
3950   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3951 				    "requested-attributes", G_N_ELEMENTS (printer_attrs),
3952 				    NULL, printer_attrs);
3953 
3954   cups_request_execute (cups_backend,
3955                         request,
3956                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
3957 		        request,
3958 		        NULL);
3959 
3960   return TRUE;
3961 }
3962 
3963 static void
cups_get_printer_list(GtkPrintBackend * backend)3964 cups_get_printer_list (GtkPrintBackend *backend)
3965 {
3966   GtkPrintBackendCups *cups_backend;
3967 
3968   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
3969 
3970   if (cups_backend->cups_connection_test == NULL)
3971     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
3972 
3973   if (cups_backend->list_printers_poll == 0)
3974     {
3975       if (cups_request_printer_list (cups_backend))
3976         {
3977           cups_backend->list_printers_poll = g_timeout_add (50, (GSourceFunc) cups_request_printer_list, backend);
3978           g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk] cups_request_printer_list");
3979         }
3980 
3981       avahi_request_printer_list (cups_backend);
3982     }
3983 }
3984 
3985 typedef struct {
3986   GtkPrinterCups *printer;
3987   GIOChannel *ppd_io;
3988   http_t *http;
3989 } GetPPDData;
3990 
3991 static void
get_ppd_data_free(GetPPDData * data)3992 get_ppd_data_free (GetPPDData *data)
3993 {
3994   GTK_NOTE (PRINTING,
3995             g_print ("CUPS Backend: %s\n", G_STRFUNC));
3996   httpClose (data->http);
3997   g_io_channel_unref (data->ppd_io);
3998   g_object_unref (data->printer);
3999   g_free (data);
4000 }
4001 
4002 static void
cups_request_ppd_cb(GtkPrintBackendCups * print_backend,GtkCupsResult * result,GetPPDData * data)4003 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
4004                      GtkCupsResult       *result,
4005                      GetPPDData          *data)
4006 {
4007   GtkPrinter *printer;
4008   struct stat data_info;
4009 
4010   GTK_NOTE (PRINTING,
4011             g_print ("CUPS Backend: %s\n", G_STRFUNC));
4012 
4013   printer = GTK_PRINTER (data->printer);
4014   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
4015   print_backend->reading_ppds--;
4016 
4017   if (!gtk_cups_result_is_error (result))
4018     {
4019       G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4020 
4021       /* let ppdOpenFd take over the ownership of the open file */
4022       g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
4023       data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
4024       ppdLocalize (data->printer->ppd_file);
4025       ppdMarkDefaults (data->printer->ppd_file);
4026 
4027       G_GNUC_END_IGNORE_DEPRECATIONS
4028     }
4029 
4030   fstat (g_io_channel_unix_get_fd (data->ppd_io), &data_info);
4031   /*
4032    * Standalone Avahi printers and raw printers don't have PPD files or have
4033    * empty PPD files. Try to get printer details via IPP.
4034    * Always do this for Avahi printers.
4035    */
4036   if (data_info.st_size == 0 ||
4037       GTK_PRINTER_CUPS (printer)->avahi_browsed ||
4038       (gtk_cups_result_is_error (result) &&
4039        ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
4040          (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))))
4041     {
4042       GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
4043 
4044       /* Try to get the PPD from original host if it is not
4045        * available on current CUPS server.
4046        */
4047       if (!cups_printer->avahi_browsed &&
4048           (gtk_cups_result_is_error (result) &&
4049            ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
4050             (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))) &&
4051           cups_printer->remote &&
4052           !cups_printer->request_original_uri &&
4053           cups_printer->original_device_uri != NULL &&
4054           (g_str_has_prefix (cups_printer->original_device_uri, "ipp://") ||
4055            g_str_has_prefix (cups_printer->original_device_uri, "ipps://")))
4056         {
4057           cups_printer->request_original_uri = TRUE;
4058 
4059           gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
4060           g_clear_handle_id (&cups_printer->get_remote_ppd_poll, g_source_remove);
4061           cups_printer->get_remote_ppd_attempts = 0;
4062 
4063           cups_printer->remote_cups_connection_test =
4064             gtk_cups_connection_test_new (cups_printer->original_hostname,
4065                                           cups_printer->original_port);
4066 
4067           if (cups_request_ppd (printer))
4068             {
4069               cups_printer->get_remote_ppd_poll = g_timeout_add (50, (GSourceFunc) cups_request_ppd, printer);
4070               g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk] cups_request_ppd");
4071             }
4072         }
4073       else
4074         {
4075           if (cups_printer->request_original_uri)
4076             cups_printer->request_original_uri = FALSE;
4077 
4078           cups_request_printer_info (cups_printer);
4079         }
4080 
4081       return;
4082     }
4083 
4084   gtk_printer_set_has_details (printer, TRUE);
4085   g_signal_emit_by_name (printer, "details-acquired", TRUE);
4086 }
4087 
4088 static gboolean
cups_request_ppd(GtkPrinter * printer)4089 cups_request_ppd (GtkPrinter *printer)
4090 {
4091   GError *error;
4092   GtkPrintBackend *print_backend;
4093   GtkPrinterCups *cups_printer;
4094   GtkCupsRequest *request;
4095   char *ppd_filename = NULL;
4096   char *resource;
4097   http_t *http;
4098   GetPPDData *data;
4099   int fd;
4100   const char *hostname;
4101   int port;
4102 
4103   cups_printer = GTK_PRINTER_CUPS (printer);
4104 
4105   error = NULL;
4106 
4107   GTK_NOTE (PRINTING,
4108             g_print ("CUPS Backend: %s\n", G_STRFUNC));
4109 
4110   if (cups_printer->remote && !cups_printer->avahi_browsed)
4111     {
4112       GtkCupsConnectionState state;
4113 
4114       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
4115 
4116       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
4117         {
4118           if (cups_printer->get_remote_ppd_attempts == 60)
4119             {
4120               cups_printer->get_remote_ppd_attempts = -1;
4121               if (cups_printer->get_remote_ppd_poll > 0)
4122                 g_source_remove (cups_printer->get_remote_ppd_poll);
4123               cups_printer->get_remote_ppd_poll = g_timeout_add (200, (GSourceFunc) cups_request_ppd, printer);
4124               g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk] cups_request_ppd");
4125             }
4126           else if (cups_printer->get_remote_ppd_attempts != -1)
4127             cups_printer->get_remote_ppd_attempts++;
4128 
4129           return TRUE;
4130         }
4131 
4132       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
4133       cups_printer->remote_cups_connection_test = NULL;
4134       cups_printer->get_remote_ppd_poll = 0;
4135       cups_printer->get_remote_ppd_attempts = 0;
4136 
4137       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
4138         {
4139           g_signal_emit_by_name (printer, "details-acquired", FALSE);
4140           return FALSE;
4141         }
4142     }
4143 
4144   if (cups_printer->request_original_uri)
4145     {
4146       hostname = cups_printer->original_hostname;
4147       port = cups_printer->original_port;
4148       resource = g_strdup_printf ("%s.ppd", cups_printer->original_resource);
4149     }
4150   else
4151     {
4152       if (cups_printer->is_temporary)
4153         hostname = cupsServer ();
4154       else
4155         hostname = cups_printer->hostname;
4156       port = cups_printer->port;
4157       resource = g_strdup_printf ("/printers/%s.ppd",
4158                                   gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
4159     }
4160 
4161   http = httpConnect2 (hostname, port,
4162                        NULL, AF_UNSPEC,
4163                        cupsEncryption (),
4164                        1, 30000, NULL);
4165 
4166   data = g_new0 (GetPPDData, 1);
4167 
4168   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
4169                         &ppd_filename,
4170                         &error);
4171 
4172 #ifdef G_ENABLE_DEBUG
4173   /* If we are debugging printing don't delete the tmp files */
4174   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
4175     unlink (ppd_filename);
4176 #else
4177   unlink (ppd_filename);
4178 #endif /* G_ENABLE_DEBUG */
4179 
4180   if (error != NULL)
4181     {
4182       GTK_NOTE (PRINTING,
4183                 g_warning ("CUPS Backend: Failed to create temp file, %s\n",
4184                            error->message));
4185       g_error_free (error);
4186       httpClose (http);
4187       g_free (ppd_filename);
4188       g_free (data);
4189 
4190       g_signal_emit_by_name (printer, "details-acquired", FALSE);
4191       return FALSE;
4192     }
4193 
4194   data->http = http;
4195   fchmod (fd, S_IRUSR | S_IWUSR);
4196   data->ppd_io = g_io_channel_unix_new (fd);
4197   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
4198   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
4199 
4200   data->printer = (GtkPrinterCups *) g_object_ref (printer);
4201 
4202   print_backend = gtk_printer_get_backend (printer);
4203 
4204   request = gtk_cups_request_new_with_username (data->http,
4205                                                 GTK_CUPS_GET,
4206                                                 0,
4207                                                 data->ppd_io,
4208                                                 hostname,
4209                                                 resource,
4210                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
4211 
4212   gtk_cups_request_set_ipp_version (request,
4213                                     cups_printer->ipp_version_major,
4214                                     cups_printer->ipp_version_minor);
4215 
4216   GTK_NOTE (PRINTING,
4217             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
4218 
4219 
4220   cups_printer->reading_ppd = TRUE;
4221   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
4222 
4223   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
4224                         request,
4225                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
4226                         data,
4227                         (GDestroyNotify)get_ppd_data_free);
4228 
4229   g_free (resource);
4230   g_free (ppd_filename);
4231 
4232   return FALSE;
4233 }
4234 
4235 /* Ordering matters for default preference */
4236 static const char *lpoptions_locations[] = {
4237   "/etc/cups/lpoptions",
4238   ".lpoptions",
4239   ".cups/lpoptions"
4240 };
4241 
4242 static void
cups_parse_user_default_printer(const char * filename,char ** printer_name)4243 cups_parse_user_default_printer (const char  *filename,
4244                                  char       **printer_name)
4245 {
4246   FILE *fp;
4247   char line[1024], *lineptr, *defname = NULL;
4248 
4249   if ((fp = g_fopen (filename, "r")) == NULL)
4250     return;
4251 
4252   while (fgets (line, sizeof (line), fp) != NULL)
4253     {
4254       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
4255         continue;
4256 
4257       lineptr = line + 8;
4258       while (isspace (*lineptr))
4259         lineptr++;
4260 
4261       if (!*lineptr)
4262         continue;
4263 
4264       defname = lineptr;
4265       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
4266         lineptr++;
4267 
4268       *lineptr = '\0';
4269 
4270       g_free (*printer_name);
4271 
4272       *printer_name = g_strdup (defname);
4273     }
4274 
4275   fclose (fp);
4276 }
4277 
4278 static void
cups_get_user_default_printer(char ** printer_name)4279 cups_get_user_default_printer (char **printer_name)
4280 {
4281   int i;
4282 
4283   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
4284     {
4285       if (g_path_is_absolute (lpoptions_locations[i]))
4286         {
4287           cups_parse_user_default_printer (lpoptions_locations[i],
4288                                            printer_name);
4289         }
4290       else
4291         {
4292           char *filename;
4293 
4294           filename = g_build_filename (g_get_home_dir (),
4295                                        lpoptions_locations[i], NULL);
4296           cups_parse_user_default_printer (filename, printer_name);
4297           g_free (filename);
4298         }
4299     }
4300 }
4301 
4302 static int
cups_parse_user_options(const char * filename,const char * printer_name,int num_options,cups_option_t ** options)4303 cups_parse_user_options (const char     *filename,
4304                          const char     *printer_name,
4305                          int             num_options,
4306                          cups_option_t **options)
4307 {
4308   FILE *fp;
4309   char line[1024], *lineptr, *name;
4310 
4311   if ((fp = g_fopen (filename, "r")) == NULL)
4312     return num_options;
4313 
4314   while (fgets (line, sizeof (line), fp) != NULL)
4315     {
4316       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
4317         lineptr = line + 4;
4318       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
4319         lineptr = line + 7;
4320       else
4321         continue;
4322 
4323       /* Skip leading whitespace */
4324       while (isspace (*lineptr))
4325         lineptr++;
4326 
4327       if (!*lineptr)
4328         continue;
4329 
4330       name = lineptr;
4331       while (!isspace (*lineptr) && *lineptr)
4332         {
4333           lineptr++;
4334         }
4335 
4336       if (!*lineptr)
4337         continue;
4338 
4339       *lineptr++ = '\0';
4340 
4341       if (strcasecmp (name, printer_name) != 0)
4342           continue;
4343 
4344       /* We found our printer, parse the options */
4345       num_options = cupsParseOptions (lineptr, num_options, options);
4346     }
4347 
4348   fclose (fp);
4349 
4350   return num_options;
4351 }
4352 
4353 static int
cups_get_user_options(const char * printer_name,int num_options,cups_option_t ** options)4354 cups_get_user_options (const char     *printer_name,
4355                        int             num_options,
4356                        cups_option_t **options)
4357 {
4358   int i;
4359 
4360   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
4361     {
4362       if (g_path_is_absolute (lpoptions_locations[i]))
4363         {
4364            num_options = cups_parse_user_options (lpoptions_locations[i],
4365                                                   printer_name,
4366                                                   num_options,
4367                                                   options);
4368         }
4369       else
4370         {
4371           char *filename;
4372 
4373           filename = g_build_filename (g_get_home_dir (),
4374                                        lpoptions_locations[i], NULL);
4375           num_options = cups_parse_user_options (filename, printer_name,
4376                                                  num_options, options);
4377           g_free (filename);
4378         }
4379     }
4380 
4381   return num_options;
4382 }
4383 
4384 /* This function requests default printer from a CUPS server in regular intervals.
4385  * In the case of unreachable CUPS server the request is repeated later.
4386  * The default printer is not requested in the case of previous success.
4387  */
4388 static void
cups_get_default_printer(GtkPrintBackendCups * backend)4389 cups_get_default_printer (GtkPrintBackendCups *backend)
4390 {
4391   GtkPrintBackendCups *cups_backend;
4392 
4393   cups_backend = backend;
4394 
4395   if (cups_backend->cups_connection_test == NULL)
4396     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
4397 
4398   if (cups_backend->default_printer_poll == 0)
4399     {
4400       if (cups_request_default_printer (cups_backend))
4401         {
4402           cups_backend->default_printer_poll = g_timeout_add (200, (GSourceFunc) cups_request_default_printer, backend);
4403           g_source_set_name_by_id (cups_backend->default_printer_poll, "[gtk] cups_request_default_printer");
4404         }
4405     }
4406 }
4407 
4408 /* This function gets default printer from local settings.*/
4409 static void
cups_get_local_default_printer(GtkPrintBackendCups * backend)4410 cups_get_local_default_printer (GtkPrintBackendCups *backend)
4411 {
4412   const char *str;
4413   char *name = NULL;
4414 
4415   if ((str = g_getenv ("LPDEST")) != NULL)
4416     {
4417       backend->default_printer = g_strdup (str);
4418       backend->got_default_printer = TRUE;
4419       return;
4420     }
4421   else if ((str = g_getenv ("PRINTER")) != NULL &&
4422 	   strcmp (str, "lp") != 0)
4423     {
4424       backend->default_printer = g_strdup (str);
4425       backend->got_default_printer = TRUE;
4426       return;
4427     }
4428 
4429   /* Figure out user setting for default printer */
4430   cups_get_user_default_printer (&name);
4431   if (name != NULL)
4432     {
4433       backend->default_printer = name;
4434       backend->got_default_printer = TRUE;
4435       return;
4436     }
4437 }
4438 
4439 static void
cups_request_default_printer_cb(GtkPrintBackendCups * print_backend,GtkCupsResult * result,gpointer user_data)4440 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
4441 				 GtkCupsResult       *result,
4442 				 gpointer             user_data)
4443 {
4444   ipp_t *response;
4445   ipp_attribute_t *attr;
4446   GtkPrinter *printer;
4447 
4448   if (gtk_cups_result_is_error (result))
4449     {
4450       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
4451           gtk_cups_result_get_error_code (result) == 1)
4452         {
4453           /* Canceled by user, stop popping up more password dialogs */
4454           if (print_backend->list_printers_poll > 0)
4455             g_source_remove (print_backend->list_printers_poll);
4456           print_backend->list_printers_poll = 0;
4457         }
4458 
4459       return;
4460     }
4461 
4462   response = gtk_cups_result_get_response (result);
4463 
4464   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
4465     print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
4466 
4467   print_backend->got_default_printer = TRUE;
4468 
4469   if (print_backend->default_printer != NULL)
4470     {
4471       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend),
4472                                                 print_backend->default_printer);
4473       if (printer != NULL)
4474         {
4475           gtk_printer_set_is_default (printer, TRUE);
4476           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
4477         }
4478     }
4479 
4480   /* Make sure to kick off get_printers if we are polling it,
4481    * as we could have blocked this reading the default printer
4482    */
4483   if (print_backend->list_printers_poll != 0)
4484     cups_request_printer_list (print_backend);
4485 }
4486 
4487 static gboolean
cups_request_default_printer(GtkPrintBackendCups * print_backend)4488 cups_request_default_printer (GtkPrintBackendCups *print_backend)
4489 {
4490   GtkCupsConnectionState state;
4491   GtkCupsRequest *request;
4492 
4493   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
4494   update_backend_status (print_backend, state);
4495 
4496   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
4497     return TRUE;
4498 
4499   request = gtk_cups_request_new_with_username (NULL,
4500                                                 GTK_CUPS_POST,
4501                                                 CUPS_GET_DEFAULT,
4502                                                 NULL,
4503                                                 NULL,
4504                                                 NULL,
4505                                                 print_backend->username);
4506 
4507   cups_request_execute (print_backend,
4508                         request,
4509                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
4510 		        g_object_ref (print_backend),
4511 		        g_object_unref);
4512 
4513   return FALSE;
4514 }
4515 
4516 static void
cups_printer_request_details(GtkPrinter * printer)4517 cups_printer_request_details (GtkPrinter *printer)
4518 {
4519   GtkPrinterCups *cups_printer;
4520 
4521   cups_printer = GTK_PRINTER_CUPS (printer);
4522 
4523   if (cups_printer->avahi_browsed)
4524     {
4525       create_temporary_queue (GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer)),
4526                               gtk_printer_get_name (printer),
4527                               cups_printer->printer_uri,
4528                               cups_printer->temporary_queue_device_uri);
4529     }
4530   else if (!cups_printer->reading_ppd &&
4531            gtk_printer_cups_get_ppd (cups_printer) == NULL)
4532     {
4533       if (cups_printer->remote && !cups_printer->avahi_browsed)
4534         {
4535           if (cups_printer->get_remote_ppd_poll == 0)
4536             {
4537               cups_printer->remote_cups_connection_test =
4538                 gtk_cups_connection_test_new (cups_printer->hostname,
4539                                               cups_printer->port);
4540 
4541               if (cups_request_ppd (printer))
4542                 {
4543                   cups_printer->get_remote_ppd_poll = g_timeout_add (50, (GSourceFunc) cups_request_ppd, printer);
4544                   g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk] cups_request_ppd");
4545                 }
4546             }
4547         }
4548       else
4549         cups_request_ppd (printer);
4550     }
4551 }
4552 
4553 static char *
ppd_text_to_utf8(ppd_file_t * ppd_file,const char * text)4554 ppd_text_to_utf8 (ppd_file_t *ppd_file,
4555 		  const char *text)
4556 {
4557   const char *encoding = NULL;
4558   char *res;
4559 
4560   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
4561     {
4562       return g_strdup (text);
4563     }
4564   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
4565     {
4566       encoding = "ISO-8859-1";
4567     }
4568   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
4569     {
4570       encoding = "ISO-8859-2";
4571     }
4572   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
4573     {
4574       encoding = "ISO-8859-5";
4575     }
4576   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
4577     {
4578       encoding = "SHIFT-JIS";
4579     }
4580   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
4581     {
4582       encoding = "MACINTOSH";
4583     }
4584   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
4585     {
4586       encoding = "WINDOWS-1252";
4587     }
4588   else
4589     {
4590       /* Fallback, try iso-8859-1... */
4591       encoding = "ISO-8859-1";
4592     }
4593 
4594   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
4595 
4596   if (res == NULL)
4597     {
4598       GTK_NOTE (PRINTING,
4599                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
4600       res = g_strdup ("???");
4601     }
4602 
4603   return res;
4604 }
4605 
4606 /* TODO: Add more translations for common settings here */
4607 
4608 static const struct {
4609   const char *keyword;
4610   const char *translation;
4611 } cups_option_translations[] = {
4612   { "Duplex", NC_("printing option", "Two Sided") },
4613   { "MediaType", NC_("printing option", "Paper Type") },
4614   { "InputSlot", NC_("printing option", "Paper Source") },
4615   { "OutputBin", NC_("printing option", "Output Tray") },
4616   { "Resolution", NC_("printing option", "Resolution") },
4617   { "PreFilter", NC_("printing option", "GhostScript pre-filtering") }
4618 };
4619 
4620 
4621 static const struct {
4622   const char *keyword;
4623   const char *choice;
4624   const char *translation;
4625 } cups_choice_translations[] = {
4626   { "Duplex", "None", NC_("printing option value", "One Sided") },
4627   /* Translators: this is an option of "Two Sided" */
4628   { "Duplex", "DuplexNoTumble", NC_("printing option value", "Long Edge (Standard)") },
4629   /* Translators: this is an option of "Two Sided" */
4630   { "Duplex", "DuplexTumble", NC_("printing option value", "Short Edge (Flip)") },
4631   /* Translators: this is an option of "Paper Source" */
4632   { "InputSlot", "Auto", NC_("printing option value", "Auto Select") },
4633   /* Translators: this is an option of "Paper Source" */
4634   { "InputSlot", "AutoSelect", NC_("printing option value", "Auto Select") },
4635   /* Translators: this is an option of "Paper Source" */
4636   { "InputSlot", "Default", NC_("printing option value", "Printer Default") },
4637   /* Translators: this is an option of "Paper Source" */
4638   { "InputSlot", "None", NC_("printing option value", "Printer Default") },
4639   /* Translators: this is an option of "Paper Source" */
4640   { "InputSlot", "PrinterDefault", NC_("printing option value", "Printer Default") },
4641   /* Translators: this is an option of "Paper Source" */
4642   { "InputSlot", "Unspecified", NC_("printing option value", "Auto Select") },
4643   /* Translators: this is an option of "Resolution" */
4644   { "Resolution", "default", NC_("printing option value", "Printer Default") },
4645   /* Translators: this is an option of "GhostScript" */
4646   { "PreFilter", "EmbedFonts", NC_("printing option value", "Embed GhostScript fonts only") },
4647   /* Translators: this is an option of "GhostScript" */
4648   { "PreFilter", "Level1", NC_("printing option value", "Convert to PS level 1") },
4649   /* Translators: this is an option of "GhostScript" */
4650   { "PreFilter", "Level2", NC_("printing option value", "Convert to PS level 2") },
4651   /* Translators: this is an option of "GhostScript" */
4652   { "PreFilter", "No", NC_("printing option value", "No pre-filtering") }
4653 };
4654 
4655 static const struct {
4656   const char *name;
4657   const char *translation;
4658 } cups_group_translations[] = {
4659 /* Translators: "Miscellaneous" is the label for a button, that opens
4660    up an extra panel of settings in a print dialog. */
4661   { "Miscellaneous", NC_("printing option group", "Miscellaneous") }
4662 };
4663 
4664 static const struct {
4665   const char *ppd_keyword;
4666   const char *name;
4667 } ppd_option_names[] = {
4668   { "Duplex", "gtk-duplex" },
4669   { "MediaType", "gtk-paper-type" },
4670   { "InputSlot", "gtk-paper-source" },
4671   { "OutputBin", "gtk-output-tray" }
4672 };
4673 
4674 static const struct {
4675   const char *ipp_option_name;
4676   const char *gtk_option_name;
4677   const char *translation;
4678 } ipp_option_translations[] = {
4679   { "sides", "gtk-duplex", NC_("printing option", "Two Sided") },
4680   { "output-bin", "gtk-output-tray", NC_("printing option", "Output Tray") }
4681 };
4682 
4683 static const struct {
4684   const char *ipp_option_name;
4685   const char *ipp_choice;
4686   const char *translation;
4687 } ipp_choice_translations[] = {
4688   { "sides", "one-sided", NC_("sides", "One Sided") },
4689   /* Translators: this is an option of "Two Sided" */
4690   { "sides", "two-sided-long-edge", NC_("sides", "Long Edge (Standard)") },
4691   /* Translators: this is an option of "Two Sided" */
4692   { "sides", "two-sided-short-edge", NC_("sides", "Short Edge (Flip)") },
4693 
4694   /* Translators: Top output bin */
4695   { "output-bin", "top", NC_("output-bin", "Top Bin") },
4696   /* Translators: Middle output bin */
4697   { "output-bin", "middle", NC_("output-bin", "Middle Bin") },
4698   /* Translators: Bottom output bin */
4699   { "output-bin", "bottom", NC_("output-bin", "Bottom Bin") },
4700   /* Translators: Side output bin */
4701   { "output-bin", "side", NC_("output-bin", "Side Bin") },
4702   /* Translators: Left output bin */
4703   { "output-bin", "left", NC_("output-bin", "Left Bin") },
4704   /* Translators: Right output bin */
4705   { "output-bin", "right", NC_("output-bin", "Right Bin") },
4706   /* Translators: Center output bin */
4707   { "output-bin", "center", NC_("output-bin", "Center Bin") },
4708   /* Translators: Rear output bin */
4709   { "output-bin", "rear", NC_("output-bin", "Rear Bin") },
4710   /* Translators: Output bin where one sided output is oriented in the face-up position */
4711   { "output-bin", "face-up", NC_("output-bin", "Face Up Bin") },
4712   /* Translators: Output bin where one sided output is oriented in the face-down position */
4713   { "output-bin", "face-down", NC_("output-bin", "Face Down Bin") },
4714   /* Translators: Large capacity output bin */
4715   { "output-bin", "large-capacity", NC_("output-bin", "Large Capacity Bin") },
4716   { NULL, NULL, NULL }
4717 };
4718 
4719 /*
4720  * Handles "format not a string literal" error
4721  * https://mail.gnome.org/archives/desktop-devel-list/2016-March/msg00075.html
4722  */
4723 static char *
get_ipp_choice_translation_string(int index,guint i)4724 get_ipp_choice_translation_string (int   index,
4725 				   guint i)
4726 {
4727   char *translation;
4728 
4729   if (i < G_N_ELEMENTS (ipp_choice_translations))
4730     translation = g_strdup (_(ipp_choice_translations[i].translation));
4731   else
4732     {
4733       switch (i)
4734         {
4735           case 14:
4736             /* Translators: Output stacker number %d */
4737             translation = g_strdup_printf (C_("output-bin", "Stacker %d"), index);
4738             break;
4739           case 15:
4740             /* Translators: Output mailbox number %d */
4741             translation = g_strdup_printf (C_("output-bin", "Mailbox %d"), index);
4742             break;
4743           case 16:
4744             /* Translators: Private mailbox */
4745             translation = g_strdup (C_("output-bin", "My Mailbox"));
4746             break;
4747           case 17:
4748             /* Translators: Output tray number %d */
4749             translation = g_strdup_printf (C_("output-bin", "Tray %d"), index);
4750             break;
4751           default:
4752             g_assert_not_reached ();
4753         }
4754     }
4755 
4756   return translation;
4757 }
4758 
4759 static const struct {
4760   const char *lpoption;
4761   const char *name;
4762 } lpoption_names[] = {
4763   { "number-up", "gtk-n-up" },
4764   { "number-up-layout", "gtk-n-up-layout" },
4765   { "job-billing", "gtk-billing-info" },
4766   { "job-priority", "gtk-job-prio" }
4767 };
4768 
4769 /* keep sorted when changing */
4770 static const char *color_option_allow_list[] = {
4771   "BRColorEnhancement",
4772   "BRColorMatching",
4773   "BRColorMatching",
4774   "BRColorMode",
4775   "BRGammaValue",
4776   "BRImprovedGray",
4777   "BlackSubstitution",
4778   "ColorModel",
4779   "HPCMYKInks",
4780   "HPCSGraphics",
4781   "HPCSImages",
4782   "HPCSText",
4783   "HPColorSmart",
4784   "RPSBlackMode",
4785   "RPSBlackOverPrint",
4786   "Rcmyksimulation",
4787 };
4788 
4789 /* keep sorted when changing */
4790 static const char *color_group_allow_list[] = {
4791   "ColorPage",
4792   "FPColorWise1",
4793   "FPColorWise2",
4794   "FPColorWise3",
4795   "FPColorWise4",
4796   "FPColorWise5",
4797   "HPColorOptionsPanel",
4798 };
4799 
4800 /* keep sorted when changing */
4801 static const char *image_quality_option_allow_list[] = {
4802   "BRDocument",
4803   "BRHalfTonePattern",
4804   "BRNormalPrt",
4805   "BRPrintQuality",
4806   "BitsPerPixel",
4807   "Darkness",
4808   "Dithering",
4809   "EconoMode",
4810   "Economode",
4811   "HPEconoMode",
4812   "HPEdgeControl",
4813   "HPGraphicsHalftone",
4814   "HPHalftone",
4815   "HPLJDensity",
4816   "HPPhotoHalftone",
4817   "OutputMode",
4818   "REt",
4819   "RPSBitsPerPixel",
4820   "RPSDitherType",
4821   "Resolution",
4822   "ScreenLock",
4823   "Smoothing",
4824   "TonerSaveMode",
4825   "UCRGCRForImage",
4826 };
4827 
4828 /* keep sorted when changing */
4829 static const char *image_quality_group_allow_list[] = {
4830   "FPImageQuality1",
4831   "FPImageQuality2",
4832   "FPImageQuality3",
4833   "ImageQualityPage",
4834 };
4835 
4836 /* keep sorted when changing */
4837 static const char * finishing_option_allow_list[] = {
4838   "BindColor",
4839   "BindEdge",
4840   "BindType",
4841   "BindWhen",
4842   "Booklet",
4843   "FoldType",
4844   "FoldWhen",
4845   "HPStaplerOptions",
4846   "Jog",
4847   "Slipsheet",
4848   "Sorter",
4849   "StapleLocation",
4850   "StapleOrientation",
4851   "StapleWhen",
4852   "StapleX",
4853   "StapleY",
4854 };
4855 
4856 /* keep sorted when changing */
4857 static const char *finishing_group_allow_list[] = {
4858   "FPFinishing1",
4859   "FPFinishing2",
4860   "FPFinishing3",
4861   "FPFinishing4",
4862   "FinishingPage",
4863   "HPFinishingPanel",
4864 };
4865 
4866 /* keep sorted when changing */
4867 static const char *cups_option_ignore_list[] = {
4868   "Collate",
4869   "Copies",
4870   "OutputOrder",
4871   "PageRegion",
4872   "PageSize",
4873 };
4874 
4875 static char *
get_option_text(ppd_file_t * ppd_file,ppd_option_t * option)4876 get_option_text (ppd_file_t   *ppd_file,
4877 		 ppd_option_t *option)
4878 {
4879   int i;
4880   char *utf8;
4881 
4882   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
4883     {
4884       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
4885         return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
4886                                        "printing option",
4887                                        cups_option_translations[i].translation));
4888     }
4889 
4890   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
4891 
4892   /* Some ppd files have spaces in the text before the colon */
4893   g_strchomp (utf8);
4894 
4895   return utf8;
4896 }
4897 
4898 static char *
get_choice_text(ppd_file_t * ppd_file,ppd_choice_t * choice)4899 get_choice_text (ppd_file_t   *ppd_file,
4900 		 ppd_choice_t *choice)
4901 {
4902   int i;
4903   ppd_option_t *option = choice->option;
4904   const char *keyword = option->keyword;
4905 
4906   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
4907     {
4908       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
4909 	  strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
4910         return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
4911                                        "printing option value",
4912                                        cups_choice_translations[i].translation));
4913     }
4914   return ppd_text_to_utf8 (ppd_file, choice->text);
4915 }
4916 
4917 static gboolean
group_has_option(ppd_group_t * group,ppd_option_t * option)4918 group_has_option (ppd_group_t  *group,
4919 		  ppd_option_t *option)
4920 {
4921   int i;
4922 
4923   if (group == NULL)
4924     return FALSE;
4925 
4926   if (group->num_options > 0 &&
4927       option >= group->options && option < group->options + group->num_options)
4928     return TRUE;
4929 
4930   for (i = 0; i < group->num_subgroups; i++)
4931     {
4932       if (group_has_option (&group->subgroups[i],option))
4933 	return TRUE;
4934     }
4935   return FALSE;
4936 }
4937 
4938 static void
set_option_off(GtkPrinterOption * option)4939 set_option_off (GtkPrinterOption *option)
4940 {
4941   /* Any of these will do, _set only applies the value
4942    * if its allowed of the option */
4943   gtk_printer_option_set (option, "False");
4944   gtk_printer_option_set (option, "Off");
4945   gtk_printer_option_set (option, "None");
4946 }
4947 
4948 static gboolean
value_is_off(const char * value)4949 value_is_off (const char *value)
4950 {
4951   return  (strcasecmp (value, "None") == 0 ||
4952 	   strcasecmp (value, "Off") == 0 ||
4953 	   strcasecmp (value, "False") == 0);
4954 }
4955 
4956 static const char *
ppd_group_name(ppd_group_t * group)4957 ppd_group_name (ppd_group_t *group)
4958 {
4959   return group->name;
4960 }
4961 
4962 static int
available_choices(ppd_file_t * ppd,ppd_option_t * option,ppd_choice_t *** available,gboolean keep_if_only_one_option)4963 available_choices (ppd_file_t     *ppd,
4964 		   ppd_option_t   *option,
4965 		   ppd_choice_t ***available,
4966 		   gboolean        keep_if_only_one_option)
4967 {
4968   ppd_option_t *other_option;
4969   int i, j;
4970   char *conflicts;
4971   ppd_const_t *constraint;
4972   const char *choice, *other_choice;
4973   ppd_option_t *option1, *option2;
4974   ppd_group_t *installed_options;
4975   int num_conflicts;
4976   gboolean all_default;
4977   int add_auto;
4978 
4979   if (available)
4980     *available = NULL;
4981 
4982   conflicts = g_new0 (char, option->num_choices);
4983 
4984   installed_options = NULL;
4985   for (i = 0; i < ppd->num_groups; i++)
4986     {
4987       const char *name;
4988 
4989       name = ppd_group_name (&ppd->groups[i]);
4990       if (strcmp (name, "InstallableOptions") == 0)
4991 	{
4992 	  installed_options = &ppd->groups[i];
4993 	  break;
4994 	}
4995     }
4996 
4997 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4998 
4999   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
5000     {
5001       option1 = ppdFindOption (ppd, constraint->option1);
5002       if (option1 == NULL)
5003 	continue;
5004 
5005       option2 = ppdFindOption (ppd, constraint->option2);
5006       if (option2 == NULL)
5007 	continue;
5008 
5009       if (option == option1)
5010 	{
5011 	  choice = constraint->choice1;
5012 	  other_option = option2;
5013 	  other_choice = constraint->choice2;
5014 	}
5015       else if (option == option2)
5016 	{
5017 	  choice = constraint->choice2;
5018 	  other_option = option1;
5019 	  other_choice = constraint->choice1;
5020 	}
5021       else
5022 	continue;
5023 
5024       /* We only care of conflicts with installed_options and PageSize */
5025       if (!group_has_option (installed_options, other_option) &&
5026 	  (strcmp (other_option->keyword, "PageSize") != 0))
5027 	continue;
5028 
5029       if (*other_choice == 0)
5030 	{
5031 	  /* Conflict only if the installed option is not off */
5032 	  if (value_is_off (other_option->defchoice))
5033 	    continue;
5034 	}
5035       /* Conflict if the installed option has the specified default */
5036       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
5037 	continue;
5038 
5039       if (*choice == 0)
5040 	{
5041 	  /* Conflict with all non-off choices */
5042 	  for (j = 0; j < option->num_choices; j++)
5043 	    {
5044 	      if (!value_is_off (option->choices[j].choice))
5045 		conflicts[j] = 1;
5046 	    }
5047 	}
5048       else
5049 	{
5050 	  for (j = 0; j < option->num_choices; j++)
5051 	    {
5052 	      if (strcasecmp (option->choices[j].choice, choice) == 0)
5053 		conflicts[j] = 1;
5054 	    }
5055 	}
5056     }
5057 
5058 G_GNUC_END_IGNORE_DEPRECATIONS
5059 
5060   num_conflicts = 0;
5061   all_default = TRUE;
5062   for (j = 0; j < option->num_choices; j++)
5063     {
5064       if (conflicts[j])
5065 	num_conflicts++;
5066       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
5067 	all_default = FALSE;
5068     }
5069 
5070   if ((all_default && !keep_if_only_one_option) ||
5071       (num_conflicts == option->num_choices))
5072     {
5073       g_free (conflicts);
5074 
5075       return 0;
5076     }
5077 
5078   /* Some ppds don't have a "use printer default" option for
5079    * InputSlot. This means you always have to select a particular slot,
5080    * and you can't auto-pick source based on the paper size. To support
5081    * this we always add an auto option if there isn't one already. If
5082    * the user chooses the generated option we don't send any InputSlot
5083    * value when printing. The way we detect existing auto-cases is based
5084    * on feedback from Michael Sweet of cups fame.
5085    */
5086   add_auto = 0;
5087   if (strcmp (option->keyword, "InputSlot") == 0)
5088     {
5089       gboolean found_auto = FALSE;
5090       for (j = 0; j < option->num_choices; j++)
5091 	{
5092 	  if (!conflicts[j])
5093 	    {
5094 	      if (strcmp (option->choices[j].choice, "Auto") == 0 ||
5095 		  strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
5096 		  strcmp (option->choices[j].choice, "Default") == 0 ||
5097 		  strcmp (option->choices[j].choice, "None") == 0 ||
5098 		  strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
5099 		  strcmp (option->choices[j].choice, "Unspecified") == 0 ||
5100 		  option->choices[j].code == NULL ||
5101 		  option->choices[j].code[0] == 0)
5102 		{
5103 		  found_auto = TRUE;
5104 		  break;
5105 		}
5106 	    }
5107 	}
5108 
5109       if (!found_auto)
5110 	add_auto = 1;
5111     }
5112 
5113   if (available)
5114     {
5115       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
5116 
5117       i = 0;
5118       for (j = 0; j < option->num_choices; j++)
5119 	{
5120 	  if (!conflicts[j])
5121 	    (*available)[i++] = &option->choices[j];
5122 	}
5123 
5124       if (add_auto)
5125 	(*available)[i++] = NULL;
5126     }
5127 
5128   g_free (conflicts);
5129 
5130   return option->num_choices - num_conflicts + add_auto;
5131 }
5132 
5133 static GtkPrinterOption *
create_pickone_option(ppd_file_t * ppd_file,ppd_option_t * ppd_option,const char * gtk_name)5134 create_pickone_option (ppd_file_t   *ppd_file,
5135 		       ppd_option_t *ppd_option,
5136 		       const char   *gtk_name)
5137 {
5138   GtkPrinterOption *option;
5139   ppd_choice_t **available;
5140   char *label;
5141   int n_choices;
5142   int i;
5143   ppd_coption_t *coption;
5144 
5145   g_assert (ppd_option->ui == PPD_UI_PICKONE);
5146 
5147   option = NULL;
5148 
5149 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
5150 
5151   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
5152   if (n_choices > 0)
5153     {
5154 
5155       /* right now only support one parameter per custom option
5156        * if more than one print warning and only offer the default choices
5157        */
5158 
5159       label = get_option_text (ppd_file, ppd_option);
5160 
5161       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
5162 
5163       if (coption)
5164         {
5165 	  ppd_cparam_t *cparam;
5166 
5167           cparam = ppdFirstCustomParam (coption);
5168 
5169           if (ppdNextCustomParam (coption) == NULL)
5170 	    {
5171               switch ((guint)cparam->type)
5172 	        {
5173                 case PPD_CUSTOM_INT:
5174 		  option = gtk_printer_option_new (gtk_name, label,
5175 				         GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
5176 		  break;
5177                 case PPD_CUSTOM_PASSCODE:
5178 		  option = gtk_printer_option_new (gtk_name, label,
5179 				         GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
5180 		  break;
5181                 case PPD_CUSTOM_PASSWORD:
5182 		    option = gtk_printer_option_new (gtk_name, label,
5183 				         GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
5184 		  break;
5185                case PPD_CUSTOM_REAL:
5186 		    option = gtk_printer_option_new (gtk_name, label,
5187 				         GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
5188 		  break;
5189                 case PPD_CUSTOM_STRING:
5190 		  option = gtk_printer_option_new (gtk_name, label,
5191 				         GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
5192 		  break;
5193 #ifdef PRINT_IGNORED_OPTIONS
5194                 case PPD_CUSTOM_POINTS:
5195 		  g_warning ("CUPS Backend: PPD Custom Points Option not supported");
5196 		  break;
5197                 case PPD_CUSTOM_CURVE:
5198                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
5199 		  break;
5200                 case PPD_CUSTOM_INVCURVE:
5201 		  g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
5202 		  break;
5203 #endif
5204                 default:
5205                   break;
5206 		}
5207 	    }
5208 #ifdef PRINT_IGNORED_OPTIONS
5209 	  else
5210 	    g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
5211 #endif
5212 	}
5213 
5214       if (!option)
5215         option = gtk_printer_option_new (gtk_name, label,
5216 				         GTK_PRINTER_OPTION_TYPE_PICKONE);
5217       g_free (label);
5218 
5219       gtk_printer_option_allocate_choices (option, n_choices);
5220       for (i = 0; i < n_choices; i++)
5221 	{
5222 	  if (available[i] == NULL)
5223 	    {
5224 	      /* This was auto-added */
5225 	      option->choices[i] = g_strdup ("gtk-ignore-value");
5226 	      option->choices_display[i] = g_strdup (_("Printer Default"));
5227 	    }
5228 	  else
5229 	    {
5230 	      option->choices[i] = g_strdup (available[i]->choice);
5231 	      option->choices_display[i] = get_choice_text (ppd_file, available[i]);
5232 	    }
5233 	}
5234 
5235       if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
5236         {
5237           if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
5238             gtk_printer_option_set (option, ppd_option->defchoice + 7);
5239           else
5240             gtk_printer_option_set (option, ppd_option->defchoice);
5241         }
5242       else
5243         {
5244           gtk_printer_option_set (option, ppd_option->defchoice);
5245         }
5246     }
5247 #ifdef PRINT_IGNORED_OPTIONS
5248   else
5249     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
5250 #endif
5251 
5252 G_GNUC_END_IGNORE_DEPRECATIONS
5253 
5254   g_free (available);
5255 
5256   return option;
5257 }
5258 
5259 static GtkPrinterOption *
create_boolean_option(ppd_file_t * ppd_file,ppd_option_t * ppd_option,const char * gtk_name)5260 create_boolean_option (ppd_file_t   *ppd_file,
5261 		       ppd_option_t *ppd_option,
5262 		       const char   *gtk_name)
5263 {
5264   GtkPrinterOption *option;
5265   ppd_choice_t **available;
5266   char *label;
5267   int n_choices;
5268 
5269   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
5270 
5271   option = NULL;
5272 
5273   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
5274   if (n_choices == 2)
5275     {
5276       label = get_option_text (ppd_file, ppd_option);
5277       option = gtk_printer_option_new (gtk_name, label,
5278 				       GTK_PRINTER_OPTION_TYPE_BOOLEAN);
5279       g_free (label);
5280 
5281       gtk_printer_option_allocate_choices (option, 2);
5282       option->choices[0] = g_strdup ("True");
5283       option->choices_display[0] = g_strdup ("True");
5284       option->choices[1] = g_strdup ("False");
5285       option->choices_display[1] = g_strdup ("False");
5286 
5287       gtk_printer_option_set (option, ppd_option->defchoice);
5288     }
5289 #ifdef PRINT_IGNORED_OPTIONS
5290   else
5291     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
5292 #endif
5293   g_free (available);
5294 
5295   return option;
5296 }
5297 
5298 static char *
get_ppd_option_name(const char * keyword)5299 get_ppd_option_name (const char *keyword)
5300 {
5301   int i;
5302 
5303   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
5304     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
5305       return g_strdup (ppd_option_names[i].name);
5306 
5307   return g_strdup_printf ("cups-%s", keyword);
5308 }
5309 
5310 static char *
get_lpoption_name(const char * lpoption)5311 get_lpoption_name (const char *lpoption)
5312 {
5313   int i;
5314 
5315   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
5316     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
5317       return g_strdup (ppd_option_names[i].name);
5318 
5319   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
5320     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
5321       return g_strdup (lpoption_names[i].name);
5322 
5323   return g_strdup_printf ("cups-%s", lpoption);
5324 }
5325 
5326 static int
strptr_cmp(const void * a,const void * b)5327 strptr_cmp (const void *a,
5328 	    const void *b)
5329 {
5330   char **aa = (char **)a;
5331   char **bb = (char **)b;
5332   return strcmp (*aa, *bb);
5333 }
5334 
5335 
5336 static gboolean
string_in_table(const char * str,const char * table[],int table_len)5337 string_in_table (const char *str,
5338 		 const char *table[],
5339 		 int          table_len)
5340 {
5341   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
5342 }
5343 
5344 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
5345 
5346 static void
handle_option(GtkPrinterOptionSet * set,ppd_file_t * ppd_file,ppd_option_t * ppd_option,ppd_group_t * toplevel_group,GtkPrintSettings * settings)5347 handle_option (GtkPrinterOptionSet *set,
5348 	       ppd_file_t          *ppd_file,
5349 	       ppd_option_t        *ppd_option,
5350 	       ppd_group_t         *toplevel_group,
5351 	       GtkPrintSettings    *settings)
5352 {
5353   GtkPrinterOption *option;
5354   char *option_name;
5355   int i;
5356 
5357   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_ignore_list))
5358     return;
5359 
5360   option_name = get_ppd_option_name (ppd_option->keyword);
5361 
5362   option = NULL;
5363   if (ppd_option->ui == PPD_UI_PICKONE)
5364     option = create_pickone_option (ppd_file, ppd_option, option_name);
5365   else if (ppd_option->ui == PPD_UI_BOOLEAN)
5366     option = create_boolean_option (ppd_file, ppd_option, option_name);
5367 #ifdef PRINT_IGNORED_OPTIONS
5368   else
5369     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
5370 #endif
5371 
5372   if (option)
5373     {
5374       const char *name;
5375 
5376       name = ppd_group_name (toplevel_group);
5377       if (STRING_IN_TABLE (name, color_group_allow_list) ||
5378 	  STRING_IN_TABLE (ppd_option->keyword, color_option_allow_list))
5379 	{
5380 	  option->group = g_strdup ("ColorPage");
5381 	}
5382       else if (STRING_IN_TABLE (name, image_quality_group_allow_list) ||
5383 	       STRING_IN_TABLE (ppd_option->keyword, image_quality_option_allow_list))
5384 	{
5385 	  option->group = g_strdup ("ImageQualityPage");
5386 	}
5387       else if (STRING_IN_TABLE (name, finishing_group_allow_list) ||
5388 	       STRING_IN_TABLE (ppd_option->keyword, finishing_option_allow_list))
5389 	{
5390 	  option->group = g_strdup ("FinishingPage");
5391 	}
5392       else
5393 	{
5394 	  for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
5395 	    {
5396 	      if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
5397 		{
5398                   option->group = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
5399                                                           "printing option group",
5400                                                           cups_group_translations[i].translation));
5401 		  break;
5402 		}
5403 	    }
5404 
5405 	  if (i == G_N_ELEMENTS (cups_group_translations))
5406 	    option->group = g_strdup (toplevel_group->text);
5407 	}
5408 
5409       set_option_from_settings (option, settings);
5410 
5411       gtk_printer_option_set_add (set, option);
5412     }
5413 
5414   g_free (option_name);
5415 }
5416 
5417 static void
handle_group(GtkPrinterOptionSet * set,ppd_file_t * ppd_file,ppd_group_t * group,ppd_group_t * toplevel_group,GtkPrintSettings * settings)5418 handle_group (GtkPrinterOptionSet *set,
5419 	      ppd_file_t          *ppd_file,
5420 	      ppd_group_t         *group,
5421 	      ppd_group_t         *toplevel_group,
5422 	      GtkPrintSettings    *settings)
5423 {
5424   int i;
5425   const char *name;
5426 
5427   /* Ignore installable options */
5428   name = ppd_group_name (toplevel_group);
5429   if (strcmp (name, "InstallableOptions") == 0)
5430     return;
5431 
5432   for (i = 0; i < group->num_options; i++)
5433     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
5434 
5435   for (i = 0; i < group->num_subgroups; i++)
5436     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
5437 
5438 }
5439 
5440 #ifdef HAVE_COLORD
5441 
5442 typedef struct {
5443         GtkPrintSettings     *settings;
5444         GtkPrinter           *printer;
5445 } GtkPrintBackendCupsColordHelper;
5446 
5447 static void
colord_printer_option_set_changed_cb(GtkPrinterOptionSet * set,GtkPrintBackendCupsColordHelper * helper)5448 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
5449                                       GtkPrintBackendCupsColordHelper *helper)
5450 {
5451   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
5452                                     helper->settings,
5453                                     set);
5454 }
5455 #endif
5456 
5457 /*
5458  * Lookup translation and GTK name of given IPP option name.
5459  */
5460 static gboolean
get_ipp_option_translation(const char * ipp_option_name,char ** gtk_option_name,char ** translation)5461 get_ipp_option_translation (const char   *ipp_option_name,
5462                             char        **gtk_option_name,
5463                             char        **translation)
5464 {
5465   int i;
5466 
5467   *gtk_option_name = NULL;
5468   *translation = NULL;
5469 
5470   for (i = 0; i < G_N_ELEMENTS (ipp_option_translations); i++)
5471     {
5472       if (g_strcmp0 (ipp_option_translations[i].ipp_option_name, ipp_option_name) == 0)
5473         {
5474           *gtk_option_name = g_strdup (ipp_option_translations[i].gtk_option_name);
5475           *translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
5476                                                  "printing option",
5477                                                  ipp_option_translations[i].translation));
5478           return TRUE;
5479         }
5480     }
5481 
5482   return FALSE;
5483 }
5484 
5485 /*
5486  * Lookup translation of given IPP choice.
5487  */
5488 static char *
get_ipp_choice_translation(const char * ipp_option_name,const char * ipp_choice)5489 get_ipp_choice_translation (const char   *ipp_option_name,
5490                             const char   *ipp_choice)
5491 {
5492   const char *nptr;
5493   guint64      index;
5494   char        *translation = NULL;
5495   gsize        ipp_choice_length;
5496   char        *endptr;
5497   int          i;
5498 
5499   for (i = 0; ipp_choice_translations[i].ipp_option_name != NULL; i++)
5500     {
5501       if (g_strcmp0 (ipp_choice_translations[i].ipp_option_name, ipp_option_name) == 0)
5502         {
5503           ipp_choice_length = strlen (ipp_choice_translations[i].ipp_choice);
5504 
5505           if (g_strcmp0 (ipp_choice_translations[i].ipp_choice, ipp_choice) == 0)
5506             {
5507               translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
5508                                                     ipp_option_name,
5509                                                     ipp_choice_translations[i].translation));
5510               break;
5511             }
5512           else if (g_str_has_suffix (ipp_choice_translations[i].ipp_choice, "-N") &&
5513                    g_ascii_strncasecmp (ipp_choice_translations[i].ipp_choice,
5514                                         ipp_choice,
5515                                         ipp_choice_length - 2) == 0)
5516             {
5517               /* Find out index of the ipp_choice if it is supported for the choice. */
5518               endptr = NULL;
5519               nptr = ipp_choice + ipp_choice_length - 1;
5520               index = g_ascii_strtoull (nptr,
5521                                         &endptr,
5522                                         10);
5523 
5524               if (index != 0 || endptr != nptr)
5525                 {
5526                   translation = get_ipp_choice_translation_string (index, i);
5527                   break;
5528                 }
5529             }
5530         }
5531     }
5532 
5533   return translation;
5534 }
5535 
5536 /*
5537  * Format an IPP choice to a displayable string.
5538  */
5539 static char *
format_ipp_choice(const char * ipp_choice)5540 format_ipp_choice (const char *ipp_choice)
5541 {
5542   gboolean  after_space = TRUE;
5543   char     *result = NULL;
5544   gsize     i;
5545 
5546   if (ipp_choice != NULL)
5547     {
5548       result = g_strdup (ipp_choice);
5549       /* Replace all '-' by spaces. */
5550       result = g_strdelimit (result, "-", ' ');
5551       if (g_str_is_ascii (result))
5552         {
5553           /* Convert all leading characters to upper case. */
5554           for (i = 0; i < strlen (result); i++)
5555             {
5556               if (after_space && g_ascii_isalpha (result[i]))
5557                 result[i] = g_ascii_toupper (result[i]);
5558 
5559               after_space = g_ascii_isspace (result[i]);
5560             }
5561         }
5562     }
5563 
5564   return result;
5565 }
5566 
5567 /*
5568  * Look the IPP option up in given set of options.
5569  * Create it if it doesn't exist and set its default value
5570  * if available.
5571  */
5572 static GtkPrinterOption *
setup_ipp_option(const char * ipp_option_name,const char * ipp_choice_default,GList * ipp_choices,GtkPrinterOptionSet * set)5573 setup_ipp_option (const char          *ipp_option_name,
5574                   const char          *ipp_choice_default,
5575                   GList               *ipp_choices,
5576                   GtkPrinterOptionSet *set)
5577 {
5578   GtkPrinterOption *option = NULL;
5579   char             *gtk_option_name = NULL;
5580   char             *translation = NULL;
5581   char             *ipp_choice;
5582   gsize             i;
5583 
5584   get_ipp_option_translation (ipp_option_name,
5585                               &gtk_option_name,
5586                               &translation);
5587 
5588   /* Look the option up in the given set of options. */
5589   if (gtk_option_name != NULL)
5590     option = gtk_printer_option_set_lookup (set, gtk_option_name);
5591 
5592   /* The option was not found, create it from given choices. */
5593   if (option == NULL &&
5594       ipp_choices != NULL)
5595     {
5596       GList  *iter;
5597       gsize   length;
5598       char  **choices = NULL;
5599       char  **choices_display = NULL;
5600 
5601       option = gtk_printer_option_new (gtk_option_name,
5602                                        translation,
5603                                        GTK_PRINTER_OPTION_TYPE_PICKONE);
5604 
5605       length = g_list_length (ipp_choices);
5606 
5607       choices = g_new0 (char *, length);
5608       choices_display = g_new0 (char *, length);
5609 
5610       i = 0;
5611       for (iter = ipp_choices; iter != NULL; iter = iter->next)
5612         {
5613           ipp_choice = (char *) iter->data;
5614 
5615           choices[i] = g_strdup (ipp_choice);
5616 
5617           translation = get_ipp_choice_translation (ipp_option_name,
5618                                                     ipp_choice);
5619           if (translation != NULL)
5620             choices_display[i] = translation;
5621           else
5622             choices_display[i] = format_ipp_choice (ipp_choice);
5623 
5624           i++;
5625         }
5626 
5627       if (choices != NULL &&
5628           choices_display != NULL)
5629         {
5630           gtk_printer_option_choices_from_array (option,
5631                                                  length,
5632                                                  (const char **)choices,
5633                                                  (const char **)choices_display);
5634         }
5635 
5636       option_set_is_ipp_option (option, TRUE);
5637 
5638       gtk_printer_option_set_add (set, option);
5639 
5640       g_strfreev (choices);
5641       g_strfreev (choices_display);
5642     }
5643 
5644   /* The option exists. Set its default value if available. */
5645   if (option != NULL &&
5646       ipp_choice_default != NULL)
5647     {
5648       gtk_printer_option_set (option, ipp_choice_default);
5649     }
5650 
5651   return option;
5652 }
5653 
5654 static GtkPrinterOptionSet *
cups_printer_get_options(GtkPrinter * printer,GtkPrintSettings * settings,GtkPageSetup * page_setup,GtkPrintCapabilities capabilities)5655 cups_printer_get_options (GtkPrinter           *printer,
5656 			  GtkPrintSettings     *settings,
5657 			  GtkPageSetup         *page_setup,
5658 			  GtkPrintCapabilities  capabilities)
5659 {
5660   GtkPrinterOptionSet *set;
5661   GtkPrinterOption *option;
5662   ppd_file_t *ppd_file;
5663   int i;
5664   const char *print_at[] = { "now", "at", "on-hold" };
5665   const char *n_up[] = {"1", "2", "4", "6", "9", "16" };
5666   const char *prio[] = {"100", "80", "50", "30" };
5667   /* Translators: These strings name the possible values of the
5668    * job priority option in the print dialog
5669    */
5670   const char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
5671   const char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
5672   /* Translators: These strings name the possible arrangements of
5673    * multiple pages on a sheet when printing
5674    */
5675   const char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
5676                                         N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
5677                                         N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
5678                                         N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
5679   char *name;
5680   int num_opts;
5681   cups_option_t *opts = NULL;
5682   GtkPrintBackendCups *backend;
5683   GtkTextDirection text_direction;
5684   GtkPrinterCups *cups_printer = NULL;
5685 #ifdef HAVE_COLORD
5686   GtkPrintBackendCupsColordHelper *helper;
5687 #endif
5688   char *default_number_up;
5689 
5690   set = gtk_printer_option_set_new ();
5691 
5692   /* Cups specific, non-ppd related settings */
5693 
5694   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
5695     prio_display[i] = _(prio_display[i]);
5696 
5697   /* Translators, this string is used to label the job priority option
5698    * in the print dialog
5699    */
5700   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
5701   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
5702 					 prio, prio_display);
5703   gtk_printer_option_set (option, "50");
5704   set_option_from_settings (option, settings);
5705   gtk_printer_option_set_add (set, option);
5706   g_object_unref (option);
5707 
5708   /* Translators, this string is used to label the billing info entry
5709    * in the print dialog
5710    */
5711   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
5712   gtk_printer_option_set (option, "");
5713   set_option_from_settings (option, settings);
5714   gtk_printer_option_set_add (set, option);
5715   g_object_unref (option);
5716 
5717   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
5718   cups_printer = GTK_PRINTER_CUPS (printer);
5719 
5720   if (backend != NULL && printer != NULL)
5721     {
5722       const char *cover_default[] = {
5723         "none",
5724         "classified",
5725         "confidential",
5726         "secret",
5727         "standard",
5728         "topsecret",
5729         "unclassified"
5730       };
5731       const char *cover_display_default[] = {
5732         /* Translators, these strings are names for various 'standard' cover
5733          * pages that the printing system may support.
5734          */
5735         NC_("cover page", "None"),
5736         NC_("cover page", "Classified"),
5737         NC_("cover page", "Confidential"),
5738         NC_("cover page", "Secret"),
5739         NC_("cover page", "Standard"),
5740         NC_("cover page", "Top Secret"),
5741         NC_("cover page", "Unclassified")
5742       };
5743       char **cover = NULL;
5744       char **cover_display = NULL;
5745       char **cover_display_translated = NULL;
5746       int num_of_covers = 0;
5747       gconstpointer value;
5748       int j;
5749 
5750        /* Translators, this string is used to label the pages-per-sheet option
5751         * in the print dialog
5752         */
5753       option = gtk_printer_option_new ("gtk-n-up", C_("printer option", "Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
5754       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), n_up, n_up);
5755       default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
5756       gtk_printer_option_set (option, default_number_up);
5757       g_free (default_number_up);
5758       set_option_from_settings (option, settings);
5759       gtk_printer_option_set_add (set, option);
5760       g_object_unref (option);
5761 
5762       if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
5763         {
5764           for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
5765             n_up_layout_display[i] = _(n_up_layout_display[i]);
5766 
5767            /* Translators, this string is used to label the option in the print
5768             * dialog that controls in what order multiple pages are arranged
5769             */
5770           option = gtk_printer_option_new ("gtk-n-up-layout", C_("printer option", "Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
5771           gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
5772                                                  n_up_layout, n_up_layout_display);
5773 
5774           text_direction = gtk_widget_get_default_direction ();
5775           if (text_direction == GTK_TEXT_DIR_LTR)
5776             gtk_printer_option_set (option, "lrtb");
5777           else
5778             gtk_printer_option_set (option, "rltb");
5779 
5780           set_option_from_settings (option, settings);
5781           gtk_printer_option_set_add (set, option);
5782           g_object_unref (option);
5783         }
5784 
5785       num_of_covers = cups_printer->number_of_covers;
5786       cover = g_new (char *, num_of_covers + 1);
5787       cover[num_of_covers] = NULL;
5788       cover_display = g_new (char *, num_of_covers + 1);
5789       cover_display[num_of_covers] = NULL;
5790       cover_display_translated = g_new (char *, num_of_covers + 1);
5791       cover_display_translated[num_of_covers] = NULL;
5792 
5793       for (i = 0; i < num_of_covers; i++)
5794         {
5795           cover[i] = g_strdup (cups_printer->covers[i]);
5796           value = NULL;
5797           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
5798             if (strcmp (cover_default[j], cover[i]) == 0)
5799               {
5800                 value = cover_display_default[j];
5801                 break;
5802               }
5803           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (cups_printer->covers[i]);
5804         }
5805 
5806       for (i = 0; i < num_of_covers; i++)
5807         cover_display_translated[i] = (char *)g_dpgettext2 (GETTEXT_PACKAGE, "cover page", cover_display[i]);
5808 
5809       /* Translators, this is the label used for the option in the print
5810        * dialog that controls the front cover page.
5811        */
5812       option = gtk_printer_option_new ("gtk-cover-before", C_("printer option", "Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
5813       gtk_printer_option_choices_from_array (option, num_of_covers,
5814                                              (const char **)cover, (const char **)cover_display_translated);
5815 
5816       if (cups_printer->default_cover_before != NULL)
5817         gtk_printer_option_set (option, cups_printer->default_cover_before);
5818       else
5819         gtk_printer_option_set (option, "none");
5820       set_option_from_settings (option, settings);
5821       gtk_printer_option_set_add (set, option);
5822       g_object_unref (option);
5823 
5824       /* Translators, this is the label used for the option in the print
5825        * dialog that controls the back cover page.
5826        */
5827       option = gtk_printer_option_new ("gtk-cover-after", C_("printer option", "After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
5828       gtk_printer_option_choices_from_array (option, num_of_covers,
5829                                              (const char **)cover, (const char **)cover_display_translated);
5830       if (cups_printer->default_cover_after != NULL)
5831         gtk_printer_option_set (option, cups_printer->default_cover_after);
5832       else
5833         gtk_printer_option_set (option, "none");
5834       set_option_from_settings (option, settings);
5835       gtk_printer_option_set_add (set, option);
5836       g_object_unref (option);
5837 
5838       g_strfreev (cover);
5839       g_strfreev (cover_display);
5840       g_free (cover_display_translated);
5841     }
5842 
5843   /* Translators: this is the name of the option that controls when
5844    * a print job is printed. Possible values are 'now', a specified time,
5845    * or 'on hold'
5846    */
5847   option = gtk_printer_option_new ("gtk-print-time", C_("printer option", "Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
5848   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
5849 					 print_at, print_at);
5850   gtk_printer_option_set (option, "now");
5851   set_option_from_settings (option, settings);
5852   gtk_printer_option_set_add (set, option);
5853   g_object_unref (option);
5854 
5855   /* Translators: this is the name of the option that allows the user
5856    * to specify a time when a print job will be printed.
5857    */
5858   option = gtk_printer_option_new ("gtk-print-time-text", C_("printer option", "Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
5859   gtk_printer_option_set (option, "");
5860   set_option_from_settings (option, settings);
5861   gtk_printer_option_set_add (set, option);
5862   g_object_unref (option);
5863 
5864   /* Printer (ppd) specific settings */
5865   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
5866   if (ppd_file)
5867     {
5868       GtkPaperSize *paper_size;
5869       ppd_option_t *ppd_option;
5870       const char *ppd_name;
5871 
5872       G_GNUC_BEGIN_IGNORE_DEPRECATIONS
5873 
5874       ppdMarkDefaults (ppd_file);
5875 
5876       paper_size = gtk_page_setup_get_paper_size (page_setup);
5877 
5878       ppd_option = ppdFindOption (ppd_file, "PageSize");
5879       if (ppd_option)
5880 	{
5881 	  ppd_name = gtk_paper_size_get_ppd_name (paper_size);
5882 
5883 	  if (ppd_name)
5884             {
5885               strncpy (ppd_option->defchoice, ppd_name, PPD_MAX_NAME - 1);
5886               ppd_option->defchoice[PPD_MAX_NAME - 1] = '\0';
5887             }
5888 	  else
5889 	    {
5890 	      char *custom_name;
5891 	      char width[G_ASCII_DTOSTR_BUF_SIZE];
5892 	      char height[G_ASCII_DTOSTR_BUF_SIZE];
5893 
5894 	      g_ascii_formatd (width, sizeof (width), "%.2f",
5895 			       gtk_paper_size_get_width (paper_size,
5896 							 GTK_UNIT_POINTS));
5897 	      g_ascii_formatd (height, sizeof (height), "%.2f",
5898 			       gtk_paper_size_get_height (paper_size,
5899 							  GTK_UNIT_POINTS));
5900 	      /* Translators: this format is used to display a custom
5901 	       * paper size. The two placeholders are replaced with
5902 	       * the width and height in points. E.g: "Custom
5903 	       * 230.4x142.9"
5904                */
5905 	      custom_name = g_strdup_printf (_("Custom %s×%s"), width, height);
5906               strncpy (ppd_option->defchoice, custom_name, PPD_MAX_NAME - 1);
5907               ppd_option->defchoice[PPD_MAX_NAME - 1] = '\0';
5908 	      g_free (custom_name);
5909 	    }
5910 	}
5911       G_GNUC_END_IGNORE_DEPRECATIONS
5912 
5913       for (i = 0; i < ppd_file->num_groups; i++)
5914         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
5915     }
5916   else
5917     {
5918       /* Try IPP options */
5919 
5920       option = setup_ipp_option ("sides",
5921                                  cups_printer->sides_default,
5922                                  cups_printer->sides_supported,
5923                                  set);
5924 
5925       if (option != NULL)
5926         set_option_from_settings (option, settings);
5927 
5928       option = setup_ipp_option ("output-bin",
5929                                  cups_printer->output_bin_default,
5930                                  cups_printer->output_bin_supported,
5931                                  set);
5932 
5933       if (option != NULL)
5934         set_option_from_settings (option, settings);
5935     }
5936 
5937   /* Now honor the user set defaults for this printer */
5938   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
5939 
5940   for (i = 0; i < num_opts; i++)
5941     {
5942       if (STRING_IN_TABLE (opts[i].name, cups_option_ignore_list))
5943         continue;
5944 
5945       name = get_lpoption_name (opts[i].name);
5946       if (strcmp (name, "cups-job-sheets") == 0)
5947         {
5948           char **values;
5949           int     num_values;
5950 
5951           values = g_strsplit (opts[i].value, ",", 2);
5952           num_values = g_strv_length (values);
5953 
5954           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
5955           if (option && num_values > 0)
5956             gtk_printer_option_set (option, g_strstrip (values[0]));
5957 
5958           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
5959           if (option && num_values > 1)
5960             gtk_printer_option_set (option, g_strstrip (values[1]));
5961 
5962           g_strfreev (values);
5963         }
5964       else if (strcmp (name, "cups-job-hold-until") == 0)
5965         {
5966           GtkPrinterOption *option2 = NULL;
5967 
5968           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
5969           if (option && opts[i].value)
5970             {
5971               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
5972               if (option2)
5973                 {
5974                   if (strcmp (opts[i].value, "indefinite") == 0)
5975                     gtk_printer_option_set (option2, "on-hold");
5976                   else
5977                     {
5978                       gtk_printer_option_set (option2, "at");
5979                       gtk_printer_option_set (option, opts[i].value);
5980                     }
5981                 }
5982             }
5983         }
5984       else if (strcmp (name, "cups-sides") == 0)
5985         {
5986           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
5987           if (option && opts[i].value)
5988             {
5989               if (!option_is_ipp_option (option))
5990                 {
5991                   if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
5992                     gtk_printer_option_set (option, "DuplexTumble");
5993                   else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
5994                     gtk_printer_option_set (option, "DuplexNoTumble");
5995                 }
5996               else
5997                 {
5998                   gtk_printer_option_set (option, opts[i].value);
5999                 }
6000             }
6001         }
6002       else
6003         {
6004           option = gtk_printer_option_set_lookup (set, name);
6005           if (option)
6006             gtk_printer_option_set (option, opts[i].value);
6007         }
6008       g_free (name);
6009     }
6010 
6011   cupsFreeOptions (num_opts, opts);
6012 
6013 #ifdef HAVE_COLORD
6014   option = gtk_printer_option_new ("colord-profile",
6015                                    /* TRANSLATORS: this is the ICC color profile to use for this job */
6016                                    C_("printer option", "Printer Profile"),
6017                                    GTK_PRINTER_OPTION_TYPE_INFO);
6018 
6019   /* assign it to the color page */
6020   option->group = g_strdup ("ColorPage");
6021 
6022   /* TRANSLATORS: this is when color profile information is unavailable */
6023   gtk_printer_option_set (option, C_("printer option value", "Unavailable"));
6024   gtk_printer_option_set_add (set, option);
6025 
6026   /* watch to see if the user changed the options */
6027   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
6028   helper->printer = printer;
6029   helper->settings = settings;
6030   g_signal_connect_data (set, "changed",
6031                          G_CALLBACK (colord_printer_option_set_changed_cb),
6032                          helper,
6033                          (GClosureNotify) g_free,
6034                          0);
6035 
6036   /* initial coldplug */
6037   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
6038                                     settings, set);
6039   g_object_bind_property (printer, "profile-title",
6040                           option, "value",
6041                           G_BINDING_DEFAULT);
6042 
6043 #endif
6044 
6045   return set;
6046 }
6047 
6048 
6049 static void
mark_option_from_set(GtkPrinterOptionSet * set,ppd_file_t * ppd_file,ppd_option_t * ppd_option)6050 mark_option_from_set (GtkPrinterOptionSet *set,
6051 		      ppd_file_t          *ppd_file,
6052 		      ppd_option_t        *ppd_option)
6053 {
6054   GtkPrinterOption *option;
6055   char *name = get_ppd_option_name (ppd_option->keyword);
6056 
6057   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
6058 
6059   option = gtk_printer_option_set_lookup (set, name);
6060 
6061   if (option)
6062     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
6063 
6064   G_GNUC_END_IGNORE_DEPRECATIONS
6065 
6066   g_free (name);
6067 }
6068 
6069 
6070 static void
mark_group_from_set(GtkPrinterOptionSet * set,ppd_file_t * ppd_file,ppd_group_t * group)6071 mark_group_from_set (GtkPrinterOptionSet *set,
6072 		     ppd_file_t          *ppd_file,
6073 		     ppd_group_t         *group)
6074 {
6075   int i;
6076 
6077   for (i = 0; i < group->num_options; i++)
6078     mark_option_from_set (set, ppd_file, &group->options[i]);
6079 
6080   for (i = 0; i < group->num_subgroups; i++)
6081     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
6082 }
6083 
6084 static void
set_conflicts_from_option(GtkPrinterOptionSet * set,ppd_file_t * ppd_file,ppd_option_t * ppd_option)6085 set_conflicts_from_option (GtkPrinterOptionSet *set,
6086 			   ppd_file_t          *ppd_file,
6087 			   ppd_option_t        *ppd_option)
6088 {
6089   GtkPrinterOption *option;
6090   char *name;
6091 
6092   if (ppd_option->conflicted)
6093     {
6094       name = get_ppd_option_name (ppd_option->keyword);
6095       option = gtk_printer_option_set_lookup (set, name);
6096 
6097       if (option)
6098 	gtk_printer_option_set_has_conflict (option, TRUE);
6099 #ifdef PRINT_IGNORED_OPTIONS
6100       else
6101 	g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
6102 #endif
6103 
6104       g_free (name);
6105     }
6106 }
6107 
6108 static void
set_conflicts_from_group(GtkPrinterOptionSet * set,ppd_file_t * ppd_file,ppd_group_t * group)6109 set_conflicts_from_group (GtkPrinterOptionSet *set,
6110 			  ppd_file_t          *ppd_file,
6111 			  ppd_group_t         *group)
6112 {
6113   int i;
6114 
6115   for (i = 0; i < group->num_options; i++)
6116     set_conflicts_from_option (set, ppd_file, &group->options[i]);
6117 
6118   for (i = 0; i < group->num_subgroups; i++)
6119     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
6120 }
6121 
6122 static gboolean
cups_printer_mark_conflicts(GtkPrinter * printer,GtkPrinterOptionSet * options)6123 cups_printer_mark_conflicts (GtkPrinter          *printer,
6124 			     GtkPrinterOptionSet *options)
6125 {
6126   ppd_file_t *ppd_file;
6127   int num_conflicts;
6128   int i;
6129 
6130   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
6131 
6132   if (ppd_file == NULL)
6133     return FALSE;
6134 
6135   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
6136 
6137   ppdMarkDefaults (ppd_file);
6138 
6139   for (i = 0; i < ppd_file->num_groups; i++)
6140     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
6141 
6142   num_conflicts = ppdConflicts (ppd_file);
6143 
6144   if (num_conflicts > 0)
6145     {
6146       for (i = 0; i < ppd_file->num_groups; i++)
6147 	set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
6148     }
6149 
6150   G_GNUC_END_IGNORE_DEPRECATIONS
6151 
6152   return num_conflicts > 0;
6153 }
6154 
6155 struct OptionData {
6156   GtkPrinter *printer;
6157   GtkPrinterOptionSet *options;
6158   GtkPrintSettings *settings;
6159   ppd_file_t *ppd_file;
6160 };
6161 
6162 typedef struct {
6163   const char *cups;
6164   const char *standard;
6165 } NameMapping;
6166 
6167 static void
map_settings_to_option(GtkPrinterOption * option,const NameMapping table[],int n_elements,GtkPrintSettings * settings,const char * standard_name,const char * cups_name,const char * ipp_name)6168 map_settings_to_option (GtkPrinterOption  *option,
6169 			const NameMapping  table[],
6170 			int                n_elements,
6171 			GtkPrintSettings  *settings,
6172 			const char        *standard_name,
6173 			const char        *cups_name,
6174 			const char        *ipp_name)
6175 {
6176   int i;
6177   char *name;
6178   const char *cups_value;
6179   const char *ipp_value;
6180   const char *standard_value;
6181 
6182   /* If the cups-specific setting is set, always use that */
6183   name = g_strdup_printf ("cups-%s", cups_name);
6184   cups_value = gtk_print_settings_get (settings, name);
6185   g_free (name);
6186 
6187   if (cups_value != NULL)
6188     {
6189       gtk_printer_option_set (option, cups_value);
6190       return;
6191     }
6192 
6193   /* If the IPP-specific setting is set, use that */
6194   name = g_strdup_printf ("cups-%s", ipp_name);
6195   ipp_value = gtk_print_settings_get (settings, name);
6196   g_free (name);
6197 
6198   if (ipp_value != NULL)
6199     {
6200       gtk_printer_option_set (option, ipp_value);
6201       return;
6202     }
6203 
6204   /* Otherwise we try to convert from the general setting */
6205   standard_value = gtk_print_settings_get (settings, standard_name);
6206   if (standard_value == NULL)
6207     return;
6208 
6209   for (i = 0; i < n_elements; i++)
6210     {
6211       if (table[i].cups == NULL && table[i].standard == NULL)
6212 	{
6213 	  gtk_printer_option_set (option, standard_value);
6214 	  break;
6215 	}
6216       else if (table[i].cups == NULL &&
6217 	       strcmp (table[i].standard, standard_value) == 0)
6218 	{
6219 	  set_option_off (option);
6220 	  break;
6221 	}
6222       else if (strcmp (table[i].standard, standard_value) == 0)
6223 	{
6224 	  gtk_printer_option_set (option, table[i].cups);
6225 	  break;
6226 	}
6227     }
6228 }
6229 
6230 static void
map_option_to_settings(const char * value,const NameMapping table[],int n_elements,GtkPrintSettings * settings,const char * standard_name,const char * cups_name,const char * ipp_name,gboolean is_ipp_option)6231 map_option_to_settings (const char        *value,
6232 			const NameMapping  table[],
6233 			int                n_elements,
6234 			GtkPrintSettings  *settings,
6235 			const char        *standard_name,
6236 			const char        *cups_name,
6237 			const char        *ipp_name,
6238 			gboolean           is_ipp_option)
6239 {
6240   int i;
6241   char *name;
6242 
6243   for (i = 0; i < n_elements; i++)
6244     {
6245       if (table[i].cups == NULL && table[i].standard == NULL)
6246 	{
6247 	  gtk_print_settings_set (settings,
6248 				  standard_name,
6249 				  value);
6250 	  break;
6251 	}
6252       else if (table[i].cups == NULL && table[i].standard != NULL)
6253 	{
6254 	  if (value_is_off (value))
6255 	    {
6256 	      gtk_print_settings_set (settings,
6257 				      standard_name,
6258 				      table[i].standard);
6259 	      break;
6260 	    }
6261 	}
6262       else if (strcmp (table[i].cups, value) == 0)
6263 	{
6264 	  gtk_print_settings_set (settings,
6265 				  standard_name,
6266 				  table[i].standard);
6267 	  break;
6268 	}
6269     }
6270 
6271   /* Always set the corresponding cups-specific setting */
6272   if (is_ipp_option)
6273     name = g_strdup_printf ("cups-%s", ipp_name);
6274   else
6275     name = g_strdup_printf ("cups-%s", cups_name);
6276 
6277   gtk_print_settings_set (settings, name, value);
6278 
6279   g_free (name);
6280 }
6281 
6282 
6283 static const NameMapping paper_source_map[] = {
6284   { "Lower", "lower"},
6285   { "Middle", "middle"},
6286   { "Upper", "upper"},
6287   { "Rear", "rear"},
6288   { "Envelope", "envelope"},
6289   { "Cassette", "cassette"},
6290   { "LargeCapacity", "large-capacity"},
6291   { "AnySmallFormat", "small-format"},
6292   { "AnyLargeFormat", "large-format"},
6293   { NULL, NULL}
6294 };
6295 
6296 static const NameMapping output_tray_map[] = {
6297   { "Upper", "upper"},
6298   { "Lower", "lower"},
6299   { "Rear", "rear"},
6300   { NULL, NULL}
6301 };
6302 
6303 static const NameMapping duplex_map[] = {
6304   { "DuplexTumble", "vertical" },
6305   { "DuplexNoTumble", "horizontal" },
6306   { NULL, "simplex" }
6307 };
6308 
6309 static const NameMapping output_mode_map[] = {
6310   { "Standard", "normal" },
6311   { "Normal", "normal" },
6312   { "Draft", "draft" },
6313   { "Fast", "draft" },
6314 };
6315 
6316 static const NameMapping media_type_map[] = {
6317   { "Transparency", "transparency"},
6318   { "Standard", "stationery"},
6319   { NULL, NULL}
6320 };
6321 
6322 static const NameMapping all_map[] = {
6323   { NULL, NULL}
6324 };
6325 
6326 
6327 static void
set_option_from_settings(GtkPrinterOption * option,GtkPrintSettings * settings)6328 set_option_from_settings (GtkPrinterOption *option,
6329 			  GtkPrintSettings *settings)
6330 {
6331   const char *cups_value;
6332   char *value;
6333 
6334   if (settings == NULL)
6335     return;
6336 
6337   if (strcmp (option->name, "gtk-paper-source") == 0)
6338     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
6339 			     settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
6340 			     "InputSlot", NULL);
6341   else if (strcmp (option->name, "gtk-output-tray") == 0)
6342     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
6343 			    settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
6344 			    "OutputBin", "output-bin");
6345   else if (strcmp (option->name, "gtk-duplex") == 0)
6346     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
6347 			    settings, GTK_PRINT_SETTINGS_DUPLEX,
6348 			    "Duplex", "sides");
6349   else if (strcmp (option->name, "cups-OutputMode") == 0)
6350     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
6351 			    settings, GTK_PRINT_SETTINGS_QUALITY,
6352 			    "OutputMode", NULL);
6353   else if (strcmp (option->name, "cups-Resolution") == 0)
6354     {
6355       cups_value = gtk_print_settings_get (settings, option->name);
6356       if (cups_value)
6357 	gtk_printer_option_set (option, cups_value);
6358       else
6359 	{
6360 	  if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
6361 	      gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
6362 	      gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
6363 	      option->value == NULL || option->value[0] == '\0')
6364 	    {
6365               int res = gtk_print_settings_get_resolution (settings);
6366               int res_x = gtk_print_settings_get_resolution_x (settings);
6367               int res_y = gtk_print_settings_get_resolution_y (settings);
6368 
6369               if (res_x != res_y)
6370                 {
6371                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
6372                   gtk_printer_option_set (option, value);
6373                   g_free (value);
6374                 }
6375               else if (res != 0)
6376                 {
6377                   value = g_strdup_printf ("%ddpi", res);
6378                   gtk_printer_option_set (option, value);
6379                   g_free (value);
6380                 }
6381             }
6382         }
6383     }
6384   else if (strcmp (option->name, "gtk-paper-type") == 0)
6385     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
6386 			    settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
6387 			    "MediaType", NULL);
6388   else if (strcmp (option->name, "gtk-n-up") == 0)
6389     {
6390       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
6391 			      settings, GTK_PRINT_SETTINGS_NUMBER_UP,
6392 			      "number-up", NULL);
6393     }
6394   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
6395     {
6396       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
6397 			      settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
6398 			      "number-up-layout", NULL);
6399     }
6400   else if (strcmp (option->name, "gtk-billing-info") == 0)
6401     {
6402       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
6403       if (cups_value)
6404 	gtk_printer_option_set (option, cups_value);
6405     }
6406   else if (strcmp (option->name, "gtk-job-prio") == 0)
6407     {
6408       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
6409       if (cups_value)
6410 	gtk_printer_option_set (option, cups_value);
6411     }
6412   else if (strcmp (option->name, "gtk-cover-before") == 0)
6413     {
6414       cups_value = gtk_print_settings_get (settings, "cover-before");
6415       if (cups_value)
6416 	gtk_printer_option_set (option, cups_value);
6417     }
6418   else if (strcmp (option->name, "gtk-cover-after") == 0)
6419     {
6420       cups_value = gtk_print_settings_get (settings, "cover-after");
6421       if (cups_value)
6422 	gtk_printer_option_set (option, cups_value);
6423     }
6424   else if (strcmp (option->name, "gtk-print-time") == 0)
6425     {
6426       cups_value = gtk_print_settings_get (settings, "print-at");
6427       if (cups_value)
6428 	gtk_printer_option_set (option, cups_value);
6429     }
6430   else if (strcmp (option->name, "gtk-print-time-text") == 0)
6431     {
6432       cups_value = gtk_print_settings_get (settings, "print-at-time");
6433       if (cups_value)
6434 	gtk_printer_option_set (option, cups_value);
6435     }
6436   else if (g_str_has_prefix (option->name, "cups-"))
6437     {
6438       cups_value = gtk_print_settings_get (settings, option->name);
6439       if (cups_value)
6440 	gtk_printer_option_set (option, cups_value);
6441     }
6442 }
6443 
6444 static void
foreach_option_get_settings(GtkPrinterOption * option,gpointer user_data)6445 foreach_option_get_settings (GtkPrinterOption *option,
6446 			     gpointer          user_data)
6447 {
6448   struct OptionData *data = user_data;
6449   GtkPrintSettings *settings = data->settings;
6450   const char *value;
6451 
6452   value = option->value;
6453 
6454   if (strcmp (option->name, "gtk-paper-source") == 0)
6455     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
6456 			    settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
6457 			    "InputSlot", NULL, FALSE);
6458   else if (strcmp (option->name, "gtk-output-tray") == 0)
6459     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
6460 			    settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
6461 			    "OutputBin", "output-bin", option_is_ipp_option (option));
6462   else if (strcmp (option->name, "gtk-duplex") == 0)
6463     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
6464 			    settings, GTK_PRINT_SETTINGS_DUPLEX,
6465 			    "Duplex", "sides", option_is_ipp_option (option));
6466   else if (strcmp (option->name, "cups-OutputMode") == 0)
6467     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
6468 			    settings, GTK_PRINT_SETTINGS_QUALITY,
6469 			    "OutputMode", NULL, FALSE);
6470   else if (strcmp (option->name, "cups-Resolution") == 0)
6471     {
6472       int res, res_x, res_y;
6473 
6474       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
6475         {
6476           if (res_x > 0 && res_y > 0)
6477             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
6478         }
6479       else if (sscanf (value, "%ddpi", &res) == 1)
6480         {
6481           if (res > 0)
6482             gtk_print_settings_set_resolution (settings, res);
6483         }
6484 
6485       gtk_print_settings_set (settings, option->name, value);
6486     }
6487   else if (strcmp (option->name, "gtk-paper-type") == 0)
6488     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
6489 			    settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
6490 			    "MediaType", NULL, FALSE);
6491   else if (strcmp (option->name, "gtk-n-up") == 0)
6492     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
6493 			    settings, GTK_PRINT_SETTINGS_NUMBER_UP,
6494 			    "number-up", NULL, FALSE);
6495   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
6496     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
6497 			    settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
6498 			    "number-up-layout", NULL, FALSE);
6499   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
6500     gtk_print_settings_set (settings, "cups-job-billing", value);
6501   else if (strcmp (option->name, "gtk-job-prio") == 0)
6502     gtk_print_settings_set (settings, "cups-job-priority", value);
6503   else if (strcmp (option->name, "gtk-cover-before") == 0)
6504     gtk_print_settings_set (settings, "cover-before", value);
6505   else if (strcmp (option->name, "gtk-cover-after") == 0)
6506     gtk_print_settings_set (settings, "cover-after", value);
6507   else if (strcmp (option->name, "gtk-print-time") == 0)
6508     gtk_print_settings_set (settings, "print-at", value);
6509   else if (strcmp (option->name, "gtk-print-time-text") == 0)
6510     gtk_print_settings_set (settings, "print-at-time", value);
6511   else if (g_str_has_prefix (option->name, "cups-"))
6512     gtk_print_settings_set (settings, option->name, value);
6513 }
6514 
6515 static gboolean
supports_am_pm(void)6516 supports_am_pm (void)
6517 {
6518   struct tm tmp_tm = { 0 };
6519   char   time[8];
6520   int    length;
6521 
6522   length = strftime (time, sizeof (time), "%p", &tmp_tm);
6523 
6524   return length != 0;
6525 }
6526 
6527 /* Converts local time to UTC time. Local time has to be in one of these
6528  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
6529  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
6530  * Returns a newly allocated string holding UTC time in HH:MM:SS format
6531  * or NULL.
6532  */
6533 static char *
localtime_to_utctime(const char * local_time)6534 localtime_to_utctime (const char *local_time)
6535 {
6536   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
6537                              " %H : %M : %S ",
6538                              " %I : %M %p ", " %p %I : %M ",
6539                              " %H : %M ",
6540                              " %I %p ", " %p %I "};
6541   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
6542   const char *end = NULL;
6543   struct tm  *actual_local_time;
6544   struct tm  *actual_utc_time;
6545   struct tm   local_print_time;
6546   struct tm   utc_print_time;
6547   struct tm   diff_time;
6548   char       *utc_time = NULL;
6549   int         i, n;
6550 
6551   if (local_time == NULL || local_time[0] == '\0')
6552     return NULL;
6553 
6554   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
6555 
6556   for (i = 0; i < n; i++)
6557     {
6558       local_print_time.tm_hour = 0;
6559       local_print_time.tm_min  = 0;
6560       local_print_time.tm_sec  = 0;
6561 
6562       if (supports_am_pm ())
6563         end = strptime (local_time, formats_0[i], &local_print_time);
6564       else
6565         end = strptime (local_time, formats_1[i], &local_print_time);
6566 
6567       if (end != NULL && end[0] == '\0')
6568         break;
6569     }
6570 
6571   if (end != NULL && end[0] == '\0')
6572     {
6573       time_t rawtime;
6574       time (&rawtime);
6575 
6576       actual_utc_time = g_memdup2 (gmtime (&rawtime), sizeof (struct tm));
6577       actual_local_time = g_memdup2 (localtime (&rawtime), sizeof (struct tm));
6578 
6579       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
6580       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
6581       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
6582 
6583       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
6584       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
6585       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
6586 
6587       utc_time = g_strdup_printf ("%02d:%02d:%02d",
6588                                   utc_print_time.tm_hour,
6589                                   utc_print_time.tm_min,
6590                                   utc_print_time.tm_sec);
6591     }
6592 
6593   return utc_time;
6594 }
6595 
6596 static void
cups_printer_get_settings_from_options(GtkPrinter * printer,GtkPrinterOptionSet * options,GtkPrintSettings * settings)6597 cups_printer_get_settings_from_options (GtkPrinter          *printer,
6598 					GtkPrinterOptionSet *options,
6599 					GtkPrintSettings    *settings)
6600 {
6601   struct OptionData data;
6602   const char *print_at, *print_at_time;
6603 
6604   data.printer = printer;
6605   data.options = options;
6606   data.settings = settings;
6607   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
6608 
6609   gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
6610   if (data.ppd_file != NULL)
6611     {
6612       GtkPrinterOption *cover_before, *cover_after;
6613 
6614       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
6615       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
6616       if (cover_before && cover_after)
6617 	{
6618 	  char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
6619 	  gtk_print_settings_set (settings, "cups-job-sheets", value);
6620 	  g_free (value);
6621 	}
6622 
6623       print_at = gtk_print_settings_get (settings, "print-at");
6624       print_at_time = gtk_print_settings_get (settings, "print-at-time");
6625 
6626       if (strcmp (print_at, "at") == 0)
6627         {
6628           char *utc_time = NULL;
6629 
6630           utc_time = localtime_to_utctime (print_at_time);
6631 
6632           if (utc_time != NULL)
6633             {
6634               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
6635               g_free (utc_time);
6636             }
6637           else
6638             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
6639         }
6640       else if (strcmp (print_at, "on-hold") == 0)
6641 	gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
6642     }
6643 }
6644 
6645 static void
cups_printer_prepare_for_print(GtkPrinter * printer,GtkPrintJob * print_job,GtkPrintSettings * settings,GtkPageSetup * page_setup)6646 cups_printer_prepare_for_print (GtkPrinter       *printer,
6647 				GtkPrintJob      *print_job,
6648 				GtkPrintSettings *settings,
6649 				GtkPageSetup     *page_setup)
6650 {
6651   GtkPrintPages pages;
6652   GtkPageRange *ranges;
6653   int n_ranges;
6654   GtkPageSet page_set;
6655   GtkPaperSize *paper_size;
6656   const char *ppd_paper_name;
6657   double scale;
6658   GtkPrintCapabilities  capabilities;
6659 
6660   capabilities = cups_printer_get_capabilities (printer);
6661   pages = gtk_print_settings_get_print_pages (settings);
6662   gtk_print_job_set_pages (print_job, pages);
6663 
6664   if (pages == GTK_PRINT_PAGES_RANGES)
6665     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
6666   else
6667     {
6668       ranges = NULL;
6669       n_ranges = 0;
6670     }
6671 
6672   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
6673 
6674   if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
6675     {
6676       if (gtk_print_settings_get_collate (settings))
6677         gtk_print_settings_set (settings, "cups-Collate", "True");
6678       else
6679         gtk_print_settings_set (settings, "cups-Collate", "False");
6680       gtk_print_job_set_collate (print_job, FALSE);
6681     }
6682   else
6683     {
6684       gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
6685     }
6686 
6687   if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
6688     {
6689       if (gtk_print_settings_get_reverse (settings))
6690         gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
6691       gtk_print_job_set_reverse (print_job, FALSE);
6692     }
6693   else
6694     {
6695       gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
6696     }
6697 
6698   if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
6699     {
6700       if (gtk_print_settings_get_n_copies (settings) > 1)
6701         gtk_print_settings_set_int (settings, "cups-copies",
6702                                     gtk_print_settings_get_n_copies (settings));
6703       gtk_print_job_set_num_copies (print_job, 1);
6704     }
6705   else
6706     {
6707       gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings));
6708     }
6709 
6710   scale = gtk_print_settings_get_scale (settings);
6711   if (scale != 100.0)
6712     gtk_print_job_set_scale (print_job, scale / 100.0);
6713 
6714   page_set = gtk_print_settings_get_page_set (settings);
6715   if (page_set == GTK_PAGE_SET_EVEN)
6716     gtk_print_settings_set (settings, "cups-page-set", "even");
6717   else if (page_set == GTK_PAGE_SET_ODD)
6718     gtk_print_settings_set (settings, "cups-page-set", "odd");
6719   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
6720 
6721   paper_size = gtk_page_setup_get_paper_size (page_setup);
6722   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
6723   if (ppd_paper_name != NULL)
6724     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
6725   else if (gtk_paper_size_is_ipp (paper_size))
6726     gtk_print_settings_set (settings, "cups-media", gtk_paper_size_get_name (paper_size));
6727   else
6728     {
6729       char width[G_ASCII_DTOSTR_BUF_SIZE];
6730       char height[G_ASCII_DTOSTR_BUF_SIZE];
6731       char *custom_name;
6732 
6733       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
6734       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
6735       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
6736       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
6737       g_free (custom_name);
6738     }
6739 
6740   if (gtk_print_settings_get_number_up (settings) > 1)
6741     {
6742       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
6743       GEnumClass        *enum_class;
6744       GEnumValue        *enum_value;
6745 
6746       switch (gtk_page_setup_get_orientation (page_setup))
6747         {
6748           case GTK_PAGE_ORIENTATION_LANDSCAPE:
6749             if (layout < 4)
6750               layout = layout + 2 + 4 * (1 - layout / 2);
6751             else
6752               layout = layout - 3 - 2 * (layout % 2);
6753             break;
6754           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
6755             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
6756             break;
6757           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
6758             if (layout < 4)
6759               layout = layout + 5 - 2 * (layout % 2);
6760             else
6761               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
6762             break;
6763 
6764           case GTK_PAGE_ORIENTATION_PORTRAIT:
6765           default:
6766             break;
6767         }
6768 
6769       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
6770       enum_value = g_enum_get_value (enum_class, layout);
6771       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
6772       g_type_class_unref (enum_class);
6773 
6774       if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
6775         {
6776           gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
6777           gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
6778         }
6779     }
6780 
6781   gtk_print_job_set_rotate (print_job, TRUE);
6782 }
6783 
6784 static GtkPageSetup *
create_page_setup(ppd_file_t * ppd_file,ppd_size_t * size)6785 create_page_setup (ppd_file_t *ppd_file,
6786 		   ppd_size_t *size)
6787 {
6788   char *display_name;
6789   GtkPageSetup *page_setup;
6790   GtkPaperSize *paper_size;
6791   ppd_option_t *option;
6792   ppd_choice_t *choice;
6793 
6794   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
6795 
6796   display_name = NULL;
6797   option = ppdFindOption (ppd_file, "PageSize");
6798   if (option)
6799     {
6800       choice = ppdFindChoice (option, size->name);
6801       if (choice)
6802 	display_name = ppd_text_to_utf8 (ppd_file, choice->text);
6803     }
6804 
6805   G_GNUC_END_IGNORE_DEPRECATIONS
6806 
6807   if (display_name == NULL)
6808     display_name = g_strdup (size->name);
6809 
6810   page_setup = gtk_page_setup_new ();
6811   paper_size = gtk_paper_size_new_from_ppd (size->name,
6812 					    display_name,
6813 					    size->width,
6814 					    size->length);
6815   gtk_page_setup_set_paper_size (page_setup, paper_size);
6816   gtk_paper_size_free (paper_size);
6817 
6818   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
6819   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
6820   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
6821   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
6822 
6823   g_free (display_name);
6824 
6825   return page_setup;
6826 }
6827 
6828 static GtkPageSetup *
create_page_setup_from_media(char * media,MediaSize * media_size,gboolean media_margin_default_set,int media_bottom_margin_default,int media_top_margin_default,int media_left_margin_default,int media_right_margin_default)6829 create_page_setup_from_media (char      *media,
6830                               MediaSize *media_size,
6831                               gboolean   media_margin_default_set,
6832                               int        media_bottom_margin_default,
6833                               int        media_top_margin_default,
6834                               int        media_left_margin_default,
6835                               int        media_right_margin_default)
6836 {
6837   GtkPageSetup *page_setup;
6838   GtkPaperSize *paper_size;
6839 
6840   page_setup = gtk_page_setup_new ();
6841   paper_size = gtk_paper_size_new_from_ipp (media,
6842                                             POINTS_PER_INCH * (media_size->x_dimension / MM_PER_INCH),
6843                                             POINTS_PER_INCH * (media_size->y_dimension / MM_PER_INCH));
6844   gtk_page_setup_set_paper_size (page_setup, paper_size);
6845   gtk_paper_size_free (paper_size);
6846 
6847   if (media_margin_default_set)
6848     {
6849       gtk_page_setup_set_bottom_margin (page_setup, media_bottom_margin_default, GTK_UNIT_MM);
6850       gtk_page_setup_set_top_margin (page_setup, media_top_margin_default, GTK_UNIT_MM);
6851       gtk_page_setup_set_left_margin (page_setup, media_left_margin_default, GTK_UNIT_MM);
6852       gtk_page_setup_set_right_margin (page_setup, media_right_margin_default, GTK_UNIT_MM);
6853     }
6854 
6855   return page_setup;
6856 }
6857 
6858 static GList *
cups_printer_list_papers(GtkPrinter * printer)6859 cups_printer_list_papers (GtkPrinter *printer)
6860 {
6861   ppd_file_t *ppd_file;
6862   ppd_size_t *size;
6863   GtkPageSetup *page_setup;
6864   GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
6865   GList *result = NULL;
6866   int i;
6867 
6868   ppd_file = gtk_printer_cups_get_ppd (cups_printer);
6869   if (ppd_file != NULL)
6870     {
6871       for (i = 0; i < ppd_file->num_sizes; i++)
6872         {
6873           size = &ppd_file->sizes[i];
6874 
6875           page_setup = create_page_setup (ppd_file, size);
6876 
6877           result = g_list_prepend (result, page_setup);
6878         }
6879     }
6880   else if (cups_printer->media_supported != NULL &&
6881            cups_printer->media_size_supported != NULL &&
6882            /*
6883             * 'media_supported' list can contain names of minimal and maximal sizes
6884             * for which we don't create item in 'media_size_supported' list.
6885             */
6886            g_list_length (cups_printer->media_supported) >=
6887            g_list_length (cups_printer->media_size_supported))
6888     {
6889       MediaSize *media_size;
6890       GList     *media_iter;
6891       GList     *media_size_iter;
6892       char      *media;
6893 
6894       for (media_iter = cups_printer->media_supported,
6895            media_size_iter = cups_printer->media_size_supported;
6896            media_size_iter != NULL;
6897            media_iter = media_iter->next,
6898            media_size_iter = media_size_iter->next)
6899         {
6900           media = (char *) media_iter->data;
6901           media_size = (MediaSize *) media_size_iter->data;
6902 
6903           page_setup = create_page_setup_from_media (media,
6904                                                      media_size,
6905                                                      cups_printer->media_margin_default_set,
6906                                                      cups_printer->media_bottom_margin_default,
6907                                                      cups_printer->media_top_margin_default,
6908                                                      cups_printer->media_left_margin_default,
6909                                                      cups_printer->media_right_margin_default);
6910 
6911           result = g_list_prepend (result, page_setup);
6912         }
6913     }
6914 
6915   result = g_list_reverse (result);
6916 
6917   return result;
6918 }
6919 
6920 static GtkPageSetup *
cups_printer_get_default_page_size(GtkPrinter * printer)6921 cups_printer_get_default_page_size (GtkPrinter *printer)
6922 {
6923   GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
6924   GtkPageSetup   *result = NULL;
6925   ppd_option_t   *option;
6926   ppd_file_t     *ppd_file;
6927   ppd_size_t     *size;
6928 
6929   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
6930   if (ppd_file != NULL)
6931     {
6932       G_GNUC_BEGIN_IGNORE_DEPRECATIONS
6933 
6934       option = ppdFindOption (ppd_file, "PageSize");
6935       if (option == NULL)
6936         return NULL;
6937 
6938       size = ppdPageSize (ppd_file, option->defchoice);
6939       if (size == NULL)
6940         return NULL;
6941 
6942       G_GNUC_END_IGNORE_DEPRECATIONS
6943 
6944       result = create_page_setup (ppd_file, size);
6945     }
6946   else if (cups_printer->media_default != NULL)
6947     {
6948       MediaSize *media_size;
6949       GList     *media_iter;
6950       GList     *media_size_iter;
6951       char      *media;
6952 
6953       for (media_iter = cups_printer->media_supported,
6954            media_size_iter = cups_printer->media_size_supported;
6955            media_size_iter != NULL;
6956            media_iter = media_iter->next,
6957            media_size_iter = media_size_iter->next)
6958         {
6959           media = (char *) media_iter->data;
6960           media_size = (MediaSize *) media_size_iter->data;
6961 
6962           if (g_strcmp0 (cups_printer->media_default, media) == 0)
6963             {
6964               result = create_page_setup_from_media (media,
6965                                                      media_size,
6966                                                      cups_printer->media_margin_default_set,
6967                                                      cups_printer->media_bottom_margin_default,
6968                                                      cups_printer->media_top_margin_default,
6969                                                      cups_printer->media_left_margin_default,
6970                                                      cups_printer->media_right_margin_default);
6971             }
6972         }
6973     }
6974 
6975   return result;
6976 }
6977 
6978 static gboolean
cups_printer_get_hard_margins(GtkPrinter * printer,double * top,double * bottom,double * left,double * right)6979 cups_printer_get_hard_margins (GtkPrinter *printer,
6980 			       double     *top,
6981 			       double     *bottom,
6982 			       double     *left,
6983 			       double     *right)
6984 {
6985   GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
6986   ppd_file_t     *ppd_file;
6987   gboolean        result = FALSE;
6988 
6989   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
6990   if (ppd_file != NULL)
6991     {
6992       *left = ppd_file->custom_margins[0];
6993       *bottom = ppd_file->custom_margins[1];
6994       *right = ppd_file->custom_margins[2];
6995       *top = ppd_file->custom_margins[3];
6996       result = TRUE;
6997     }
6998   else if (cups_printer->media_margin_default_set)
6999     {
7000       *left = POINTS_PER_INCH * cups_printer->media_left_margin_default / MM_PER_INCH;
7001       *bottom = POINTS_PER_INCH * cups_printer->media_bottom_margin_default / MM_PER_INCH;
7002       *right = POINTS_PER_INCH * cups_printer->media_right_margin_default / MM_PER_INCH;
7003       *top = POINTS_PER_INCH * cups_printer->media_top_margin_default / MM_PER_INCH;
7004       result = TRUE;
7005     }
7006 
7007   return result;
7008 }
7009 
7010 static gboolean
cups_printer_get_hard_margins_for_paper_size(GtkPrinter * printer,GtkPaperSize * paper_size,double * top,double * bottom,double * left,double * right)7011 cups_printer_get_hard_margins_for_paper_size (GtkPrinter   *printer,
7012 					      GtkPaperSize *paper_size,
7013 					      double       *top,
7014 					      double       *bottom,
7015 					      double       *left,
7016 					      double       *right)
7017 {
7018   ppd_file_t *ppd_file;
7019   ppd_size_t *size;
7020   const char *paper_name;
7021   int i;
7022 
7023   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
7024   if (ppd_file == NULL)
7025     return FALSE;
7026 
7027   paper_name = gtk_paper_size_get_ppd_name (paper_size);
7028 
7029   for (i = 0; i < ppd_file->num_sizes; i++)
7030     {
7031       size = &ppd_file->sizes[i];
7032       if (g_strcmp0(size->name, paper_name) == 0)
7033         {
7034 	   *top = size->length - size->top;
7035 	   *bottom = size->bottom;
7036 	   *left = size->left;
7037 	   *right = size->width - size->right;
7038 	   return TRUE;
7039 	}
7040     }
7041 
7042   /* Custom size */
7043   *left = ppd_file->custom_margins[0];
7044   *bottom = ppd_file->custom_margins[1];
7045   *right = ppd_file->custom_margins[2];
7046   *top = ppd_file->custom_margins[3];
7047 
7048   return TRUE;
7049 }
7050 
7051 static GtkPrintCapabilities
cups_printer_get_capabilities(GtkPrinter * printer)7052 cups_printer_get_capabilities (GtkPrinter *printer)
7053 {
7054   GtkPrintCapabilities  capabilities = 0;
7055   GtkPrinterCups       *cups_printer = GTK_PRINTER_CUPS (printer);
7056 
7057   if (gtk_printer_cups_get_ppd (cups_printer))
7058     {
7059       capabilities = GTK_PRINT_CAPABILITY_REVERSE;
7060     }
7061 
7062   if (cups_printer->supports_copies)
7063     {
7064       capabilities |= GTK_PRINT_CAPABILITY_COPIES;
7065     }
7066 
7067   if (cups_printer->supports_collate)
7068     {
7069       capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
7070     }
7071 
7072   if (cups_printer->supports_number_up)
7073     {
7074       capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
7075                       GTK_PRINT_CAPABILITY_NUMBER_UP;
7076     }
7077 
7078   return capabilities;
7079 }
7080 
7081 static void
secrets_service_appeared_cb(GDBusConnection * connection,const char * name,const char * name_owner,gpointer user_data)7082 secrets_service_appeared_cb (GDBusConnection *connection,
7083                              const char      *name,
7084                              const char      *name_owner,
7085                              gpointer         user_data)
7086 {
7087   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
7088 
7089   backend->secrets_service_available = TRUE;
7090 }
7091 
7092 static void
secrets_service_vanished_cb(GDBusConnection * connection,const char * name,gpointer user_data)7093 secrets_service_vanished_cb (GDBusConnection *connection,
7094                              const char      *name,
7095                              gpointer         user_data)
7096 {
7097   GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
7098 
7099   backend->secrets_service_available = FALSE;
7100 }
7101