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