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 >k_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