forked from AuroraMiddleware/gtk
255225584e
We are currently truncating job names to 255 bytes, because that's the maximum allowed length of job-name attribute in CUPS. This is a CUPS limitation that GtkPrintOperation shouldn't need to know, and it shouldn't affect other backends, that might have other limitations or even no limitation at all. This has another side effect, that what you set as GtkPrintOperation:job-name could be different to what you get if the property is truncated, this is not documented in gtk_print_operation_set_job_name(). So, I think the job name should be truncated by the CUPS backend, right before setting the job-name attribute. https://bugzilla.gnome.org/show_bug.cgi?id=774097
6732 lines
215 KiB
C
6732 lines
215 KiB
C
/* GTK - The GIMP Toolkit
|
||
* gtkprintbackendcups.h: Default implementation of GtkPrintBackend
|
||
* for the Common Unix Print System (CUPS)
|
||
* Copyright (C) 2006, 2007 Red Hat, Inc.
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "config.h"
|
||
#include <ctype.h>
|
||
#include <unistd.h>
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
/* Cups 1.6 deprecates ppdFindAttr(), ppdFindCustomOption(),
|
||
* ppdFirstCustomParam(), and ppdNextCustomParam() among others. This
|
||
* turns off the warning so that it will compile.
|
||
*/
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
# define _PPD_DEPRECATED
|
||
#endif
|
||
|
||
#include <cups/cups.h>
|
||
#include <cups/language.h>
|
||
#include <cups/http.h>
|
||
#include <cups/ipp.h>
|
||
#include <errno.h>
|
||
#include <cairo.h>
|
||
#include <cairo-pdf.h>
|
||
#include <cairo-ps.h>
|
||
|
||
#include <glib/gstdio.h>
|
||
#include <glib/gi18n-lib.h>
|
||
#include <gmodule.h>
|
||
|
||
#include <gtk/gtk.h>
|
||
#include <gtk/gtkprintbackend.h>
|
||
#include <gtk/gtkunixprint.h>
|
||
#include <gtk/gtkprinter-private.h>
|
||
|
||
#include "gtkprintbackendcups.h"
|
||
#include "gtkprintercups.h"
|
||
|
||
#include "gtkcupsutils.h"
|
||
#include "gtkcupssecretsutils.h"
|
||
|
||
#include <gtkprintutils.h>
|
||
|
||
#ifdef HAVE_COLORD
|
||
#include <colord.h>
|
||
#endif
|
||
|
||
typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
|
||
|
||
#define GTK_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
|
||
#define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
|
||
#define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
|
||
|
||
#define _CUPS_MAX_ATTEMPTS 10
|
||
#define _CUPS_MAX_CHUNK_SIZE 8192
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
#define AVAHI_IF_UNSPEC -1
|
||
#define AVAHI_PROTO_INET 0
|
||
#define AVAHI_PROTO_INET6 1
|
||
#define AVAHI_PROTO_UNSPEC -1
|
||
|
||
#define AVAHI_BUS "org.freedesktop.Avahi"
|
||
#define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server"
|
||
#define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
|
||
#define AVAHI_SERVICE_RESOLVER_IFACE "org.freedesktop.Avahi.ServiceResolver"
|
||
#endif
|
||
|
||
/* define this to see warnings about ignored ppd options */
|
||
#undef PRINT_IGNORED_OPTIONS
|
||
|
||
#define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
|
||
#define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
|
||
|
||
static GType print_backend_cups_type = 0;
|
||
|
||
typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
|
||
GtkCupsResult *result,
|
||
gpointer user_data);
|
||
|
||
typedef enum
|
||
{
|
||
DISPATCH_SETUP,
|
||
DISPATCH_REQUEST,
|
||
DISPATCH_SEND,
|
||
DISPATCH_CHECK,
|
||
DISPATCH_READ,
|
||
DISPATCH_ERROR
|
||
} GtkPrintCupsDispatchState;
|
||
|
||
typedef struct
|
||
{
|
||
GSource source;
|
||
|
||
http_t *http;
|
||
GtkCupsRequest *request;
|
||
GtkCupsPollState poll_state;
|
||
GPollFD *data_poll;
|
||
GtkPrintBackendCups *backend;
|
||
GtkPrintCupsResponseCallbackFunc callback;
|
||
gpointer callback_data;
|
||
|
||
} GtkPrintCupsDispatchWatch;
|
||
|
||
struct _GtkPrintBackendCupsClass
|
||
{
|
||
GtkPrintBackendClass parent_class;
|
||
};
|
||
|
||
struct _GtkPrintBackendCups
|
||
{
|
||
GtkPrintBackend parent_instance;
|
||
|
||
char *default_printer;
|
||
|
||
guint list_printers_poll;
|
||
guint list_printers_pending : 1;
|
||
gint list_printers_attempts;
|
||
guint got_default_printer : 1;
|
||
guint default_printer_poll;
|
||
GtkCupsConnectionTest *cups_connection_test;
|
||
gint reading_ppds;
|
||
|
||
GList *requests;
|
||
GHashTable *auth;
|
||
gchar *username;
|
||
gboolean authentication_lock;
|
||
#ifdef HAVE_COLORD
|
||
CdClient *colord_client;
|
||
#endif
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
GDBusConnection *dbus_connection;
|
||
gchar *avahi_default_printer;
|
||
guint avahi_service_browser_subscription_id;
|
||
guint avahi_service_browser_subscription_ids[2];
|
||
gchar *avahi_service_browser_paths[2];
|
||
GCancellable *avahi_cancellable;
|
||
#endif
|
||
gboolean secrets_service_available;
|
||
guint secrets_service_watch_id;
|
||
GCancellable *secrets_service_cancellable;
|
||
};
|
||
|
||
static GObjectClass *backend_parent_class;
|
||
|
||
static void gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class);
|
||
static void gtk_print_backend_cups_init (GtkPrintBackendCups *impl);
|
||
static void gtk_print_backend_cups_finalize (GObject *object);
|
||
static void gtk_print_backend_cups_dispose (GObject *object);
|
||
static void cups_get_printer_list (GtkPrintBackend *print_backend);
|
||
static void cups_get_default_printer (GtkPrintBackendCups *print_backend);
|
||
static void cups_get_local_default_printer (GtkPrintBackendCups *print_backend);
|
||
static void cups_request_execute (GtkPrintBackendCups *print_backend,
|
||
GtkCupsRequest *request,
|
||
GtkPrintCupsResponseCallbackFunc callback,
|
||
gpointer user_data,
|
||
GDestroyNotify notify);
|
||
static void cups_printer_get_settings_from_options (GtkPrinter *printer,
|
||
GtkPrinterOptionSet *options,
|
||
GtkPrintSettings *settings);
|
||
static gboolean cups_printer_mark_conflicts (GtkPrinter *printer,
|
||
GtkPrinterOptionSet *options);
|
||
static GtkPrinterOptionSet *cups_printer_get_options (GtkPrinter *printer,
|
||
GtkPrintSettings *settings,
|
||
GtkPageSetup *page_setup,
|
||
GtkPrintCapabilities capabilities);
|
||
static void cups_printer_prepare_for_print (GtkPrinter *printer,
|
||
GtkPrintJob *print_job,
|
||
GtkPrintSettings *settings,
|
||
GtkPageSetup *page_setup);
|
||
static GList * cups_printer_list_papers (GtkPrinter *printer);
|
||
static GtkPageSetup * cups_printer_get_default_page_size (GtkPrinter *printer);
|
||
static void cups_printer_request_details (GtkPrinter *printer);
|
||
static gboolean cups_request_default_printer (GtkPrintBackendCups *print_backend);
|
||
static gboolean cups_request_ppd (GtkPrinter *printer);
|
||
static gboolean cups_printer_get_hard_margins (GtkPrinter *printer,
|
||
gdouble *top,
|
||
gdouble *bottom,
|
||
gdouble *left,
|
||
gdouble *right);
|
||
static GtkPrintCapabilities cups_printer_get_capabilities (GtkPrinter *printer);
|
||
static void set_option_from_settings (GtkPrinterOption *option,
|
||
GtkPrintSettings *setting);
|
||
static void cups_begin_polling_info (GtkPrintBackendCups *print_backend,
|
||
GtkPrintJob *job,
|
||
int job_id);
|
||
static gboolean cups_job_info_poll_timeout (gpointer user_data);
|
||
static void gtk_print_backend_cups_print_stream (GtkPrintBackend *backend,
|
||
GtkPrintJob *job,
|
||
GIOChannel *data_io,
|
||
GtkPrintJobCompleteFunc callback,
|
||
gpointer user_data,
|
||
GDestroyNotify dnotify);
|
||
static cairo_surface_t * cups_printer_create_cairo_surface (GtkPrinter *printer,
|
||
GtkPrintSettings *settings,
|
||
gdouble width,
|
||
gdouble height,
|
||
GIOChannel *cache_io);
|
||
|
||
static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend,
|
||
gchar **auth_info_required,
|
||
gchar **auth_info,
|
||
gboolean store_auth_info);
|
||
|
||
void overwrite_and_free (gpointer data);
|
||
static gboolean is_address_local (const gchar *address);
|
||
static gboolean request_auth_info (gpointer data);
|
||
static void lookup_auth_info (gpointer data);
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
static void avahi_request_printer_list (GtkPrintBackendCups *cups_backend);
|
||
#endif
|
||
|
||
static void secrets_service_appeared_cb (GDBusConnection *connection,
|
||
const gchar *name,
|
||
const gchar *name_owner,
|
||
gpointer user_data);
|
||
static void secrets_service_vanished_cb (GDBusConnection *connection,
|
||
const gchar *name,
|
||
gpointer user_data);
|
||
|
||
static void
|
||
gtk_print_backend_cups_register_type (GTypeModule *module)
|
||
{
|
||
const GTypeInfo print_backend_cups_info =
|
||
{
|
||
sizeof (GtkPrintBackendCupsClass),
|
||
NULL, /* base_init */
|
||
NULL, /* base_finalize */
|
||
(GClassInitFunc) gtk_print_backend_cups_class_init,
|
||
NULL, /* class_finalize */
|
||
NULL, /* class_data */
|
||
sizeof (GtkPrintBackendCups),
|
||
0, /* n_preallocs */
|
||
(GInstanceInitFunc) gtk_print_backend_cups_init
|
||
};
|
||
|
||
print_backend_cups_type = g_type_module_register_type (module,
|
||
GTK_TYPE_PRINT_BACKEND,
|
||
"GtkPrintBackendCups",
|
||
&print_backend_cups_info, 0);
|
||
}
|
||
|
||
G_MODULE_EXPORT void
|
||
pb_module_init (GTypeModule *module)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Initializing the CUPS print backend module\n"));
|
||
|
||
gtk_print_backend_cups_register_type (module);
|
||
gtk_printer_cups_register_type (module);
|
||
}
|
||
|
||
G_MODULE_EXPORT void
|
||
pb_module_exit (void)
|
||
{
|
||
|
||
}
|
||
|
||
G_MODULE_EXPORT GtkPrintBackend *
|
||
pb_module_create (void)
|
||
{
|
||
return gtk_print_backend_cups_new ();
|
||
}
|
||
/* CUPS 1.6 Getter/Setter Functions CUPS 1.6 makes private most of the
|
||
* IPP structures and enforces access via new getter functions, which
|
||
* are unfortunately not available in earlier versions. We define
|
||
* below those getter functions as macros for use when building
|
||
* against earlier CUPS versions.
|
||
*/
|
||
#ifndef HAVE_CUPS_API_1_6
|
||
#define ippGetOperation(ipp_request) ipp_request->request.op.operation_id
|
||
#define ippGetInteger(attr, index) attr->values[index].integer
|
||
#define ippGetBoolean(attr, index) attr->values[index].boolean
|
||
#define ippGetString(attr, index, foo) attr->values[index].string.text
|
||
#define ippGetValueTag(attr) attr->value_tag
|
||
#define ippGetName(attr) attr->name
|
||
#define ippGetCount(attr) attr->num_values
|
||
#define ippGetGroupTag(attr) attr->group_tag
|
||
#define ippGetCollection(attr, index) attr->values[index].collection
|
||
|
||
static int
|
||
ippGetRange (ipp_attribute_t *attr,
|
||
int element,
|
||
int *upper)
|
||
{
|
||
*upper = attr->values[element].range.upper;
|
||
return (attr->values[element].range.lower);
|
||
}
|
||
|
||
static ipp_attribute_t *
|
||
ippFirstAttribute (ipp_t *ipp)
|
||
{
|
||
if (!ipp)
|
||
return (NULL);
|
||
|
||
return (ipp->current = ipp->attrs);
|
||
}
|
||
|
||
static ipp_attribute_t *
|
||
ippNextAttribute (ipp_t *ipp)
|
||
{
|
||
if (!ipp || !ipp->current)
|
||
return (NULL);
|
||
|
||
return (ipp->current = ipp->current->next);
|
||
}
|
||
#endif
|
||
/*
|
||
* GtkPrintBackendCups
|
||
*/
|
||
GType
|
||
gtk_print_backend_cups_get_type (void)
|
||
{
|
||
return print_backend_cups_type;
|
||
}
|
||
|
||
/**
|
||
* gtk_print_backend_cups_new:
|
||
*
|
||
* Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
|
||
* implements the #GtkPrintBackend interface with direct access to
|
||
* the filesystem using Unix/Linux API calls
|
||
*
|
||
* Returns: the new #GtkPrintBackendCups object
|
||
*/
|
||
GtkPrintBackend *
|
||
gtk_print_backend_cups_new (void)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
|
||
|
||
return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
|
||
}
|
||
|
||
static void
|
||
gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
|
||
{
|
||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||
GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
|
||
|
||
backend_parent_class = g_type_class_peek_parent (class);
|
||
|
||
gobject_class->finalize = gtk_print_backend_cups_finalize;
|
||
gobject_class->dispose = gtk_print_backend_cups_dispose;
|
||
|
||
backend_class->request_printer_list = cups_get_printer_list;
|
||
backend_class->print_stream = gtk_print_backend_cups_print_stream;
|
||
backend_class->printer_request_details = cups_printer_request_details;
|
||
backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
|
||
backend_class->printer_get_options = cups_printer_get_options;
|
||
backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
|
||
backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
|
||
backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
|
||
backend_class->printer_list_papers = cups_printer_list_papers;
|
||
backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
|
||
backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
|
||
backend_class->printer_get_capabilities = cups_printer_get_capabilities;
|
||
backend_class->set_password = gtk_print_backend_cups_set_password;
|
||
}
|
||
|
||
static gboolean
|
||
option_is_ipp_option (GtkPrinterOption *option)
|
||
{
|
||
gpointer data = g_object_get_data (G_OBJECT (option), "is-ipp-option");
|
||
|
||
if (data != NULL)
|
||
return GPOINTER_TO_UINT (data) != 0;
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
option_set_is_ipp_option (GtkPrinterOption *option,
|
||
gboolean is_ipp_option)
|
||
{
|
||
g_object_set_data (G_OBJECT (option),
|
||
"is-ipp-option",
|
||
GUINT_TO_POINTER (is_ipp_option ? 1 : 0));
|
||
}
|
||
|
||
static cairo_status_t
|
||
_cairo_write_to_cups (void *closure,
|
||
const unsigned char *data,
|
||
unsigned int length)
|
||
{
|
||
GIOChannel *io = (GIOChannel *)closure;
|
||
gsize written;
|
||
GError *error;
|
||
|
||
error = NULL;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
|
||
|
||
while (length > 0)
|
||
{
|
||
g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
|
||
|
||
if (error != NULL)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Error writing to temp file, %s\n",
|
||
error->message));
|
||
|
||
g_error_free (error);
|
||
return CAIRO_STATUS_WRITE_ERROR;
|
||
}
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written));
|
||
|
||
data += written;
|
||
length -= written;
|
||
}
|
||
|
||
return CAIRO_STATUS_SUCCESS;
|
||
}
|
||
|
||
static cairo_surface_t *
|
||
cups_printer_create_cairo_surface (GtkPrinter *printer,
|
||
GtkPrintSettings *settings,
|
||
gdouble width,
|
||
gdouble height,
|
||
GIOChannel *cache_io)
|
||
{
|
||
cairo_surface_t *surface;
|
||
ppd_file_t *ppd_file = NULL;
|
||
ppd_attr_t *ppd_attr = NULL;
|
||
ppd_attr_t *ppd_attr_res = NULL;
|
||
ppd_attr_t *ppd_attr_screen_freq = NULL;
|
||
ppd_attr_t *ppd_attr_res_screen_freq = NULL;
|
||
gchar *res_string = NULL;
|
||
gint level = 2;
|
||
|
||
if (gtk_printer_accepts_pdf (printer))
|
||
surface = cairo_pdf_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height);
|
||
else
|
||
surface = cairo_ps_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height);
|
||
|
||
ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
|
||
|
||
if (ppd_file != NULL)
|
||
{
|
||
ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
|
||
|
||
if (ppd_attr != NULL)
|
||
level = atoi (ppd_attr->value);
|
||
|
||
if (gtk_print_settings_get_resolution (settings) == 0)
|
||
{
|
||
ppd_attr_res = ppdFindAttr (ppd_file, "DefaultResolution", NULL);
|
||
|
||
if (ppd_attr_res != NULL)
|
||
{
|
||
int res, res_x, res_y;
|
||
|
||
if (sscanf (ppd_attr_res->value, "%dx%ddpi", &res_x, &res_y) == 2)
|
||
{
|
||
if (res_x > 0 && res_y > 0)
|
||
gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
|
||
}
|
||
else if (sscanf (ppd_attr_res->value, "%ddpi", &res) == 1)
|
||
{
|
||
if (res > 0)
|
||
gtk_print_settings_set_resolution (settings, res);
|
||
}
|
||
}
|
||
}
|
||
|
||
res_string = g_strdup_printf ("%ddpi",
|
||
gtk_print_settings_get_resolution (settings));
|
||
ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
|
||
g_free (res_string);
|
||
|
||
if (ppd_attr_res_screen_freq == NULL)
|
||
{
|
||
res_string = g_strdup_printf ("%dx%ddpi",
|
||
gtk_print_settings_get_resolution_x (settings),
|
||
gtk_print_settings_get_resolution_y (settings));
|
||
ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
|
||
g_free (res_string);
|
||
}
|
||
|
||
ppd_attr_screen_freq = ppdFindAttr (ppd_file, "ScreenFreq", NULL);
|
||
|
||
if (ppd_attr_res_screen_freq != NULL && atof (ppd_attr_res_screen_freq->value) > 0.0)
|
||
gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_res_screen_freq->value));
|
||
else if (ppd_attr_screen_freq != NULL && atof (ppd_attr_screen_freq->value) > 0.0)
|
||
gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_screen_freq->value));
|
||
}
|
||
|
||
if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PS)
|
||
{
|
||
if (level == 2)
|
||
cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
|
||
|
||
if (level == 3)
|
||
cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
|
||
}
|
||
|
||
cairo_surface_set_fallback_resolution (surface,
|
||
2.0 * gtk_print_settings_get_printer_lpi (settings),
|
||
2.0 * gtk_print_settings_get_printer_lpi (settings));
|
||
|
||
return surface;
|
||
}
|
||
|
||
typedef struct {
|
||
GtkPrintJobCompleteFunc callback;
|
||
GtkPrintJob *job;
|
||
gpointer user_data;
|
||
GDestroyNotify dnotify;
|
||
http_t *http;
|
||
} CupsPrintStreamData;
|
||
|
||
static void
|
||
cups_free_print_stream_data (CupsPrintStreamData *data)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
if (data->dnotify)
|
||
data->dnotify (data->user_data);
|
||
g_object_unref (data->job);
|
||
if (data->http != NULL)
|
||
httpClose (data->http);
|
||
g_free (data);
|
||
}
|
||
|
||
static void
|
||
cups_print_cb (GtkPrintBackendCups *print_backend,
|
||
GtkCupsResult *result,
|
||
gpointer user_data)
|
||
{
|
||
GError *error = NULL;
|
||
CupsPrintStreamData *ps = user_data;
|
||
|
||
gdk_threads_enter ();
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
if (gtk_cups_result_is_error (result))
|
||
error = g_error_new_literal (gtk_print_error_quark (),
|
||
GTK_PRINT_ERROR_INTERNAL_ERROR,
|
||
gtk_cups_result_get_error_string (result));
|
||
|
||
if (ps->callback)
|
||
ps->callback (ps->job, ps->user_data, error);
|
||
|
||
if (error == NULL)
|
||
{
|
||
int job_id = 0;
|
||
ipp_attribute_t *attr; /* IPP job-id attribute */
|
||
ipp_t *response = gtk_cups_result_get_response (result);
|
||
|
||
if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
|
||
job_id = ippGetInteger (attr, 0);
|
||
|
||
if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
|
||
gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
|
||
else
|
||
{
|
||
gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
|
||
cups_begin_polling_info (print_backend, ps->job, job_id);
|
||
}
|
||
}
|
||
else
|
||
gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
|
||
|
||
|
||
if (error)
|
||
g_error_free (error);
|
||
|
||
gdk_threads_leave ();
|
||
}
|
||
|
||
typedef struct {
|
||
GtkCupsRequest *request;
|
||
GtkPrinterCups *printer;
|
||
} CupsOptionsData;
|
||
|
||
static void
|
||
add_cups_options (const gchar *key,
|
||
const gchar *value,
|
||
gpointer user_data)
|
||
{
|
||
CupsOptionsData *data = (CupsOptionsData *) user_data;
|
||
GtkCupsRequest *request = data->request;
|
||
GtkPrinterCups *printer = data->printer;
|
||
gboolean custom_value = FALSE;
|
||
gchar *new_value = NULL;
|
||
gint i;
|
||
|
||
if (!key || !value)
|
||
return;
|
||
|
||
if (!g_str_has_prefix (key, "cups-"))
|
||
return;
|
||
|
||
if (strcmp (value, "gtk-ignore-value") == 0)
|
||
return;
|
||
|
||
key = key + strlen ("cups-");
|
||
|
||
if (printer && printer->ppd_file)
|
||
{
|
||
ppd_coption_t *coption;
|
||
gboolean found = FALSE;
|
||
gboolean custom_values_enabled = FALSE;
|
||
|
||
coption = ppdFindCustomOption (printer->ppd_file, key);
|
||
if (coption && coption->option)
|
||
{
|
||
for (i = 0; i < coption->option->num_choices; i++)
|
||
{
|
||
/* Are custom values enabled ? */
|
||
if (g_str_equal (coption->option->choices[i].choice, "Custom"))
|
||
custom_values_enabled = TRUE;
|
||
|
||
/* Is the value among available choices ? */
|
||
if (g_str_equal (coption->option->choices[i].choice, value))
|
||
found = TRUE;
|
||
}
|
||
|
||
if (custom_values_enabled && !found)
|
||
custom_value = TRUE;
|
||
}
|
||
}
|
||
|
||
/* Add "Custom." prefix to custom values if not already added. */
|
||
if (custom_value && !g_str_has_prefix (value, "Custom."))
|
||
{
|
||
new_value = g_strdup_printf ("Custom.%s", value);
|
||
gtk_cups_request_encode_option (request, key, new_value);
|
||
g_free (new_value);
|
||
}
|
||
else
|
||
gtk_cups_request_encode_option (request, key, value);
|
||
}
|
||
|
||
static void
|
||
gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend,
|
||
GtkPrintJob *job,
|
||
GIOChannel *data_io,
|
||
GtkPrintJobCompleteFunc callback,
|
||
gpointer user_data,
|
||
GDestroyNotify dnotify)
|
||
{
|
||
GtkPrinterCups *cups_printer;
|
||
CupsPrintStreamData *ps;
|
||
CupsOptionsData *options_data;
|
||
GtkCupsRequest *request = NULL;
|
||
GtkPrintSettings *settings;
|
||
const gchar *title;
|
||
char printer_absolute_uri[HTTP_MAX_URI];
|
||
http_t *http = NULL;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
|
||
settings = gtk_print_job_get_settings (job);
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
if (cups_printer->avahi_browsed)
|
||
{
|
||
http = httpConnect (cups_printer->hostname, cups_printer->port);
|
||
if (http)
|
||
{
|
||
request = gtk_cups_request_new_with_username (http,
|
||
GTK_CUPS_POST,
|
||
IPP_PRINT_JOB,
|
||
data_io,
|
||
cups_printer->hostname,
|
||
cups_printer->device_uri,
|
||
GTK_PRINT_BACKEND_CUPS (print_backend)->username);
|
||
g_snprintf (printer_absolute_uri, HTTP_MAX_URI, "%s", cups_printer->printer_uri);
|
||
}
|
||
else
|
||
{
|
||
GError *error = NULL;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_warning ("CUPS Backend: Error connecting to %s:%d",
|
||
cups_printer->hostname,
|
||
cups_printer->port));
|
||
|
||
error = g_error_new (gtk_print_error_quark (),
|
||
GTK_CUPS_ERROR_GENERAL,
|
||
"Error connecting to %s",
|
||
cups_printer->hostname);
|
||
|
||
gtk_print_job_set_status (job, GTK_PRINT_STATUS_FINISHED_ABORTED);
|
||
|
||
if (callback)
|
||
{
|
||
callback (job, user_data, error);
|
||
}
|
||
|
||
g_clear_error (&error);
|
||
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
request = gtk_cups_request_new_with_username (NULL,
|
||
GTK_CUPS_POST,
|
||
IPP_PRINT_JOB,
|
||
data_io,
|
||
NULL,
|
||
cups_printer->device_uri,
|
||
GTK_PRINT_BACKEND_CUPS (print_backend)->username);
|
||
|
||
httpAssembleURIf (HTTP_URI_CODING_ALL,
|
||
printer_absolute_uri,
|
||
sizeof (printer_absolute_uri),
|
||
"ipp",
|
||
NULL,
|
||
"localhost",
|
||
ippPort (),
|
||
"/printers/%s",
|
||
gtk_printer_get_name (gtk_print_job_get_printer (job)));
|
||
}
|
||
|
||
gtk_cups_request_set_ipp_version (request,
|
||
cups_printer->ipp_version_major,
|
||
cups_printer->ipp_version_minor);
|
||
|
||
gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
|
||
IPP_TAG_URI, "printer-uri",
|
||
NULL, printer_absolute_uri);
|
||
|
||
title = gtk_print_job_get_title (job);
|
||
if (title) {
|
||
char *title_truncated = NULL;
|
||
size_t title_bytes = strlen (title);
|
||
|
||
if (title_bytes >= IPP_MAX_NAME)
|
||
{
|
||
gchar *end;
|
||
|
||
end = g_utf8_find_prev_char (title, title + IPP_MAX_NAME - 1);
|
||
title_truncated = g_utf8_substring (title,
|
||
0,
|
||
g_utf8_pointer_to_offset (title, end));
|
||
}
|
||
|
||
gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
|
||
IPP_TAG_NAME, "job-name",
|
||
NULL,
|
||
title_truncated ? title_truncated : title);
|
||
g_free (title_truncated);
|
||
}
|
||
|
||
options_data = g_new0 (CupsOptionsData, 1);
|
||
options_data->request = request;
|
||
options_data->printer = cups_printer;
|
||
gtk_print_settings_foreach (settings, add_cups_options, options_data);
|
||
g_free (options_data);
|
||
|
||
ps = g_new0 (CupsPrintStreamData, 1);
|
||
ps->callback = callback;
|
||
ps->user_data = user_data;
|
||
ps->dnotify = dnotify;
|
||
ps->job = g_object_ref (job);
|
||
ps->http = http;
|
||
|
||
request->need_auth_info = FALSE;
|
||
request->auth_info_required = NULL;
|
||
|
||
/* Check if auth_info_required is set and if it should be handled.
|
||
* The cups libraries handle the ticket exchange for "negotiate". */
|
||
if (cups_printer->auth_info_required != NULL &&
|
||
g_strv_length (cups_printer->auth_info_required) == 1 &&
|
||
g_strcmp0 (cups_printer->auth_info_required[0], "negotiate") == 0)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Ignoring auth-info-required \"%s\"\n",
|
||
cups_printer->auth_info_required[0]));
|
||
}
|
||
else if (cups_printer->auth_info_required != NULL)
|
||
{
|
||
request->need_auth_info = TRUE;
|
||
request->auth_info_required = g_strdupv (cups_printer->auth_info_required);
|
||
}
|
||
|
||
cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
|
||
request,
|
||
(GtkPrintCupsResponseCallbackFunc) cups_print_cb,
|
||
ps,
|
||
(GDestroyNotify)cups_free_print_stream_data);
|
||
}
|
||
|
||
void overwrite_and_free (gpointer data)
|
||
{
|
||
gchar *password = (gchar *) data;
|
||
|
||
if (password != NULL)
|
||
{
|
||
memset (password, 0, strlen (password));
|
||
g_free (password);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
|
||
{
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
gint i;
|
||
#endif
|
||
|
||
backend_cups->list_printers_poll = FALSE;
|
||
backend_cups->got_default_printer = FALSE;
|
||
backend_cups->list_printers_pending = FALSE;
|
||
backend_cups->list_printers_attempts = 0;
|
||
backend_cups->reading_ppds = 0;
|
||
|
||
backend_cups->requests = NULL;
|
||
backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
|
||
backend_cups->authentication_lock = FALSE;
|
||
|
||
backend_cups->default_printer_poll = 0;
|
||
backend_cups->cups_connection_test = NULL;
|
||
|
||
backend_cups->username = NULL;
|
||
|
||
#ifdef HAVE_COLORD
|
||
backend_cups->colord_client = cd_client_new ();
|
||
#endif
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
backend_cups->dbus_connection = NULL;
|
||
backend_cups->avahi_default_printer = NULL;
|
||
backend_cups->avahi_service_browser_subscription_id = 0;
|
||
for (i = 0; i < 2; i++)
|
||
{
|
||
backend_cups->avahi_service_browser_paths[i] = NULL;
|
||
backend_cups->avahi_service_browser_subscription_ids[i] = 0;
|
||
}
|
||
#endif
|
||
|
||
cups_get_local_default_printer (backend_cups);
|
||
|
||
backend_cups->secrets_service_available = FALSE;
|
||
backend_cups->secrets_service_cancellable = g_cancellable_new ();
|
||
backend_cups->secrets_service_watch_id =
|
||
gtk_cups_secrets_service_watch (secrets_service_appeared_cb,
|
||
secrets_service_vanished_cb,
|
||
backend_cups);
|
||
}
|
||
|
||
static void
|
||
gtk_print_backend_cups_finalize (GObject *object)
|
||
{
|
||
GtkPrintBackendCups *backend_cups;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: finalizing CUPS backend module\n"));
|
||
|
||
backend_cups = GTK_PRINT_BACKEND_CUPS (object);
|
||
|
||
g_free (backend_cups->default_printer);
|
||
backend_cups->default_printer = NULL;
|
||
|
||
gtk_cups_connection_test_free (backend_cups->cups_connection_test);
|
||
backend_cups->cups_connection_test = NULL;
|
||
|
||
g_hash_table_destroy (backend_cups->auth);
|
||
|
||
g_free (backend_cups->username);
|
||
|
||
#ifdef HAVE_COLORD
|
||
g_object_unref (backend_cups->colord_client);
|
||
#endif
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
g_clear_object (&backend_cups->avahi_cancellable);
|
||
g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
|
||
g_clear_object (&backend_cups->dbus_connection);
|
||
#endif
|
||
|
||
g_clear_object (&backend_cups->secrets_service_cancellable);
|
||
if (backend_cups->secrets_service_watch_id != 0)
|
||
{
|
||
g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
|
||
}
|
||
|
||
backend_parent_class->finalize (object);
|
||
}
|
||
|
||
static void
|
||
gtk_print_backend_cups_dispose (GObject *object)
|
||
{
|
||
GtkPrintBackendCups *backend_cups;
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
gint i;
|
||
#endif
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
backend_cups = GTK_PRINT_BACKEND_CUPS (object);
|
||
|
||
if (backend_cups->list_printers_poll > 0)
|
||
g_source_remove (backend_cups->list_printers_poll);
|
||
backend_cups->list_printers_poll = 0;
|
||
backend_cups->list_printers_attempts = 0;
|
||
|
||
if (backend_cups->default_printer_poll > 0)
|
||
g_source_remove (backend_cups->default_printer_poll);
|
||
backend_cups->default_printer_poll = 0;
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
g_cancellable_cancel (backend_cups->avahi_cancellable);
|
||
|
||
for (i = 0; i < 2; i++)
|
||
{
|
||
if (backend_cups->avahi_service_browser_subscription_ids[i] > 0)
|
||
{
|
||
g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
|
||
backend_cups->avahi_service_browser_subscription_ids[i]);
|
||
backend_cups->avahi_service_browser_subscription_ids[i] = 0;
|
||
}
|
||
|
||
if (backend_cups->avahi_service_browser_paths[i])
|
||
{
|
||
g_dbus_connection_call (backend_cups->dbus_connection,
|
||
AVAHI_BUS,
|
||
backend_cups->avahi_service_browser_paths[i],
|
||
AVAHI_SERVICE_BROWSER_IFACE,
|
||
"Free",
|
||
NULL,
|
||
NULL,
|
||
G_DBUS_CALL_FLAGS_NONE,
|
||
-1,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
g_clear_pointer (&backend_cups->avahi_service_browser_paths[i], g_free);
|
||
}
|
||
}
|
||
|
||
if (backend_cups->avahi_service_browser_subscription_id > 0)
|
||
{
|
||
g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
|
||
backend_cups->avahi_service_browser_subscription_id);
|
||
backend_cups->avahi_service_browser_subscription_id = 0;
|
||
}
|
||
#endif
|
||
|
||
backend_parent_class->dispose (object);
|
||
}
|
||
|
||
static gboolean
|
||
is_address_local (const gchar *address)
|
||
{
|
||
if (address[0] == '/' ||
|
||
strcmp (address, "127.0.0.1") == 0 ||
|
||
strcmp (address, "[::1]") == 0)
|
||
return TRUE;
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
gtk_print_backend_cups_set_password (GtkPrintBackend *backend,
|
||
gchar **auth_info_required,
|
||
gchar **auth_info,
|
||
gboolean store_auth_info)
|
||
{
|
||
GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
|
||
GList *l;
|
||
char dispatch_hostname[HTTP_MAX_URI];
|
||
gchar *username = NULL;
|
||
gchar *hostname = NULL;
|
||
gchar *password = NULL;
|
||
gint length;
|
||
gint i;
|
||
|
||
length = g_strv_length (auth_info_required);
|
||
|
||
if (auth_info != NULL)
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
if (g_strcmp0 (auth_info_required[i], "username") == 0)
|
||
username = g_strdup (auth_info[i]);
|
||
else if (g_strcmp0 (auth_info_required[i], "hostname") == 0)
|
||
hostname = g_strdup (auth_info[i]);
|
||
else if (g_strcmp0 (auth_info_required[i], "password") == 0)
|
||
password = g_strdup (auth_info[i]);
|
||
}
|
||
|
||
if (hostname != NULL && username != NULL && password != NULL)
|
||
{
|
||
gchar *key = g_strconcat (username, "@", hostname, NULL);
|
||
g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS backend: caching password for %s\n", key));
|
||
}
|
||
|
||
g_free (cups_backend->username);
|
||
cups_backend->username = g_strdup (username);
|
||
|
||
|
||
for (l = cups_backend->requests; l; l = l->next)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch = l->data;
|
||
|
||
httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
|
||
if (is_address_local (dispatch_hostname))
|
||
strcpy (dispatch_hostname, "localhost");
|
||
|
||
if (dispatch->request->need_auth_info)
|
||
{
|
||
if (auth_info != NULL)
|
||
{
|
||
dispatch->request->auth_info = g_new0 (gchar *, length + 1);
|
||
for (i = 0; i < length; i++)
|
||
dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
|
||
}
|
||
/* Save the password if the user requested it */
|
||
if (password != NULL && store_auth_info)
|
||
{
|
||
const gchar *printer_uri =
|
||
gtk_cups_request_ipp_get_string (dispatch->request,
|
||
IPP_TAG_URI,
|
||
"printer-uri");
|
||
|
||
gtk_cups_secrets_service_store (auth_info, auth_info_required,
|
||
printer_uri);
|
||
}
|
||
dispatch->backend->authentication_lock = FALSE;
|
||
dispatch->request->need_auth_info = FALSE;
|
||
}
|
||
else if (dispatch->request->password_state == GTK_CUPS_PASSWORD_REQUESTED || auth_info == NULL)
|
||
{
|
||
overwrite_and_free (dispatch->request->password);
|
||
dispatch->request->password = g_strdup (password);
|
||
g_free (dispatch->request->username);
|
||
dispatch->request->username = g_strdup (username);
|
||
dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
|
||
dispatch->backend->authentication_lock = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
request_password (gpointer data)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch = data;
|
||
const gchar *username;
|
||
gchar *password;
|
||
gchar *prompt = NULL;
|
||
gchar *key = NULL;
|
||
char hostname[HTTP_MAX_URI];
|
||
gchar **auth_info_required;
|
||
gchar **auth_info_default;
|
||
gchar **auth_info_display;
|
||
gboolean *auth_info_visible;
|
||
gint length = 3;
|
||
gint i;
|
||
|
||
if (dispatch->backend->authentication_lock)
|
||
return G_SOURCE_REMOVE;
|
||
|
||
httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
|
||
if (is_address_local (hostname))
|
||
strcpy (hostname, "localhost");
|
||
|
||
if (dispatch->backend->username != NULL)
|
||
username = dispatch->backend->username;
|
||
else
|
||
username = cupsUser ();
|
||
|
||
auth_info_required = g_new0 (gchar*, length + 1);
|
||
auth_info_required[0] = g_strdup ("hostname");
|
||
auth_info_required[1] = g_strdup ("username");
|
||
auth_info_required[2] = g_strdup ("password");
|
||
|
||
auth_info_default = g_new0 (gchar*, length + 1);
|
||
auth_info_default[0] = g_strdup (hostname);
|
||
auth_info_default[1] = g_strdup (username);
|
||
|
||
auth_info_display = g_new0 (gchar*, length + 1);
|
||
auth_info_display[1] = g_strdup (_("Username:"));
|
||
auth_info_display[2] = g_strdup (_("Password:"));
|
||
|
||
auth_info_visible = g_new0 (gboolean, length + 1);
|
||
auth_info_visible[1] = TRUE;
|
||
|
||
key = g_strconcat (username, "@", hostname, NULL);
|
||
password = g_hash_table_lookup (dispatch->backend->auth, key);
|
||
|
||
if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS backend: using stored password for %s\n", key));
|
||
|
||
overwrite_and_free (dispatch->request->password);
|
||
dispatch->request->password = g_strdup (password);
|
||
g_free (dispatch->request->username);
|
||
dispatch->request->username = g_strdup (username);
|
||
dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
|
||
}
|
||
else
|
||
{
|
||
const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
|
||
const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
|
||
char *printer_name = NULL;
|
||
|
||
if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
|
||
printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
|
||
|
||
if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
|
||
g_hash_table_remove (dispatch->backend->auth, key);
|
||
|
||
dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
|
||
|
||
dispatch->backend->authentication_lock = TRUE;
|
||
|
||
switch (ippGetOperation (dispatch->request->ipp_request))
|
||
{
|
||
case IPP_PRINT_JOB:
|
||
if (job_title != NULL && printer_name != NULL)
|
||
prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
|
||
else
|
||
prompt = g_strdup_printf ( _("Authentication is required to print a document on %s"), hostname);
|
||
break;
|
||
case IPP_GET_JOB_ATTRIBUTES:
|
||
if (job_title != NULL)
|
||
prompt = g_strdup_printf ( _("Authentication is required to get attributes of job “%s”"), job_title);
|
||
else
|
||
prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
|
||
break;
|
||
case IPP_GET_PRINTER_ATTRIBUTES:
|
||
if (printer_name != NULL)
|
||
prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
|
||
else
|
||
prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
|
||
break;
|
||
case CUPS_GET_DEFAULT:
|
||
prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
|
||
break;
|
||
case CUPS_GET_PRINTERS:
|
||
prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
|
||
break;
|
||
default:
|
||
/* work around gcc warning about 0 not being a value for this enum */
|
||
if (ippGetOperation (dispatch->request->ipp_request) == 0)
|
||
prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
|
||
else
|
||
prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
|
||
break;
|
||
}
|
||
|
||
g_free (printer_name);
|
||
|
||
g_signal_emit_by_name (dispatch->backend, "request-password",
|
||
auth_info_required, auth_info_default,
|
||
auth_info_display, auth_info_visible, prompt,
|
||
FALSE); /* Cups password is only cached not stored. */
|
||
|
||
g_free (prompt);
|
||
}
|
||
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
g_free (auth_info_required[i]);
|
||
g_free (auth_info_default[i]);
|
||
g_free (auth_info_display[i]);
|
||
}
|
||
|
||
g_free (auth_info_required);
|
||
g_free (auth_info_default);
|
||
g_free (auth_info_display);
|
||
g_free (auth_info_visible);
|
||
g_free (key);
|
||
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
static void
|
||
cups_dispatch_add_poll (GSource *source)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
GtkCupsPollState poll_state;
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) source;
|
||
|
||
poll_state = gtk_cups_request_get_poll_state (dispatch->request);
|
||
|
||
/* Remove the old source if the poll state changed. */
|
||
if (poll_state != dispatch->poll_state && dispatch->data_poll != NULL)
|
||
{
|
||
g_source_remove_poll (source, dispatch->data_poll);
|
||
g_free (dispatch->data_poll);
|
||
dispatch->data_poll = NULL;
|
||
}
|
||
|
||
if (dispatch->request->http != NULL)
|
||
{
|
||
if (dispatch->data_poll == NULL)
|
||
{
|
||
dispatch->data_poll = g_new0 (GPollFD, 1);
|
||
dispatch->poll_state = poll_state;
|
||
|
||
if (poll_state == GTK_CUPS_HTTP_READ)
|
||
dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
|
||
else if (poll_state == GTK_CUPS_HTTP_WRITE)
|
||
dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
|
||
else
|
||
dispatch->data_poll->events = 0;
|
||
|
||
dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
|
||
g_source_add_poll (source, dispatch->data_poll);
|
||
}
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
check_auth_info (gpointer user_data)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
dispatch = (GtkPrintCupsDispatchWatch *) user_data;
|
||
|
||
if (!dispatch->request->need_auth_info)
|
||
{
|
||
if (dispatch->request->auth_info == NULL)
|
||
{
|
||
dispatch->callback (GTK_PRINT_BACKEND (dispatch->backend),
|
||
gtk_cups_request_get_result (dispatch->request),
|
||
dispatch->callback_data);
|
||
g_source_destroy ((GSource *) dispatch);
|
||
}
|
||
else
|
||
{
|
||
gint length;
|
||
gint i;
|
||
|
||
length = g_strv_length (dispatch->request->auth_info_required);
|
||
|
||
gtk_cups_request_ipp_add_strings (dispatch->request,
|
||
IPP_TAG_JOB,
|
||
IPP_TAG_TEXT,
|
||
"auth-info",
|
||
length,
|
||
NULL,
|
||
(const char * const *) dispatch->request->auth_info);
|
||
|
||
g_source_attach ((GSource *) dispatch, NULL);
|
||
g_source_unref ((GSource *) dispatch);
|
||
|
||
for (i = 0; i < length; i++)
|
||
overwrite_and_free (dispatch->request->auth_info[i]);
|
||
g_free (dispatch->request->auth_info);
|
||
dispatch->request->auth_info = NULL;
|
||
}
|
||
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
return G_SOURCE_CONTINUE;
|
||
}
|
||
|
||
static void
|
||
lookup_auth_info_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GTask *task;
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
gchar **auth_info;
|
||
GError *error = NULL;
|
||
gint i;
|
||
|
||
task = (GTask *) res;
|
||
dispatch = user_data;
|
||
auth_info = g_task_propagate_pointer (task, &error);
|
||
|
||
if (auth_info == NULL)
|
||
{
|
||
if (error != NULL)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("Failed to look up auth info: %s\n", error->message));
|
||
g_error_free (error);
|
||
}
|
||
else
|
||
{
|
||
/* Error note should have been shown by the function causing this */
|
||
GTK_NOTE (PRINTING, g_print ("Failed to look up auth info.\n"));
|
||
}
|
||
dispatch->backend->authentication_lock = FALSE;
|
||
g_object_unref (task);
|
||
request_auth_info (dispatch);
|
||
return;
|
||
}
|
||
|
||
gtk_print_backend_cups_set_password (GTK_PRINT_BACKEND (dispatch->backend),
|
||
dispatch->request->auth_info_required, auth_info,
|
||
FALSE);
|
||
for (i = 0; auth_info[i] != NULL; i++)
|
||
{
|
||
overwrite_and_free (auth_info[i]);
|
||
auth_info[i] = NULL;
|
||
}
|
||
g_clear_pointer (auth_info, g_free);
|
||
|
||
g_object_unref (task);
|
||
}
|
||
|
||
static void
|
||
lookup_auth_info (gpointer user_data)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
gsize length,
|
||
i;
|
||
gboolean need_secret_auth_info = FALSE;
|
||
const gchar *printer_uri;
|
||
|
||
dispatch = user_data;
|
||
|
||
if (dispatch->backend->authentication_lock)
|
||
return;
|
||
|
||
length = g_strv_length (dispatch->request->auth_info_required);
|
||
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
|
||
{
|
||
need_secret_auth_info = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
g_idle_add (check_auth_info, user_data);
|
||
|
||
if (dispatch->backend->secrets_service_available && need_secret_auth_info)
|
||
{
|
||
dispatch->backend->authentication_lock = TRUE;
|
||
printer_uri = gtk_cups_request_ipp_get_string (dispatch->request,
|
||
IPP_TAG_URI,
|
||
"printer-uri");
|
||
gtk_cups_secrets_service_query_task (dispatch->backend,
|
||
dispatch->backend->secrets_service_cancellable,
|
||
lookup_auth_info_cb,
|
||
dispatch,
|
||
printer_uri,
|
||
dispatch->request->auth_info_required);
|
||
return;
|
||
}
|
||
|
||
request_auth_info (user_data);
|
||
}
|
||
|
||
static gboolean
|
||
request_auth_info (gpointer user_data)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
const char *job_title;
|
||
const char *printer_uri;
|
||
gchar *prompt = NULL;
|
||
char *printer_name = NULL;
|
||
gint length;
|
||
gint i;
|
||
gboolean *auth_info_visible = NULL;
|
||
gchar **auth_info_default = NULL;
|
||
gchar **auth_info_display = NULL;
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) user_data;
|
||
|
||
if (dispatch->backend->authentication_lock)
|
||
return FALSE;
|
||
|
||
job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
|
||
printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
|
||
length = g_strv_length (dispatch->request->auth_info_required);
|
||
|
||
auth_info_visible = g_new0 (gboolean, length);
|
||
auth_info_default = g_new0 (gchar *, length + 1);
|
||
auth_info_display = g_new0 (gchar *, length + 1);
|
||
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0)
|
||
{
|
||
auth_info_display[i] = g_strdup (_("Domain:"));
|
||
auth_info_default[i] = g_strdup ("WORKGROUP");
|
||
auth_info_visible[i] = TRUE;
|
||
}
|
||
else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0)
|
||
{
|
||
auth_info_display[i] = g_strdup (_("Username:"));
|
||
if (dispatch->backend->username != NULL)
|
||
auth_info_default[i] = g_strdup (dispatch->backend->username);
|
||
else
|
||
auth_info_default[i] = g_strdup (cupsUser ());
|
||
auth_info_visible[i] = TRUE;
|
||
}
|
||
else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
|
||
{
|
||
auth_info_display[i] = g_strdup (_("Password:"));
|
||
auth_info_visible[i] = FALSE;
|
||
}
|
||
}
|
||
|
||
if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
|
||
printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
|
||
|
||
dispatch->backend->authentication_lock = TRUE;
|
||
|
||
if (job_title != NULL)
|
||
{
|
||
if (printer_name != NULL)
|
||
prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
|
||
else
|
||
prompt = g_strdup_printf ( _("Authentication is required to print document “%s”"), job_title);
|
||
}
|
||
else
|
||
{
|
||
if (printer_name != NULL)
|
||
prompt = g_strdup_printf ( _("Authentication is required to print this document on printer %s"), printer_name);
|
||
else
|
||
prompt = g_strdup ( _("Authentication is required to print this document"));
|
||
}
|
||
|
||
g_signal_emit_by_name (dispatch->backend, "request-password",
|
||
dispatch->request->auth_info_required,
|
||
auth_info_default,
|
||
auth_info_display,
|
||
auth_info_visible,
|
||
prompt,
|
||
dispatch->backend->secrets_service_available);
|
||
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
g_free (auth_info_default[i]);
|
||
g_free (auth_info_display[i]);
|
||
}
|
||
|
||
g_free (auth_info_default);
|
||
g_free (auth_info_display);
|
||
g_free (printer_name);
|
||
g_free (prompt);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
cups_dispatch_watch_check (GSource *source)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
GtkCupsPollState poll_state;
|
||
gboolean result;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) source;
|
||
|
||
poll_state = gtk_cups_request_get_poll_state (dispatch->request);
|
||
|
||
if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
|
||
if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
|
||
return FALSE;
|
||
|
||
result = gtk_cups_request_read_write (dispatch->request, FALSE);
|
||
if (result && dispatch->data_poll != NULL)
|
||
{
|
||
g_source_remove_poll (source, dispatch->data_poll);
|
||
g_free (dispatch->data_poll);
|
||
dispatch->data_poll = NULL;
|
||
}
|
||
|
||
if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
|
||
{
|
||
dispatch->request->need_password = FALSE;
|
||
g_idle_add (request_password, dispatch);
|
||
result = FALSE;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static gboolean
|
||
cups_dispatch_watch_prepare (GSource *source,
|
||
gint *timeout_)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
gboolean result;
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) source;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
|
||
|
||
*timeout_ = -1;
|
||
|
||
result = gtk_cups_request_read_write (dispatch->request, TRUE);
|
||
|
||
cups_dispatch_add_poll (source);
|
||
|
||
return result;
|
||
}
|
||
|
||
static gboolean
|
||
cups_dispatch_watch_dispatch (GSource *source,
|
||
GSourceFunc callback,
|
||
gpointer user_data)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
GtkPrintCupsResponseCallbackFunc ep_callback;
|
||
GtkCupsResult *result;
|
||
|
||
g_assert (callback != NULL);
|
||
|
||
ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) source;
|
||
|
||
result = gtk_cups_request_get_result (dispatch->request);
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
|
||
|
||
if (gtk_cups_result_is_error (result))
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print("Error result: %s (type %i, status %i, code %i)\n",
|
||
gtk_cups_result_get_error_string (result),
|
||
gtk_cups_result_get_error_type (result),
|
||
gtk_cups_result_get_error_status (result),
|
||
gtk_cups_result_get_error_code (result)));
|
||
}
|
||
|
||
ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
cups_dispatch_watch_finalize (GSource *source)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
GtkCupsResult *result;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) source;
|
||
|
||
result = gtk_cups_request_get_result (dispatch->request);
|
||
if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
|
||
{
|
||
const gchar *username;
|
||
gchar hostname[HTTP_MAX_URI];
|
||
gchar *key;
|
||
|
||
httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
|
||
if (is_address_local (hostname))
|
||
strcpy (hostname, "localhost");
|
||
|
||
if (dispatch->backend->username != NULL)
|
||
username = dispatch->backend->username;
|
||
else
|
||
username = cupsUser ();
|
||
|
||
key = g_strconcat (username, "@", hostname, NULL);
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS backend: removing stored password for %s\n", key));
|
||
g_hash_table_remove (dispatch->backend->auth, key);
|
||
g_free (key);
|
||
|
||
if (dispatch->backend)
|
||
dispatch->backend->authentication_lock = FALSE;
|
||
}
|
||
|
||
gtk_cups_request_free (dispatch->request);
|
||
|
||
if (dispatch->backend)
|
||
{
|
||
/* We need to unref this at idle time, because it might be the
|
||
* last reference to this module causing the code to be
|
||
* unloaded (including this particular function!)
|
||
* Update: Doing this at idle caused a deadlock taking the
|
||
* mainloop context lock while being in a GSource callout for
|
||
* multithreaded apps. So, for now we just disable unloading
|
||
* of print backends. See _gtk_print_backend_create for the
|
||
* disabling.
|
||
*/
|
||
|
||
dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
|
||
|
||
|
||
g_object_unref (dispatch->backend);
|
||
dispatch->backend = NULL;
|
||
}
|
||
|
||
if (dispatch->data_poll)
|
||
{
|
||
g_source_remove_poll (source, dispatch->data_poll);
|
||
g_free (dispatch->data_poll);
|
||
dispatch->data_poll = NULL;
|
||
}
|
||
}
|
||
|
||
static GSourceFuncs _cups_dispatch_watch_funcs = {
|
||
cups_dispatch_watch_prepare,
|
||
cups_dispatch_watch_check,
|
||
cups_dispatch_watch_dispatch,
|
||
cups_dispatch_watch_finalize
|
||
};
|
||
|
||
|
||
static void
|
||
cups_request_execute (GtkPrintBackendCups *print_backend,
|
||
GtkCupsRequest *request,
|
||
GtkPrintCupsResponseCallbackFunc callback,
|
||
gpointer user_data,
|
||
GDestroyNotify notify)
|
||
{
|
||
GtkPrintCupsDispatchWatch *dispatch;
|
||
|
||
dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
|
||
sizeof (GtkPrintCupsDispatchWatch));
|
||
g_source_set_name (&dispatch->source, "GTK+ CUPS backend");
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
|
||
|
||
dispatch->request = request;
|
||
dispatch->backend = g_object_ref (print_backend);
|
||
dispatch->poll_state = GTK_CUPS_HTTP_IDLE;
|
||
dispatch->data_poll = NULL;
|
||
dispatch->callback = NULL;
|
||
dispatch->callback_data = NULL;
|
||
|
||
print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
|
||
|
||
g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
|
||
|
||
if (request->need_auth_info)
|
||
{
|
||
dispatch->callback = callback;
|
||
dispatch->callback_data = user_data;
|
||
lookup_auth_info (dispatch);
|
||
}
|
||
else
|
||
{
|
||
g_source_attach ((GSource *) dispatch, NULL);
|
||
g_source_unref ((GSource *) dispatch);
|
||
}
|
||
}
|
||
|
||
typedef struct {
|
||
GtkPrintBackendCups *print_backend;
|
||
GtkPrintJob *job;
|
||
int job_id;
|
||
int counter;
|
||
} CupsJobPollData;
|
||
|
||
static void
|
||
job_object_died (gpointer user_data,
|
||
GObject *where_the_object_was)
|
||
{
|
||
CupsJobPollData *data = user_data;
|
||
data->job = NULL;
|
||
}
|
||
|
||
static void
|
||
cups_job_poll_data_free (CupsJobPollData *data)
|
||
{
|
||
if (data->job)
|
||
g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
|
||
|
||
g_free (data);
|
||
}
|
||
|
||
static void
|
||
cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
|
||
GtkCupsResult *result,
|
||
gpointer user_data)
|
||
{
|
||
CupsJobPollData *data = user_data;
|
||
ipp_attribute_t *attr;
|
||
ipp_t *response;
|
||
int state;
|
||
gboolean done;
|
||
|
||
gdk_threads_enter ();
|
||
|
||
if (data->job == NULL)
|
||
{
|
||
cups_job_poll_data_free (data);
|
||
goto done;
|
||
}
|
||
|
||
data->counter++;
|
||
|
||
response = gtk_cups_result_get_response (result);
|
||
|
||
state = 0;
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
attr = ippFindAttribute (response, "job-state", IPP_TAG_ENUM);
|
||
state = ippGetInteger (attr, 0);
|
||
#else
|
||
for (attr = response->attrs; attr != NULL; attr = attr->next)
|
||
{
|
||
if (!attr->name)
|
||
continue;
|
||
|
||
_CUPS_MAP_ATTR_INT (attr, state, "job-state");
|
||
}
|
||
#endif
|
||
|
||
done = FALSE;
|
||
switch (state)
|
||
{
|
||
case IPP_JOB_PENDING:
|
||
case IPP_JOB_HELD:
|
||
case IPP_JOB_STOPPED:
|
||
gtk_print_job_set_status (data->job,
|
||
GTK_PRINT_STATUS_PENDING);
|
||
break;
|
||
case IPP_JOB_PROCESSING:
|
||
gtk_print_job_set_status (data->job,
|
||
GTK_PRINT_STATUS_PRINTING);
|
||
break;
|
||
default:
|
||
case IPP_JOB_CANCELLED:
|
||
case IPP_JOB_ABORTED:
|
||
gtk_print_job_set_status (data->job,
|
||
GTK_PRINT_STATUS_FINISHED_ABORTED);
|
||
done = TRUE;
|
||
break;
|
||
case 0:
|
||
case IPP_JOB_COMPLETED:
|
||
gtk_print_job_set_status (data->job,
|
||
GTK_PRINT_STATUS_FINISHED);
|
||
done = TRUE;
|
||
break;
|
||
}
|
||
|
||
if (!done && data->job != NULL)
|
||
{
|
||
guint32 timeout;
|
||
guint id;
|
||
|
||
if (data->counter < 5)
|
||
timeout = 100;
|
||
else if (data->counter < 10)
|
||
timeout = 500;
|
||
else
|
||
timeout = 1000;
|
||
|
||
id = g_timeout_add (timeout, cups_job_info_poll_timeout, data);
|
||
g_source_set_name_by_id (id, "[gtk+] cups_job_info_poll_timeout");
|
||
}
|
||
else
|
||
cups_job_poll_data_free (data);
|
||
|
||
done:
|
||
gdk_threads_leave ();
|
||
}
|
||
|
||
static void
|
||
cups_request_job_info (CupsJobPollData *data)
|
||
{
|
||
GtkCupsRequest *request;
|
||
gchar *job_uri;
|
||
|
||
request = gtk_cups_request_new_with_username (NULL,
|
||
GTK_CUPS_POST,
|
||
IPP_GET_JOB_ATTRIBUTES,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
data->print_backend->username);
|
||
|
||
job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
|
||
gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
|
||
"job-uri", NULL, job_uri);
|
||
g_free (job_uri);
|
||
|
||
cups_request_execute (data->print_backend,
|
||
request,
|
||
(GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
|
||
data,
|
||
NULL);
|
||
}
|
||
|
||
static gboolean
|
||
cups_job_info_poll_timeout (gpointer user_data)
|
||
{
|
||
CupsJobPollData *data = user_data;
|
||
|
||
if (data->job == NULL)
|
||
cups_job_poll_data_free (data);
|
||
else
|
||
cups_request_job_info (data);
|
||
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
static void
|
||
cups_begin_polling_info (GtkPrintBackendCups *print_backend,
|
||
GtkPrintJob *job,
|
||
gint job_id)
|
||
{
|
||
CupsJobPollData *data;
|
||
|
||
data = g_new0 (CupsJobPollData, 1);
|
||
|
||
data->print_backend = print_backend;
|
||
data->job = job;
|
||
data->job_id = job_id;
|
||
data->counter = 0;
|
||
|
||
g_object_weak_ref (G_OBJECT (job), job_object_died, data);
|
||
|
||
cups_request_job_info (data);
|
||
}
|
||
|
||
static void
|
||
mark_printer_inactive (GtkPrinter *printer,
|
||
GtkPrintBackend *backend)
|
||
{
|
||
gtk_printer_set_is_active (printer, FALSE);
|
||
g_signal_emit_by_name (backend, "printer-removed", printer);
|
||
}
|
||
|
||
static gint
|
||
find_printer (GtkPrinter *printer,
|
||
const gchar *find_name)
|
||
{
|
||
const gchar *printer_name;
|
||
|
||
printer_name = gtk_printer_get_name (printer);
|
||
return g_ascii_strcasecmp (printer_name, find_name);
|
||
}
|
||
/* Printer messages we're interested in */
|
||
static const char * const printer_messages[] =
|
||
{
|
||
"toner-low",
|
||
"toner-empty",
|
||
"developer-low",
|
||
"developer-empty",
|
||
"marker-supply-low",
|
||
"marker-supply-empty",
|
||
"cover-open",
|
||
"door-open",
|
||
"media-low",
|
||
"media-empty",
|
||
"offline",
|
||
"other"
|
||
};
|
||
|
||
/* Attributes we're interested in for printers */
|
||
static const char * const printer_attrs[] =
|
||
{
|
||
"printer-name",
|
||
"printer-uri-supported",
|
||
"member-uris",
|
||
"printer-location",
|
||
"printer-info",
|
||
"printer-state-message",
|
||
"printer-state-reasons",
|
||
"printer-state",
|
||
"queued-job-count",
|
||
"printer-is-accepting-jobs",
|
||
"job-sheets-supported",
|
||
"job-sheets-default",
|
||
"printer-type",
|
||
"auth-info-required",
|
||
"number-up-default",
|
||
"ipp-versions-supported",
|
||
"multiple-document-handling-supported",
|
||
"copies-supported",
|
||
"number-up-supported"
|
||
};
|
||
|
||
/* Attributes we're interested in for printers without PPD */
|
||
static const char * const printer_attrs_detailed[] =
|
||
{
|
||
"printer-name",
|
||
"printer-uri-supported",
|
||
"member-uris",
|
||
"printer-location",
|
||
"printer-info",
|
||
"printer-state-message",
|
||
"printer-state-reasons",
|
||
"printer-state",
|
||
"queued-job-count",
|
||
"printer-is-accepting-jobs",
|
||
"job-sheets-supported",
|
||
"job-sheets-default",
|
||
"printer-type",
|
||
"auth-info-required",
|
||
"number-up-default",
|
||
"ipp-versions-supported",
|
||
"multiple-document-handling-supported",
|
||
"copies-supported",
|
||
"number-up-supported",
|
||
"media-col-default",
|
||
"media-col-supported",
|
||
"media-default",
|
||
"media-size-supported",
|
||
"media-supported",
|
||
"media-left-margin-supported",
|
||
"media-right-margin-supported",
|
||
"media-bottom-margin-supported",
|
||
"media-top-margin-supported",
|
||
"sides-default",
|
||
"sides-supported",
|
||
"output-bin-default",
|
||
"output-bin-supported",
|
||
};
|
||
|
||
typedef enum
|
||
{
|
||
GTK_PRINTER_STATE_LEVEL_NONE = 0,
|
||
GTK_PRINTER_STATE_LEVEL_INFO = 1,
|
||
GTK_PRINTER_STATE_LEVEL_WARNING = 2,
|
||
GTK_PRINTER_STATE_LEVEL_ERROR = 3
|
||
} PrinterStateLevel;
|
||
|
||
typedef struct
|
||
{
|
||
float x_dimension;
|
||
float y_dimension;
|
||
} MediaSize;
|
||
|
||
typedef struct
|
||
{
|
||
const gchar *printer_name;
|
||
const gchar *printer_uri;
|
||
const gchar *member_uris;
|
||
const gchar *location;
|
||
const gchar *description;
|
||
gchar *state_msg;
|
||
const gchar *reason_msg;
|
||
PrinterStateLevel reason_level;
|
||
gint state;
|
||
gint job_count;
|
||
gboolean is_paused;
|
||
gboolean is_accepting_jobs;
|
||
const gchar *default_cover_before;
|
||
const gchar *default_cover_after;
|
||
gboolean default_printer;
|
||
gboolean got_printer_type;
|
||
gboolean remote_printer;
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
gboolean avahi_printer;
|
||
#endif
|
||
gchar **auth_info_required;
|
||
gint default_number_up;
|
||
guchar ipp_version_major;
|
||
guchar ipp_version_minor;
|
||
gboolean supports_copies;
|
||
gboolean supports_collate;
|
||
gboolean supports_number_up;
|
||
gchar *media_default;
|
||
GList *media_supported;
|
||
GList *media_size_supported;
|
||
float media_bottom_margin_default;
|
||
float media_top_margin_default;
|
||
float media_left_margin_default;
|
||
float media_right_margin_default;
|
||
gboolean media_margin_default_set;
|
||
gchar *sides_default;
|
||
GList *sides_supported;
|
||
char **covers;
|
||
int number_of_covers;
|
||
gchar *output_bin_default;
|
||
GList *output_bin_supported;
|
||
} PrinterSetupInfo;
|
||
|
||
static void
|
||
printer_setup_info_free (PrinterSetupInfo *info)
|
||
{
|
||
g_free (info->state_msg);
|
||
g_strfreev (info->covers);
|
||
g_slice_free (PrinterSetupInfo, info);
|
||
}
|
||
|
||
static void
|
||
get_ipp_version (const char *ipp_version_string,
|
||
guchar *ipp_version_major,
|
||
guchar *ipp_version_minor)
|
||
{
|
||
gchar **ipp_version_strv;
|
||
gchar *endptr;
|
||
|
||
*ipp_version_major = 1;
|
||
*ipp_version_minor = 1;
|
||
|
||
if (ipp_version_string)
|
||
{
|
||
ipp_version_strv = g_strsplit (ipp_version_string, ".", 0);
|
||
|
||
if (ipp_version_strv)
|
||
{
|
||
if (g_strv_length (ipp_version_strv) == 2)
|
||
{
|
||
*ipp_version_major = (guchar) g_ascii_strtoull (ipp_version_strv[0], &endptr, 10);
|
||
if (endptr == ipp_version_strv[0])
|
||
*ipp_version_major = 1;
|
||
|
||
*ipp_version_minor = (guchar) g_ascii_strtoull (ipp_version_strv[1], &endptr, 10);
|
||
if (endptr == ipp_version_strv[1])
|
||
*ipp_version_minor = 1;
|
||
}
|
||
|
||
g_strfreev (ipp_version_strv);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
get_server_ipp_version (guchar *ipp_version_major,
|
||
guchar *ipp_version_minor)
|
||
{
|
||
*ipp_version_major = 1;
|
||
*ipp_version_minor = 1;
|
||
|
||
if (IPP_VERSION && strlen (IPP_VERSION) == 2)
|
||
{
|
||
*ipp_version_major = (unsigned char) IPP_VERSION[0];
|
||
*ipp_version_minor = (unsigned char) IPP_VERSION[1];
|
||
}
|
||
}
|
||
|
||
static gint
|
||
ipp_version_cmp (guchar ipp_version_major1,
|
||
guchar ipp_version_minor1,
|
||
guchar ipp_version_major2,
|
||
guchar ipp_version_minor2)
|
||
{
|
||
if (ipp_version_major1 == ipp_version_major2 &&
|
||
ipp_version_minor1 == ipp_version_minor2)
|
||
{
|
||
return 0;
|
||
}
|
||
else if (ipp_version_major1 < ipp_version_major2 ||
|
||
(ipp_version_major1 == ipp_version_major2 &&
|
||
ipp_version_minor1 < ipp_version_minor2))
|
||
{
|
||
return -1;
|
||
}
|
||
else
|
||
{
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
static void
|
||
cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
|
||
ipp_attribute_t *attr,
|
||
PrinterSetupInfo *info)
|
||
{
|
||
gint i, j;
|
||
if (strcmp (ippGetName (attr), "printer-name") == 0 &&
|
||
ippGetValueTag (attr) == IPP_TAG_NAME)
|
||
info->printer_name = ippGetString (attr, 0, NULL);
|
||
else if (strcmp (ippGetName (attr), "printer-uri-supported") == 0 &&
|
||
ippGetValueTag (attr) == IPP_TAG_URI)
|
||
info->printer_uri = ippGetString (attr, 0, NULL);
|
||
else if (strcmp (ippGetName (attr), "member-uris") == 0 &&
|
||
ippGetValueTag (attr) == IPP_TAG_URI)
|
||
info->member_uris = ippGetString (attr, 0, NULL);
|
||
else if (strcmp (ippGetName (attr), "printer-location") == 0)
|
||
info->location = ippGetString (attr, 0, NULL);
|
||
else if (strcmp (ippGetName (attr), "printer-info") == 0)
|
||
info->description = ippGetString (attr, 0, NULL);
|
||
else if (strcmp (ippGetName (attr), "printer-state-message") == 0)
|
||
info->state_msg = g_strdup (ippGetString (attr, 0, NULL));
|
||
else if (strcmp (ippGetName (attr), "printer-state-reasons") == 0)
|
||
/* Store most important reason to reason_msg and set
|
||
its importance at printer_state_reason_level */
|
||
{
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
{
|
||
if (strcmp (ippGetString (attr, i, NULL), "none") != 0)
|
||
{
|
||
gboolean interested_in = FALSE;
|
||
/* Sets is_paused flag for paused printer. */
|
||
if (strcmp (ippGetString (attr, i, NULL), "paused") == 0)
|
||
{
|
||
info->is_paused = TRUE;
|
||
}
|
||
|
||
for (j = 0; j < G_N_ELEMENTS (printer_messages); j++)
|
||
if (strncmp (ippGetString (attr, i, NULL), printer_messages[j],
|
||
strlen (printer_messages[j])) == 0)
|
||
{
|
||
interested_in = TRUE;
|
||
break;
|
||
}
|
||
|
||
if (interested_in)
|
||
{
|
||
if (g_str_has_suffix (ippGetString (attr, i, NULL), "-report"))
|
||
{
|
||
if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_INFO)
|
||
{
|
||
info->reason_msg = ippGetString (attr, i, NULL);
|
||
info->reason_level = GTK_PRINTER_STATE_LEVEL_INFO;
|
||
}
|
||
}
|
||
else if (g_str_has_suffix (ippGetString (attr, i, NULL), "-warning"))
|
||
{
|
||
if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_WARNING)
|
||
{
|
||
info->reason_msg = ippGetString (attr, i, NULL);
|
||
info->reason_level = GTK_PRINTER_STATE_LEVEL_WARNING;
|
||
}
|
||
}
|
||
else /* It is error in the case of no suffix. */
|
||
{
|
||
info->reason_msg = ippGetString (attr, i, NULL);
|
||
info->reason_level = GTK_PRINTER_STATE_LEVEL_ERROR;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (strcmp (ippGetName (attr), "printer-state") == 0)
|
||
info->state = ippGetInteger (attr, 0);
|
||
else if (strcmp (ippGetName (attr), "queued-job-count") == 0)
|
||
info->job_count = ippGetInteger (attr, 0);
|
||
else if (strcmp (ippGetName (attr), "printer-is-accepting-jobs") == 0)
|
||
{
|
||
if (ippGetBoolean (attr, 0) == 1)
|
||
info->is_accepting_jobs = TRUE;
|
||
else
|
||
info->is_accepting_jobs = FALSE;
|
||
}
|
||
else if (strcmp (ippGetName (attr), "job-sheets-supported") == 0)
|
||
{
|
||
info->number_of_covers = ippGetCount (attr);
|
||
info->covers = g_new (char *, info->number_of_covers + 1);
|
||
for (i = 0; i < info->number_of_covers; i++)
|
||
info->covers[i] = g_strdup (ippGetString (attr, i, NULL));
|
||
info->covers[info->number_of_covers] = NULL;
|
||
}
|
||
else if (strcmp (ippGetName (attr), "job-sheets-default") == 0)
|
||
{
|
||
if (ippGetCount (attr) == 2)
|
||
{
|
||
info->default_cover_before = ippGetString (attr, 0, NULL);
|
||
info->default_cover_after = ippGetString (attr, 1, NULL);
|
||
}
|
||
}
|
||
else if (strcmp (ippGetName (attr), "printer-type") == 0)
|
||
{
|
||
info->got_printer_type = TRUE;
|
||
if (ippGetInteger (attr, 0) & 0x00020000)
|
||
info->default_printer = TRUE;
|
||
else
|
||
info->default_printer = FALSE;
|
||
|
||
if (ippGetInteger (attr, 0) & 0x00000002)
|
||
info->remote_printer = TRUE;
|
||
else
|
||
info->remote_printer = FALSE;
|
||
}
|
||
else if (strcmp (ippGetName (attr), "auth-info-required") == 0)
|
||
{
|
||
if (strcmp (ippGetString (attr, 0, NULL), "none") != 0)
|
||
{
|
||
info->auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
|
||
}
|
||
}
|
||
else if (strcmp (ippGetName (attr), "number-up-default") == 0)
|
||
{
|
||
info->default_number_up = ippGetInteger (attr, 0);
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "ipp-versions-supported") == 0)
|
||
{
|
||
guchar server_ipp_version_major;
|
||
guchar server_ipp_version_minor;
|
||
guchar ipp_version_major;
|
||
guchar ipp_version_minor;
|
||
|
||
get_server_ipp_version (&server_ipp_version_major,
|
||
&server_ipp_version_minor);
|
||
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
{
|
||
get_ipp_version (ippGetString (attr, i, NULL),
|
||
&ipp_version_major,
|
||
&ipp_version_minor);
|
||
|
||
if (ipp_version_cmp (ipp_version_major,
|
||
ipp_version_minor,
|
||
info->ipp_version_major,
|
||
info->ipp_version_minor) > 0 &&
|
||
ipp_version_cmp (ipp_version_major,
|
||
ipp_version_minor,
|
||
server_ipp_version_major,
|
||
server_ipp_version_minor) <= 0)
|
||
{
|
||
info->ipp_version_major = ipp_version_major;
|
||
info->ipp_version_minor = ipp_version_minor;
|
||
}
|
||
}
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "number-up-supported") == 0)
|
||
{
|
||
if (ippGetCount (attr) == 6)
|
||
{
|
||
info->supports_number_up = TRUE;
|
||
}
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "copies-supported") == 0)
|
||
{
|
||
int upper = 1;
|
||
|
||
ippGetRange (attr, 0, &upper);
|
||
if (upper > 1)
|
||
{
|
||
info->supports_copies = TRUE;
|
||
}
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "multiple-document-handling-supported") == 0)
|
||
{
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
{
|
||
if (g_strcmp0 (ippGetString (attr, i, NULL), "separate-documents-collated-copies") == 0)
|
||
{
|
||
info->supports_collate = TRUE;
|
||
}
|
||
}
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "sides-default") == 0)
|
||
{
|
||
info->sides_default = g_strdup (ippGetString (attr, 0, NULL));
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "sides-supported") == 0)
|
||
{
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
info->sides_supported = g_list_prepend (info->sides_supported, g_strdup (ippGetString (attr, i, NULL)));
|
||
|
||
info->sides_supported = g_list_reverse (info->sides_supported);
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "media-default") == 0)
|
||
{
|
||
if (ippGetValueTag (attr) == IPP_TAG_KEYWORD ||
|
||
ippGetValueTag (attr) == IPP_TAG_NAME)
|
||
info->media_default = g_strdup (ippGetString (attr, 0, NULL));
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "media-col-default") == 0)
|
||
{
|
||
ipp_attribute_t *iter;
|
||
ipp_t *col;
|
||
gint num_of_margins = 0;
|
||
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
{
|
||
col = ippGetCollection (attr, i);
|
||
for (iter = ippFirstAttribute (col); iter != NULL; iter = ippNextAttribute (col))
|
||
{
|
||
switch (ippGetValueTag (iter))
|
||
{
|
||
case IPP_TAG_INTEGER:
|
||
if (g_strcmp0 (ippGetName (iter), "media-bottom-margin") == 0)
|
||
{
|
||
info->media_bottom_margin_default = ippGetInteger (iter, 0) / 100.0;
|
||
num_of_margins++;
|
||
}
|
||
else if (g_strcmp0 (ippGetName (iter), "media-top-margin") == 0)
|
||
{
|
||
info->media_top_margin_default = ippGetInteger (iter, 0) / 100.0;
|
||
num_of_margins++;
|
||
}
|
||
else if (g_strcmp0 (ippGetName (iter), "media-left-margin") == 0)
|
||
{
|
||
info->media_left_margin_default = ippGetInteger (iter, 0) / 100.0;
|
||
num_of_margins++;
|
||
}
|
||
else if (g_strcmp0 (ippGetName (iter), "media-right-margin") == 0)
|
||
{
|
||
info->media_right_margin_default = ippGetInteger (iter, 0) / 100.0;
|
||
num_of_margins++;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (num_of_margins == 4)
|
||
info->media_margin_default_set = TRUE;
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "media-supported") == 0)
|
||
{
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
info->media_supported = g_list_prepend (info->media_supported, g_strdup (ippGetString (attr, i, NULL)));
|
||
|
||
info->media_supported = g_list_reverse (info->media_supported);
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "media-size-supported") == 0)
|
||
{
|
||
ipp_attribute_t *iter;
|
||
MediaSize *media_size;
|
||
gboolean number_of_dimensions;
|
||
ipp_t *media_size_collection;
|
||
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
{
|
||
media_size_collection = ippGetCollection (attr, i);
|
||
media_size = g_new0 (MediaSize, 1);
|
||
number_of_dimensions = 0;
|
||
|
||
for (iter = ippFirstAttribute (media_size_collection);
|
||
iter != NULL;
|
||
iter = ippNextAttribute (media_size_collection))
|
||
{
|
||
if (g_strcmp0 (ippGetName (iter), "x-dimension") == 0 &&
|
||
ippGetValueTag (iter) == IPP_TAG_INTEGER)
|
||
{
|
||
media_size->x_dimension = ippGetInteger (iter, 0) / 100.0;
|
||
number_of_dimensions++;
|
||
}
|
||
else if (g_strcmp0 (ippGetName (iter), "y-dimension") == 0 &&
|
||
ippGetValueTag (iter) == IPP_TAG_INTEGER)
|
||
{
|
||
media_size->y_dimension = ippGetInteger (iter, 0) / 100.0;
|
||
number_of_dimensions++;
|
||
}
|
||
}
|
||
|
||
if (number_of_dimensions == 2)
|
||
info->media_size_supported = g_list_prepend (info->media_size_supported, media_size);
|
||
else
|
||
g_free (media_size);
|
||
}
|
||
|
||
info->media_size_supported = g_list_reverse (info->media_size_supported);
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "output-bin-default") == 0)
|
||
{
|
||
info->output_bin_default = g_strdup (ippGetString (attr, 0, NULL));
|
||
}
|
||
else if (g_strcmp0 (ippGetName (attr), "output-bin-supported") == 0)
|
||
{
|
||
for (i = 0; i < ippGetCount (attr); i++)
|
||
info->output_bin_supported = g_list_prepend (info->output_bin_supported, g_strdup (ippGetString (attr, i, NULL)));
|
||
|
||
info->output_bin_supported = g_list_reverse (info->output_bin_supported);
|
||
}
|
||
else
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Attribute %s ignored", ippGetName (attr)));
|
||
}
|
||
}
|
||
|
||
static GtkPrinter*
|
||
cups_create_printer (GtkPrintBackendCups *cups_backend,
|
||
PrinterSetupInfo *info)
|
||
{
|
||
GtkPrinterCups *cups_printer;
|
||
GtkPrinter *printer;
|
||
GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
|
||
char uri[HTTP_MAX_URI]; /* Printer URI */
|
||
char method[HTTP_MAX_URI]; /* Method/scheme name */
|
||
char username[HTTP_MAX_URI]; /* Username:password */
|
||
char hostname[HTTP_MAX_URI]; /* Hostname */
|
||
char resource[HTTP_MAX_URI]; /* Resource name */
|
||
int port; /* Port number */
|
||
char *cups_server; /* CUPS server */
|
||
|
||
#ifdef HAVE_COLORD
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
if (info->avahi_printer)
|
||
cups_printer = gtk_printer_cups_new (info->printer_name,
|
||
backend,
|
||
NULL);
|
||
else
|
||
#endif
|
||
cups_printer = gtk_printer_cups_new (info->printer_name,
|
||
backend,
|
||
cups_backend->colord_client);
|
||
#else
|
||
cups_printer = gtk_printer_cups_new (info->printer_name, backend, NULL);
|
||
#endif
|
||
|
||
cups_printer->device_uri = g_strdup_printf ("/printers/%s",
|
||
info->printer_name);
|
||
|
||
/* Check to see if we are looking at a class */
|
||
if (info->member_uris)
|
||
{
|
||
cups_printer->printer_uri = g_strdup (info->member_uris);
|
||
/* TODO if member_uris is a class we need to recursivly find a printer */
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Found class with printer %s\n",
|
||
info->member_uris));
|
||
}
|
||
else
|
||
{
|
||
cups_printer->printer_uri = g_strdup (info->printer_uri);
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Found printer %s\n",
|
||
info->printer_uri));
|
||
}
|
||
|
||
httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
|
||
method, sizeof (method),
|
||
username, sizeof (username),
|
||
hostname, sizeof (hostname),
|
||
&port,
|
||
resource, sizeof (resource));
|
||
|
||
if (strncmp (resource, "/printers/", 10) == 0)
|
||
{
|
||
cups_printer->ppd_name = g_strdup (resource + 10);
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, info->printer_name));
|
||
}
|
||
|
||
gethostname (uri, sizeof (uri));
|
||
cups_server = g_strdup (cupsServer());
|
||
|
||
if (strcasecmp (uri, hostname) == 0)
|
||
strcpy (hostname, "localhost");
|
||
|
||
/* if the cups server is local and listening at a unix domain socket
|
||
* then use the socket connection
|
||
*/
|
||
if ((strstr (hostname, "localhost") != NULL) &&
|
||
(cups_server[0] == '/'))
|
||
strcpy (hostname, cups_server);
|
||
|
||
g_free (cups_server);
|
||
|
||
cups_printer->default_cover_before = g_strdup (info->default_cover_before);
|
||
cups_printer->default_cover_after = g_strdup (info->default_cover_after);
|
||
|
||
if (info->default_number_up > 0)
|
||
cups_printer->default_number_up = info->default_number_up;
|
||
|
||
cups_printer->hostname = g_strdup (hostname);
|
||
cups_printer->port = port;
|
||
|
||
cups_printer->auth_info_required = g_strdupv (info->auth_info_required);
|
||
g_strfreev (info->auth_info_required);
|
||
|
||
printer = GTK_PRINTER (cups_printer);
|
||
|
||
if (cups_backend->default_printer != NULL &&
|
||
strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
|
||
gtk_printer_set_is_default (printer, TRUE);
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
cups_printer->avahi_browsed = info->avahi_printer;
|
||
#endif
|
||
|
||
gtk_print_backend_add_printer (backend, printer);
|
||
return printer;
|
||
}
|
||
|
||
static void
|
||
set_printer_icon_name_from_info (GtkPrinter *printer,
|
||
PrinterSetupInfo *info)
|
||
{
|
||
/* Set printer icon according to importance
|
||
(none, report, warning, error - report is omitted). */
|
||
if (info->reason_level == GTK_PRINTER_STATE_LEVEL_ERROR)
|
||
gtk_printer_set_icon_name (printer, "printer-error");
|
||
else if (info->reason_level == GTK_PRINTER_STATE_LEVEL_WARNING)
|
||
gtk_printer_set_icon_name (printer, "printer-warning");
|
||
else if (gtk_printer_is_paused (printer))
|
||
gtk_printer_set_icon_name (printer, "printer-paused");
|
||
else
|
||
gtk_printer_set_icon_name (printer, "printer");
|
||
}
|
||
|
||
static gchar *
|
||
get_reason_msg_desc (guint i,
|
||
const gchar *printer_name)
|
||
{
|
||
gchar *reason_msg_desc;
|
||
|
||
/* The numbers must match the indices in the printer_messages array */
|
||
switch (i)
|
||
{
|
||
case 0:
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on toner."),
|
||
printer_name);
|
||
break;
|
||
case 1:
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” has no toner left."),
|
||
printer_name);
|
||
break;
|
||
case 2:
|
||
/* Translators: "Developer" like on photo development context */
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on developer."),
|
||
printer_name);
|
||
break;
|
||
case 3:
|
||
/* Translators: "Developer" like on photo development context */
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of developer."),
|
||
printer_name);
|
||
break;
|
||
case 4:
|
||
/* Translators: "marker" is one color bin of the printer */
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on at least one marker supply."),
|
||
printer_name);
|
||
break;
|
||
case 5:
|
||
/* Translators: "marker" is one color bin of the printer */
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of at least one marker supply."),
|
||
printer_name);
|
||
break;
|
||
case 6:
|
||
reason_msg_desc = g_strdup_printf (_("The cover is open on printer “%s”."),
|
||
printer_name);
|
||
break;
|
||
case 7:
|
||
reason_msg_desc = g_strdup_printf (_("The door is open on printer “%s”."),
|
||
printer_name);
|
||
break;
|
||
case 8:
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on paper."),
|
||
printer_name);
|
||
break;
|
||
case 9:
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of paper."),
|
||
printer_name);
|
||
break;
|
||
case 10:
|
||
reason_msg_desc = g_strdup_printf (_("Printer “%s” is currently offline."),
|
||
printer_name);
|
||
break;
|
||
case 11:
|
||
reason_msg_desc = g_strdup_printf (_("There is a problem on printer “%s”."),
|
||
printer_name);
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
return reason_msg_desc;
|
||
}
|
||
|
||
static void
|
||
set_info_state_message (PrinterSetupInfo *info)
|
||
{
|
||
gint i;
|
||
|
||
if (info->state_msg == NULL || strlen (info->state_msg) == 0)
|
||
{
|
||
gchar *tmp_msg2 = NULL;
|
||
if (info->is_paused && !info->is_accepting_jobs)
|
||
/* Translators: this is a printer status. */
|
||
tmp_msg2 = g_strdup ( _("Paused; Rejecting Jobs"));
|
||
if (info->is_paused && info->is_accepting_jobs)
|
||
/* Translators: this is a printer status. */
|
||
tmp_msg2 = g_strdup ( _("Paused"));
|
||
if (!info->is_paused && !info->is_accepting_jobs)
|
||
/* Translators: this is a printer status. */
|
||
tmp_msg2 = g_strdup ( _("Rejecting Jobs"));
|
||
|
||
if (tmp_msg2 != NULL)
|
||
{
|
||
g_free (info->state_msg);
|
||
info->state_msg = tmp_msg2;
|
||
}
|
||
}
|
||
|
||
/* Set description of the reason and combine it with printer-state-message. */
|
||
if (info->reason_msg)
|
||
{
|
||
gchar *reason_msg_desc = NULL;
|
||
gboolean found = FALSE;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
|
||
{
|
||
if (strncmp (info->reason_msg, printer_messages[i],
|
||
strlen (printer_messages[i])) == 0)
|
||
{
|
||
reason_msg_desc = get_reason_msg_desc (i, info->printer_name);
|
||
found = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!found)
|
||
info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
|
||
|
||
if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
|
||
{
|
||
if (info->state_msg == NULL || info->state_msg[0] == '\0')
|
||
{
|
||
g_free (info->state_msg);
|
||
info->state_msg = reason_msg_desc;
|
||
reason_msg_desc = NULL;
|
||
}
|
||
else
|
||
{
|
||
gchar *tmp_msg = NULL;
|
||
/* Translators: this string connects multiple printer states together. */
|
||
tmp_msg = g_strjoin ( _("; "), info->state_msg,
|
||
reason_msg_desc, NULL);
|
||
g_free (info->state_msg);
|
||
info->state_msg = tmp_msg;
|
||
}
|
||
}
|
||
|
||
g_free (reason_msg_desc);
|
||
}
|
||
}
|
||
|
||
static void
|
||
set_default_printer (GtkPrintBackendCups *cups_backend,
|
||
const gchar *default_printer_name)
|
||
{
|
||
cups_backend->default_printer = g_strdup (default_printer_name);
|
||
cups_backend->got_default_printer = TRUE;
|
||
|
||
if (cups_backend->default_printer != NULL)
|
||
{
|
||
GtkPrinter *default_printer = NULL;
|
||
default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
|
||
cups_backend->default_printer);
|
||
if (default_printer != NULL)
|
||
{
|
||
gtk_printer_set_is_default (default_printer, TRUE);
|
||
g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
|
||
"printer-status-changed", default_printer);
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
static void
|
||
cups_request_printer_info_cb (GtkPrintBackendCups *cups_backend,
|
||
GtkCupsResult *result,
|
||
gpointer user_data)
|
||
{
|
||
PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
|
||
GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
|
||
ipp_attribute_t *attr;
|
||
GtkPrinter *printer;
|
||
gboolean status_changed = FALSE;
|
||
ipp_t *response;
|
||
|
||
gdk_threads_enter ();
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
if (gtk_cups_result_is_error (result))
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_warning ("CUPS Backend: Error getting printer info: %s %d %d",
|
||
gtk_cups_result_get_error_string (result),
|
||
gtk_cups_result_get_error_type (result),
|
||
gtk_cups_result_get_error_code (result)));
|
||
|
||
goto done;
|
||
}
|
||
|
||
response = gtk_cups_result_get_response (result);
|
||
attr = ippFirstAttribute (response);
|
||
while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
|
||
attr = ippNextAttribute (response);
|
||
|
||
if (attr)
|
||
{
|
||
while (attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
|
||
{
|
||
cups_printer_handle_attribute (cups_backend, attr, info);
|
||
attr = ippNextAttribute (response);
|
||
}
|
||
|
||
if (info->printer_name && info->printer_uri)
|
||
{
|
||
set_info_state_message (info);
|
||
|
||
printer = gtk_print_backend_find_printer (backend, info->printer_name);
|
||
if (printer != NULL)
|
||
g_object_ref (printer);
|
||
else
|
||
goto done;
|
||
|
||
if (info->got_printer_type &&
|
||
info->default_printer &&
|
||
cups_backend->avahi_default_printer == NULL)
|
||
cups_backend->avahi_default_printer = g_strdup (info->printer_name);
|
||
|
||
gtk_printer_set_is_paused (printer, info->is_paused);
|
||
gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
|
||
|
||
GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
|
||
GTK_PRINTER_CUPS (printer)->state = info->state;
|
||
GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
|
||
GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
|
||
GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
|
||
GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
|
||
GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
|
||
GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
|
||
GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
|
||
status_changed = gtk_printer_set_job_count (printer, info->job_count);
|
||
status_changed |= gtk_printer_set_location (printer, info->location);
|
||
status_changed |= gtk_printer_set_description (printer, info->description);
|
||
status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
|
||
status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
|
||
|
||
set_printer_icon_name_from_info (printer, info);
|
||
|
||
GTK_PRINTER_CUPS (printer)->media_default = info->media_default;
|
||
GTK_PRINTER_CUPS (printer)->media_supported = info->media_supported;
|
||
GTK_PRINTER_CUPS (printer)->media_size_supported = info->media_size_supported;
|
||
if (info->media_margin_default_set)
|
||
{
|
||
GTK_PRINTER_CUPS (printer)->media_margin_default_set = TRUE;
|
||
GTK_PRINTER_CUPS (printer)->media_bottom_margin_default = info->media_bottom_margin_default;
|
||
GTK_PRINTER_CUPS (printer)->media_top_margin_default = info->media_top_margin_default;
|
||
GTK_PRINTER_CUPS (printer)->media_left_margin_default = info->media_left_margin_default;
|
||
GTK_PRINTER_CUPS (printer)->media_right_margin_default = info->media_right_margin_default;
|
||
}
|
||
GTK_PRINTER_CUPS (printer)->sides_default = info->sides_default;
|
||
GTK_PRINTER_CUPS (printer)->sides_supported = info->sides_supported;
|
||
GTK_PRINTER_CUPS (printer)->output_bin_default = info->output_bin_default;
|
||
GTK_PRINTER_CUPS (printer)->output_bin_supported = info->output_bin_supported;
|
||
|
||
gtk_printer_set_has_details (printer, TRUE);
|
||
g_signal_emit_by_name (printer, "details-acquired", TRUE);
|
||
|
||
if (status_changed)
|
||
g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
|
||
"printer-status-changed", printer);
|
||
|
||
/* The ref is held by GtkPrintBackend, in add_printer() */
|
||
g_object_unref (printer);
|
||
}
|
||
}
|
||
|
||
done:
|
||
if (!cups_backend->got_default_printer &&
|
||
gtk_print_backend_printer_list_is_done (backend) &&
|
||
cups_backend->avahi_default_printer != NULL)
|
||
{
|
||
set_default_printer (cups_backend, cups_backend->avahi_default_printer);
|
||
}
|
||
|
||
printer_setup_info_free (info);
|
||
|
||
gdk_threads_leave ();
|
||
}
|
||
|
||
static void
|
||
cups_request_printer_info (const gchar *printer_uri,
|
||
const gchar *host,
|
||
gint port,
|
||
GtkPrintBackendCups *backend)
|
||
{
|
||
GtkCupsRequest *request;
|
||
http_t *http;
|
||
|
||
http = httpConnect (host, port);
|
||
if (http)
|
||
{
|
||
request = gtk_cups_request_new_with_username (http,
|
||
GTK_CUPS_POST,
|
||
IPP_GET_PRINTER_ATTRIBUTES,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
backend->username);
|
||
|
||
gtk_cups_request_set_ipp_version (request, 1, 1);
|
||
|
||
gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
|
||
"printer-uri", NULL, printer_uri);
|
||
|
||
gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
|
||
"requested-attributes", G_N_ELEMENTS (printer_attrs_detailed),
|
||
NULL, printer_attrs_detailed);
|
||
|
||
cups_request_execute (backend,
|
||
request,
|
||
(GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
|
||
http,
|
||
(GDestroyNotify) httpClose);
|
||
}
|
||
}
|
||
|
||
typedef struct
|
||
{
|
||
gchar *printer_uri;
|
||
gchar *location;
|
||
gchar *host;
|
||
gint port;
|
||
gchar *printer_name;
|
||
gchar *name;
|
||
gboolean got_printer_type;
|
||
guint printer_type;
|
||
gboolean got_printer_state;
|
||
guint printer_state;
|
||
gchar *type;
|
||
gchar *domain;
|
||
GtkPrintBackendCups *backend;
|
||
} AvahiConnectionTestData;
|
||
|
||
/*
|
||
* Create new GtkPrinter from informations included in TXT records.
|
||
*/
|
||
static void
|
||
create_cups_printer_from_avahi_data (AvahiConnectionTestData *data)
|
||
{
|
||
PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
|
||
GtkPrinter *printer;
|
||
|
||
info->avahi_printer = TRUE;
|
||
info->printer_name = data->printer_name;
|
||
info->printer_uri = data->printer_uri;
|
||
|
||
if (data->got_printer_state)
|
||
{
|
||
info->state = data->printer_state;
|
||
info->is_paused = info->state == IPP_PRINTER_STOPPED;
|
||
}
|
||
|
||
info->got_printer_type = data->got_printer_type;
|
||
if (data->got_printer_type)
|
||
{
|
||
if (data->printer_type & CUPS_PRINTER_DEFAULT)
|
||
info->default_printer = TRUE;
|
||
else
|
||
info->default_printer = FALSE;
|
||
|
||
if (data->printer_type & CUPS_PRINTER_REMOTE)
|
||
info->remote_printer = TRUE;
|
||
else
|
||
info->remote_printer = FALSE;
|
||
|
||
if (data->printer_type & CUPS_PRINTER_REJECTING)
|
||
info->is_accepting_jobs = FALSE;
|
||
else
|
||
info->is_accepting_jobs = TRUE;
|
||
|
||
if (info->default_printer &&
|
||
data->backend->avahi_default_printer == NULL)
|
||
data->backend->avahi_default_printer = g_strdup (info->printer_name);
|
||
}
|
||
|
||
set_info_state_message (info);
|
||
|
||
printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (data->backend), data->printer_name);
|
||
if (printer == NULL)
|
||
{
|
||
printer = cups_create_printer (data->backend, info);
|
||
|
||
if (data->got_printer_type)
|
||
{
|
||
gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
|
||
GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
|
||
|
||
if (info->default_printer &&
|
||
data->backend->avahi_default_printer == NULL)
|
||
data->backend->avahi_default_printer = g_strdup (info->printer_name);
|
||
}
|
||
|
||
if (data->got_printer_state)
|
||
GTK_PRINTER_CUPS (printer)->state = info->state;
|
||
|
||
GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (data->name);
|
||
GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (data->type);
|
||
GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (data->domain);
|
||
g_free (GTK_PRINTER_CUPS (printer)->hostname);
|
||
GTK_PRINTER_CUPS (printer)->hostname = g_strdup (data->host);
|
||
GTK_PRINTER_CUPS (printer)->port = data->port;
|
||
gtk_printer_set_location (printer, data->location);
|
||
gtk_printer_set_state_message (printer, info->state_msg);
|
||
|
||
set_printer_icon_name_from_info (printer, info);
|
||
|
||
if (!gtk_printer_is_active (printer))
|
||
gtk_printer_set_is_active (printer, TRUE);
|
||
|
||
g_signal_emit_by_name (data->backend, "printer-added", printer);
|
||
gtk_printer_set_is_new (printer, FALSE);
|
||
g_signal_emit_by_name (data->backend, "printer-list-changed");
|
||
|
||
if (!data->backend->got_default_printer &&
|
||
gtk_print_backend_printer_list_is_done (GTK_PRINT_BACKEND (data->backend)) &&
|
||
data->backend->avahi_default_printer != NULL)
|
||
set_default_printer (data->backend, data->backend->avahi_default_printer);
|
||
|
||
/* The ref is held by GtkPrintBackend, in add_printer() */
|
||
g_object_unref (printer);
|
||
}
|
||
|
||
printer_setup_info_free (info);
|
||
}
|
||
|
||
static void
|
||
avahi_connection_test_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
|
||
GSocketConnection *connection;
|
||
|
||
connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
|
||
res,
|
||
NULL);
|
||
g_object_unref (source_object);
|
||
|
||
if (connection != NULL)
|
||
{
|
||
g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
|
||
g_object_unref (connection);
|
||
|
||
create_cups_printer_from_avahi_data (data);
|
||
}
|
||
|
||
g_free (data->printer_uri);
|
||
g_free (data->location);
|
||
g_free (data->host);
|
||
g_free (data->printer_name);
|
||
g_free (data->name);
|
||
g_free (data->type);
|
||
g_free (data->domain);
|
||
g_free (data);
|
||
}
|
||
|
||
gboolean
|
||
avahi_txt_get_key_value_pair (const gchar *entry,
|
||
gchar **key,
|
||
gchar **value)
|
||
{
|
||
const gchar *equal_sign;
|
||
|
||
*key = NULL;
|
||
*value = NULL;
|
||
|
||
if (entry != NULL)
|
||
{
|
||
/* See RFC 6763 section 6.3 */
|
||
equal_sign = strstr (entry, "=");
|
||
|
||
if (equal_sign != NULL)
|
||
{
|
||
*key = g_strndup (entry, equal_sign - entry);
|
||
*value = g_strdup (equal_sign + 1);
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
avahi_service_resolver_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
AvahiConnectionTestData *data;
|
||
GtkPrintBackendCups *backend;
|
||
const gchar *name;
|
||
const gchar *host;
|
||
const gchar *type;
|
||
const gchar *domain;
|
||
const gchar *address;
|
||
const gchar *protocol_string;
|
||
GVariant *output;
|
||
GVariant *txt;
|
||
GVariant *child;
|
||
guint32 flags;
|
||
guint16 port;
|
||
GError *error = NULL;
|
||
gchar *queue_name = NULL;
|
||
gchar *tmp;
|
||
gchar *printer_name;
|
||
gchar *endptr;
|
||
gchar *key;
|
||
gchar *value;
|
||
gsize length;
|
||
gint interface;
|
||
gint protocol;
|
||
gint aprotocol;
|
||
gint i;
|
||
|
||
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
|
||
res,
|
||
&error);
|
||
if (output)
|
||
{
|
||
backend = GTK_PRINT_BACKEND_CUPS (user_data);
|
||
|
||
g_variant_get (output, "(ii&s&s&s&si&sq@aayu)",
|
||
&interface,
|
||
&protocol,
|
||
&name,
|
||
&type,
|
||
&domain,
|
||
&host,
|
||
&aprotocol,
|
||
&address,
|
||
&port,
|
||
&txt,
|
||
&flags);
|
||
|
||
data = g_new0 (AvahiConnectionTestData, 1);
|
||
|
||
for (i = 0; i < g_variant_n_children (txt); i++)
|
||
{
|
||
child = g_variant_get_child_value (txt, i);
|
||
|
||
length = g_variant_get_size (child);
|
||
if (length > 0)
|
||
{
|
||
tmp = g_strndup (g_variant_get_data (child), length);
|
||
g_variant_unref (child);
|
||
|
||
if (!avahi_txt_get_key_value_pair (tmp, &key, &value))
|
||
{
|
||
g_free (tmp);
|
||
continue;
|
||
}
|
||
|
||
if (g_strcmp0 (key, "rp") == 0)
|
||
{
|
||
queue_name = g_strdup (value);
|
||
|
||
printer_name = g_strrstr (queue_name, "/");
|
||
if (printer_name != NULL)
|
||
data->printer_name = g_strdup (printer_name + 1);
|
||
else
|
||
data->printer_name = g_strdup (queue_name);
|
||
}
|
||
else if (g_strcmp0 (key, "note") == 0)
|
||
{
|
||
data->location = g_strdup (value);
|
||
}
|
||
else if (g_strcmp0 (key, "printer-type") == 0)
|
||
{
|
||
endptr = NULL;
|
||
data->printer_type = g_ascii_strtoull (value, &endptr, 16);
|
||
if (data->printer_type != 0 || endptr != value)
|
||
data->got_printer_type = TRUE;
|
||
}
|
||
else if (g_strcmp0 (key, "printer-state") == 0)
|
||
{
|
||
endptr = NULL;
|
||
data->printer_state = g_ascii_strtoull (value, &endptr, 10);
|
||
if (data->printer_state != 0 || endptr != value)
|
||
data->got_printer_state = TRUE;
|
||
}
|
||
|
||
g_clear_pointer (&key, g_free);
|
||
g_clear_pointer (&value, g_free);
|
||
g_free (tmp);
|
||
}
|
||
else
|
||
{
|
||
g_variant_unref (child);
|
||
}
|
||
}
|
||
|
||
if (queue_name)
|
||
{
|
||
if (g_strcmp0 (type, "_ipp._tcp") == 0)
|
||
protocol_string = "ipp";
|
||
else
|
||
protocol_string = "ipps";
|
||
|
||
if (aprotocol == AVAHI_PROTO_INET6)
|
||
data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, queue_name);
|
||
else
|
||
data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, queue_name);
|
||
|
||
data->host = g_strdup (address);
|
||
data->port = port;
|
||
|
||
data->name = g_strdup (name);
|
||
data->type = g_strdup (type);
|
||
data->domain = g_strdup (domain);
|
||
data->backend = backend;
|
||
|
||
/* It can happen that the address is not reachable */
|
||
g_socket_client_connect_to_host_async (g_socket_client_new (),
|
||
address,
|
||
port,
|
||
backend->avahi_cancellable,
|
||
avahi_connection_test_cb,
|
||
data);
|
||
g_free (queue_name);
|
||
}
|
||
else
|
||
{
|
||
g_free (data->printer_name);
|
||
g_free (data->location);
|
||
g_free (data);
|
||
}
|
||
|
||
g_variant_unref (txt);
|
||
g_variant_unref (output);
|
||
}
|
||
else
|
||
{
|
||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
g_warning ("%s", error->message);
|
||
g_error_free (error);
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
|
||
gchar *name;
|
||
gchar *type;
|
||
gchar *domain;
|
||
guint flags;
|
||
gint interface;
|
||
gint protocol;
|
||
|
||
if (g_strcmp0 (signal_name, "ItemNew") == 0)
|
||
{
|
||
g_variant_get (parameters, "(ii&s&s&su)",
|
||
&interface,
|
||
&protocol,
|
||
&name,
|
||
&type,
|
||
&domain,
|
||
&flags);
|
||
|
||
if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
|
||
g_strcmp0 (type, "_ipps._tcp") == 0)
|
||
{
|
||
g_dbus_connection_call (backend->dbus_connection,
|
||
AVAHI_BUS,
|
||
"/",
|
||
AVAHI_SERVER_IFACE,
|
||
"ResolveService",
|
||
g_variant_new ("(iisssiu)",
|
||
interface,
|
||
protocol,
|
||
name,
|
||
type,
|
||
domain,
|
||
AVAHI_PROTO_UNSPEC,
|
||
0),
|
||
G_VARIANT_TYPE ("(iissssisqaayu)"),
|
||
G_DBUS_CALL_FLAGS_NONE,
|
||
-1,
|
||
backend->avahi_cancellable,
|
||
avahi_service_resolver_cb,
|
||
user_data);
|
||
}
|
||
}
|
||
else if (g_strcmp0 (signal_name, "ItemRemove") == 0)
|
||
{
|
||
g_variant_get (parameters, "(ii&s&s&su)",
|
||
&interface,
|
||
&protocol,
|
||
&name,
|
||
&type,
|
||
&domain,
|
||
&flags);
|
||
|
||
if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
|
||
g_strcmp0 (type, "_ipps._tcp") == 0)
|
||
{
|
||
GtkPrinterCups *printer;
|
||
GList *list;
|
||
GList *iter;
|
||
|
||
list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
|
||
for (iter = list; iter; iter = iter->next)
|
||
{
|
||
printer = GTK_PRINTER_CUPS (iter->data);
|
||
if (g_strcmp0 (printer->avahi_name, name) == 0 &&
|
||
g_strcmp0 (printer->avahi_type, type) == 0 &&
|
||
g_strcmp0 (printer->avahi_domain, domain) == 0)
|
||
{
|
||
if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
|
||
backend->avahi_default_printer) == 0)
|
||
g_clear_pointer (&backend->avahi_default_printer, g_free);
|
||
|
||
g_signal_emit_by_name (backend, "printer-removed", printer);
|
||
gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
|
||
GTK_PRINTER (printer));
|
||
g_signal_emit_by_name (backend, "printer-list-changed");
|
||
break;
|
||
}
|
||
}
|
||
|
||
g_list_free (list);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
avahi_service_browser_new_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPrintBackendCups *cups_backend;
|
||
GVariant *output;
|
||
GError *error = NULL;
|
||
gint i;
|
||
|
||
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
|
||
res,
|
||
&error);
|
||
if (output)
|
||
{
|
||
cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
|
||
i = cups_backend->avahi_service_browser_paths[0] ? 1 : 0;
|
||
|
||
g_variant_get (output, "(o)", &cups_backend->avahi_service_browser_paths[i]);
|
||
|
||
cups_backend->avahi_service_browser_subscription_ids[i] =
|
||
g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
|
||
NULL,
|
||
AVAHI_SERVICE_BROWSER_IFACE,
|
||
NULL,
|
||
cups_backend->avahi_service_browser_paths[i],
|
||
NULL,
|
||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||
avahi_service_browser_signal_handler,
|
||
user_data,
|
||
NULL);
|
||
|
||
/*
|
||
* The general subscription for all service browsers is not needed
|
||
* now because we are already subscribed to service browsers
|
||
* specific to _ipp._tcp and _ipps._tcp services.
|
||
*/
|
||
if (cups_backend->avahi_service_browser_paths[0] &&
|
||
cups_backend->avahi_service_browser_paths[1] &&
|
||
cups_backend->avahi_service_browser_subscription_id > 0)
|
||
{
|
||
g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
|
||
cups_backend->avahi_service_browser_subscription_id);
|
||
cups_backend->avahi_service_browser_subscription_id = 0;
|
||
}
|
||
|
||
g_variant_unref (output);
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The creation of ServiceBrowser fails with G_IO_ERROR_DBUS_ERROR
|
||
* if Avahi is disabled.
|
||
*/
|
||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) &&
|
||
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
g_warning ("%s", error->message);
|
||
g_error_free (error);
|
||
}
|
||
}
|
||
|
||
static void
|
||
avahi_create_browsers (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GDBusConnection *dbus_connection;
|
||
GtkPrintBackendCups *cups_backend;
|
||
GError *error = NULL;
|
||
|
||
dbus_connection = g_bus_get_finish (res, &error);
|
||
if (!dbus_connection)
|
||
{
|
||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
g_warning ("Couldn't connect to D-Bus system bus, %s", error->message);
|
||
|
||
g_error_free (error);
|
||
return;
|
||
}
|
||
|
||
cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
|
||
cups_backend->dbus_connection = dbus_connection;
|
||
|
||
/*
|
||
* We need to subscribe to signals of service browser before
|
||
* we actually create it because it starts to emit them right
|
||
* after its creation.
|
||
*/
|
||
cups_backend->avahi_service_browser_subscription_id =
|
||
g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
|
||
NULL,
|
||
AVAHI_SERVICE_BROWSER_IFACE,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||
avahi_service_browser_signal_handler,
|
||
cups_backend,
|
||
NULL);
|
||
|
||
/*
|
||
* Create service browsers for _ipp._tcp and _ipps._tcp services.
|
||
*/
|
||
g_dbus_connection_call (cups_backend->dbus_connection,
|
||
AVAHI_BUS,
|
||
"/",
|
||
AVAHI_SERVER_IFACE,
|
||
"ServiceBrowserNew",
|
||
g_variant_new ("(iissu)",
|
||
AVAHI_IF_UNSPEC,
|
||
AVAHI_PROTO_UNSPEC,
|
||
"_ipp._tcp",
|
||
"",
|
||
0),
|
||
G_VARIANT_TYPE ("(o)"),
|
||
G_DBUS_CALL_FLAGS_NONE,
|
||
-1,
|
||
cups_backend->avahi_cancellable,
|
||
avahi_service_browser_new_cb,
|
||
cups_backend);
|
||
|
||
g_dbus_connection_call (cups_backend->dbus_connection,
|
||
AVAHI_BUS,
|
||
"/",
|
||
AVAHI_SERVER_IFACE,
|
||
"ServiceBrowserNew",
|
||
g_variant_new ("(iissu)",
|
||
AVAHI_IF_UNSPEC,
|
||
AVAHI_PROTO_UNSPEC,
|
||
"_ipps._tcp",
|
||
"",
|
||
0),
|
||
G_VARIANT_TYPE ("(o)"),
|
||
G_DBUS_CALL_FLAGS_NONE,
|
||
-1,
|
||
cups_backend->avahi_cancellable,
|
||
avahi_service_browser_new_cb,
|
||
cups_backend);
|
||
}
|
||
|
||
static void
|
||
avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
|
||
{
|
||
cups_backend->avahi_cancellable = g_cancellable_new ();
|
||
g_bus_get (G_BUS_TYPE_SYSTEM, cups_backend->avahi_cancellable, avahi_create_browsers, cups_backend);
|
||
}
|
||
#endif
|
||
|
||
static void
|
||
cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
|
||
GtkCupsResult *result,
|
||
gpointer user_data)
|
||
{
|
||
GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
|
||
ipp_attribute_t *attr;
|
||
ipp_t *response;
|
||
gboolean list_has_changed;
|
||
GList *removed_printer_checklist;
|
||
gchar *remote_default_printer = NULL;
|
||
GList *iter;
|
||
|
||
gdk_threads_enter ();
|
||
|
||
list_has_changed = FALSE;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
cups_backend->list_printers_pending = FALSE;
|
||
|
||
if (gtk_cups_result_is_error (result))
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
|
||
gtk_cups_result_get_error_string (result),
|
||
gtk_cups_result_get_error_type (result),
|
||
gtk_cups_result_get_error_code (result)));
|
||
|
||
if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
|
||
gtk_cups_result_get_error_code (result) == 1)
|
||
{
|
||
/* Canceled by user, stop popping up more password dialogs */
|
||
if (cups_backend->list_printers_poll > 0)
|
||
g_source_remove (cups_backend->list_printers_poll);
|
||
cups_backend->list_printers_poll = 0;
|
||
cups_backend->list_printers_attempts = 0;
|
||
}
|
||
|
||
goto done;
|
||
}
|
||
|
||
/* Gather the names of the printers in the current queue
|
||
* so we may check to see if they were removed
|
||
*/
|
||
removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
|
||
|
||
response = gtk_cups_result_get_response (result);
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
for (attr = ippFirstAttribute (response); attr != NULL;
|
||
attr = ippNextAttribute (response))
|
||
{
|
||
GtkPrinter *printer;
|
||
gboolean status_changed = FALSE;
|
||
GList *node;
|
||
PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
|
||
|
||
/* Skip leading attributes until we hit a printer...
|
||
*/
|
||
while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
|
||
attr = ippNextAttribute (response);
|
||
|
||
if (attr == NULL)
|
||
break;
|
||
while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
|
||
{
|
||
cups_printer_handle_attribute (cups_backend, attr, info);
|
||
attr = ippNextAttribute (response);
|
||
}
|
||
#else
|
||
for (attr = response->attrs; attr != NULL; attr = attr->next)
|
||
{
|
||
GtkPrinter *printer;
|
||
gboolean status_changed = FALSE;
|
||
GList *node;
|
||
PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
|
||
info->default_number_up = 1;
|
||
|
||
/* Skip leading attributes until we hit a printer...
|
||
*/
|
||
while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
|
||
attr = attr->next;
|
||
|
||
if (attr == NULL)
|
||
break;
|
||
while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
|
||
{
|
||
cups_printer_handle_attribute (cups_backend, attr, info);
|
||
attr = attr->next;
|
||
}
|
||
#endif
|
||
|
||
if (info->printer_name == NULL ||
|
||
(info->printer_uri == NULL && info->member_uris == NULL))
|
||
{
|
||
if (attr == NULL)
|
||
break;
|
||
else
|
||
continue;
|
||
}
|
||
|
||
if (info->got_printer_type)
|
||
{
|
||
if (info->default_printer && !cups_backend->got_default_printer)
|
||
{
|
||
if (!info->remote_printer)
|
||
{
|
||
cups_backend->got_default_printer = TRUE;
|
||
cups_backend->default_printer = g_strdup (info->printer_name);
|
||
}
|
||
else
|
||
{
|
||
if (remote_default_printer == NULL)
|
||
remote_default_printer = g_strdup (info->printer_name);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!cups_backend->got_default_printer)
|
||
cups_get_default_printer (cups_backend);
|
||
}
|
||
|
||
/* remove name from checklist if it was found */
|
||
node = g_list_find_custom (removed_printer_checklist,
|
||
info->printer_name,
|
||
(GCompareFunc) find_printer);
|
||
removed_printer_checklist = g_list_delete_link (removed_printer_checklist,
|
||
node);
|
||
|
||
printer = gtk_print_backend_find_printer (backend, info->printer_name);
|
||
if (!printer)
|
||
{
|
||
printer = cups_create_printer (cups_backend, info);
|
||
list_has_changed = TRUE;
|
||
}
|
||
|
||
else
|
||
g_object_ref (printer);
|
||
|
||
GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
|
||
|
||
gtk_printer_set_is_paused (printer, info->is_paused);
|
||
gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
|
||
|
||
if (!gtk_printer_is_active (printer))
|
||
{
|
||
gtk_printer_set_is_active (printer, TRUE);
|
||
gtk_printer_set_is_new (printer, TRUE);
|
||
list_has_changed = TRUE;
|
||
}
|
||
|
||
if (gtk_printer_is_new (printer))
|
||
{
|
||
g_signal_emit_by_name (backend, "printer-added", printer);
|
||
|
||
gtk_printer_set_is_new (printer, FALSE);
|
||
}
|
||
|
||
GTK_PRINTER_CUPS (printer)->state = info->state;
|
||
GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
|
||
GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
|
||
GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
|
||
GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
|
||
GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
|
||
GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
|
||
GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
|
||
status_changed = gtk_printer_set_job_count (printer, info->job_count);
|
||
status_changed |= gtk_printer_set_location (printer, info->location);
|
||
status_changed |= gtk_printer_set_description (printer,
|
||
info->description);
|
||
|
||
set_info_state_message (info);
|
||
|
||
status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
|
||
status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
|
||
|
||
set_printer_icon_name_from_info (printer, info);
|
||
|
||
if (status_changed)
|
||
g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
|
||
"printer-status-changed", printer);
|
||
|
||
/* The ref is held by GtkPrintBackend, in add_printer() */
|
||
g_object_unref (printer);
|
||
printer_setup_info_free (info);
|
||
|
||
if (attr == NULL)
|
||
break;
|
||
}
|
||
|
||
/* look at the removed printers checklist and mark any printer
|
||
as inactive if it is in the list, emitting a printer_removed signal */
|
||
if (removed_printer_checklist != NULL)
|
||
{
|
||
for (iter = removed_printer_checklist; iter; iter = iter->next)
|
||
{
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
|
||
#endif
|
||
{
|
||
mark_printer_inactive (GTK_PRINTER (iter->data), backend);
|
||
list_has_changed = TRUE;
|
||
}
|
||
}
|
||
|
||
g_list_free (removed_printer_checklist);
|
||
}
|
||
|
||
done:
|
||
if (list_has_changed)
|
||
g_signal_emit_by_name (backend, "printer-list-changed");
|
||
|
||
gtk_print_backend_set_list_done (backend);
|
||
|
||
if (!cups_backend->got_default_printer && remote_default_printer != NULL)
|
||
{
|
||
set_default_printer (cups_backend, remote_default_printer);
|
||
g_free (remote_default_printer);
|
||
}
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
|
||
{
|
||
set_default_printer (cups_backend, cups_backend->avahi_default_printer);
|
||
}
|
||
#endif
|
||
|
||
gdk_threads_leave ();
|
||
}
|
||
|
||
static void
|
||
update_backend_status (GtkPrintBackendCups *cups_backend,
|
||
GtkCupsConnectionState state)
|
||
{
|
||
switch (state)
|
||
{
|
||
case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
|
||
g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
|
||
break;
|
||
case GTK_CUPS_CONNECTION_AVAILABLE:
|
||
g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
|
||
break;
|
||
default: ;
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
cups_request_printer_list (GtkPrintBackendCups *cups_backend)
|
||
{
|
||
GtkCupsConnectionState state;
|
||
GtkCupsRequest *request;
|
||
|
||
if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
|
||
return TRUE;
|
||
|
||
state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
|
||
update_backend_status (cups_backend, state);
|
||
|
||
if (cups_backend->list_printers_attempts == 60)
|
||
{
|
||
cups_backend->list_printers_attempts = -1;
|
||
if (cups_backend->list_printers_poll > 0)
|
||
g_source_remove (cups_backend->list_printers_poll);
|
||
cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
|
||
(GSourceFunc) cups_request_printer_list,
|
||
cups_backend);
|
||
g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk+] cups_request_printer_list");
|
||
}
|
||
else if (cups_backend->list_printers_attempts != -1)
|
||
cups_backend->list_printers_attempts++;
|
||
|
||
if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
|
||
return TRUE;
|
||
else
|
||
if (cups_backend->list_printers_attempts > 0)
|
||
cups_backend->list_printers_attempts = 60;
|
||
|
||
cups_backend->list_printers_pending = TRUE;
|
||
|
||
request = gtk_cups_request_new_with_username (NULL,
|
||
GTK_CUPS_POST,
|
||
CUPS_GET_PRINTERS,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
cups_backend->username);
|
||
|
||
gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
|
||
"requested-attributes", G_N_ELEMENTS (printer_attrs),
|
||
NULL, printer_attrs);
|
||
|
||
cups_request_execute (cups_backend,
|
||
request,
|
||
(GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
|
||
request,
|
||
NULL);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
cups_get_printer_list (GtkPrintBackend *backend)
|
||
{
|
||
GtkPrintBackendCups *cups_backend;
|
||
|
||
cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
|
||
|
||
if (cups_backend->cups_connection_test == NULL)
|
||
cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
|
||
|
||
if (cups_backend->list_printers_poll == 0)
|
||
{
|
||
if (cups_request_printer_list (cups_backend))
|
||
{
|
||
cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
|
||
(GSourceFunc) cups_request_printer_list,
|
||
backend);
|
||
g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk+] cups_request_printer_list");
|
||
}
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
avahi_request_printer_list (cups_backend);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
typedef struct {
|
||
GtkPrinterCups *printer;
|
||
GIOChannel *ppd_io;
|
||
http_t *http;
|
||
} GetPPDData;
|
||
|
||
static void
|
||
get_ppd_data_free (GetPPDData *data)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
httpClose (data->http);
|
||
g_io_channel_unref (data->ppd_io);
|
||
g_object_unref (data->printer);
|
||
g_free (data);
|
||
}
|
||
|
||
static void
|
||
cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
|
||
GtkCupsResult *result,
|
||
GetPPDData *data)
|
||
{
|
||
GtkPrinter *printer;
|
||
struct stat data_info;
|
||
|
||
gdk_threads_enter ();
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
printer = GTK_PRINTER (data->printer);
|
||
GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
|
||
print_backend->reading_ppds--;
|
||
|
||
#ifndef HAVE_CUPS_API_1_6
|
||
if (gtk_cups_result_is_error (result))
|
||
{
|
||
gboolean success = FALSE;
|
||
|
||
/* If we get a 404 then it is just a raw printer without a ppd
|
||
and not an error. */
|
||
if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
|
||
(gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
|
||
{
|
||
gtk_printer_set_has_details (printer, TRUE);
|
||
success = TRUE;
|
||
}
|
||
|
||
g_signal_emit_by_name (printer, "details-acquired", success);
|
||
|
||
goto done;
|
||
}
|
||
#endif
|
||
|
||
if (!gtk_cups_result_is_error (result))
|
||
{
|
||
/* let ppdOpenFd take over the ownership of the open file */
|
||
g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
|
||
data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
|
||
ppdLocalize (data->printer->ppd_file);
|
||
ppdMarkDefaults (data->printer->ppd_file);
|
||
}
|
||
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
fstat (g_io_channel_unix_get_fd (data->ppd_io), &data_info);
|
||
/*
|
||
* Standalone Avahi printers and raw printers don't have PPD files or have
|
||
* empty PPD files. Try to get printer details via IPP.
|
||
* Always do this for Avahi printers.
|
||
*/
|
||
if (data_info.st_size == 0 ||
|
||
GTK_PRINTER_CUPS (printer)->avahi_browsed ||
|
||
(gtk_cups_result_is_error (result) &&
|
||
((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
|
||
(gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))))
|
||
{
|
||
cups_request_printer_info (GTK_PRINTER_CUPS (printer)->printer_uri,
|
||
GTK_PRINTER_CUPS (printer)->hostname,
|
||
GTK_PRINTER_CUPS (printer)->port,
|
||
GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer)));
|
||
|
||
goto done;
|
||
}
|
||
#endif
|
||
|
||
gtk_printer_set_has_details (printer, TRUE);
|
||
g_signal_emit_by_name (printer, "details-acquired", TRUE);
|
||
|
||
done:
|
||
gdk_threads_leave ();
|
||
}
|
||
|
||
static gboolean
|
||
cups_request_ppd (GtkPrinter *printer)
|
||
{
|
||
GError *error;
|
||
GtkPrintBackend *print_backend;
|
||
GtkPrinterCups *cups_printer;
|
||
GtkCupsRequest *request;
|
||
char *ppd_filename = NULL;
|
||
gchar *resource;
|
||
http_t *http;
|
||
GetPPDData *data;
|
||
int fd;
|
||
|
||
cups_printer = GTK_PRINTER_CUPS (printer);
|
||
|
||
error = NULL;
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
||
|
||
if (cups_printer->remote
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
&& !cups_printer->avahi_browsed
|
||
#endif
|
||
)
|
||
{
|
||
GtkCupsConnectionState state;
|
||
|
||
state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
|
||
|
||
if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
|
||
{
|
||
if (cups_printer->get_remote_ppd_attempts == 60)
|
||
{
|
||
cups_printer->get_remote_ppd_attempts = -1;
|
||
if (cups_printer->get_remote_ppd_poll > 0)
|
||
g_source_remove (cups_printer->get_remote_ppd_poll);
|
||
cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
|
||
(GSourceFunc) cups_request_ppd,
|
||
printer);
|
||
g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk+] cups_request_ppd");
|
||
}
|
||
else if (cups_printer->get_remote_ppd_attempts != -1)
|
||
cups_printer->get_remote_ppd_attempts++;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
|
||
cups_printer->remote_cups_connection_test = NULL;
|
||
cups_printer->get_remote_ppd_poll = 0;
|
||
cups_printer->get_remote_ppd_attempts = 0;
|
||
|
||
if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
|
||
{
|
||
g_signal_emit_by_name (printer, "details-acquired", FALSE);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
http = httpConnectEncrypt (cups_printer->hostname,
|
||
cups_printer->port,
|
||
cupsEncryption ());
|
||
|
||
data = g_new0 (GetPPDData, 1);
|
||
|
||
fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
|
||
&ppd_filename,
|
||
&error);
|
||
|
||
#ifdef G_ENABLE_DEBUG
|
||
/* If we are debugging printing don't delete the tmp files */
|
||
if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
|
||
unlink (ppd_filename);
|
||
#else
|
||
unlink (ppd_filename);
|
||
#endif /* G_ENABLE_DEBUG */
|
||
|
||
if (error != NULL)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_warning ("CUPS Backend: Failed to create temp file, %s\n",
|
||
error->message));
|
||
g_error_free (error);
|
||
httpClose (http);
|
||
g_free (ppd_filename);
|
||
g_free (data);
|
||
|
||
g_signal_emit_by_name (printer, "details-acquired", FALSE);
|
||
return FALSE;
|
||
}
|
||
|
||
data->http = http;
|
||
fchmod (fd, S_IRUSR | S_IWUSR);
|
||
data->ppd_io = g_io_channel_unix_new (fd);
|
||
g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
|
||
g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
|
||
|
||
data->printer = g_object_ref (printer);
|
||
|
||
resource = g_strdup_printf ("/printers/%s.ppd",
|
||
gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
|
||
|
||
print_backend = gtk_printer_get_backend (printer);
|
||
|
||
request = gtk_cups_request_new_with_username (data->http,
|
||
GTK_CUPS_GET,
|
||
0,
|
||
data->ppd_io,
|
||
cups_printer->hostname,
|
||
resource,
|
||
GTK_PRINT_BACKEND_CUPS (print_backend)->username);
|
||
|
||
gtk_cups_request_set_ipp_version (request,
|
||
cups_printer->ipp_version_major,
|
||
cups_printer->ipp_version_minor);
|
||
|
||
GTK_NOTE (PRINTING,
|
||
g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
|
||
|
||
|
||
cups_printer->reading_ppd = TRUE;
|
||
GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
|
||
|
||
cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
|
||
request,
|
||
(GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
|
||
data,
|
||
(GDestroyNotify)get_ppd_data_free);
|
||
|
||
g_free (resource);
|
||
g_free (ppd_filename);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/* Ordering matters for default preference */
|
||
static const char *lpoptions_locations[] = {
|
||
"/etc/cups/lpoptions",
|
||
".lpoptions",
|
||
".cups/lpoptions"
|
||
};
|
||
|
||
static void
|
||
cups_parse_user_default_printer (const char *filename,
|
||
char **printer_name)
|
||
{
|
||
FILE *fp;
|
||
char line[1024], *lineptr, *defname = NULL;
|
||
|
||
if ((fp = g_fopen (filename, "r")) == NULL)
|
||
return;
|
||
|
||
while (fgets (line, sizeof (line), fp) != NULL)
|
||
{
|
||
if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
|
||
continue;
|
||
|
||
lineptr = line + 8;
|
||
while (isspace (*lineptr))
|
||
lineptr++;
|
||
|
||
if (!*lineptr)
|
||
continue;
|
||
|
||
defname = lineptr;
|
||
while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
|
||
lineptr++;
|
||
|
||
*lineptr = '\0';
|
||
|
||
g_free (*printer_name);
|
||
|
||
*printer_name = g_strdup (defname);
|
||
}
|
||
|
||
fclose (fp);
|
||
}
|
||
|
||
static void
|
||
cups_get_user_default_printer (char **printer_name)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
|
||
{
|
||
if (g_path_is_absolute (lpoptions_locations[i]))
|
||
{
|
||
cups_parse_user_default_printer (lpoptions_locations[i],
|
||
printer_name);
|
||
}
|
||
else
|
||
{
|
||
char *filename;
|
||
|
||
filename = g_build_filename (g_get_home_dir (),
|
||
lpoptions_locations[i], NULL);
|
||
cups_parse_user_default_printer (filename, printer_name);
|
||
g_free (filename);
|
||
}
|
||
}
|
||
}
|
||
|
||
static int
|
||
cups_parse_user_options (const char *filename,
|
||
const char *printer_name,
|
||
int num_options,
|
||
cups_option_t **options)
|
||
{
|
||
FILE *fp;
|
||
gchar line[1024], *lineptr, *name;
|
||
|
||
if ((fp = g_fopen (filename, "r")) == NULL)
|
||
return num_options;
|
||
|
||
while (fgets (line, sizeof (line), fp) != NULL)
|
||
{
|
||
if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
|
||
lineptr = line + 4;
|
||
else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
|
||
lineptr = line + 7;
|
||
else
|
||
continue;
|
||
|
||
/* Skip leading whitespace */
|
||
while (isspace (*lineptr))
|
||
lineptr++;
|
||
|
||
if (!*lineptr)
|
||
continue;
|
||
|
||
name = lineptr;
|
||
while (!isspace (*lineptr) && *lineptr)
|
||
{
|
||
lineptr++;
|
||
}
|
||
|
||
if (!*lineptr)
|
||
continue;
|
||
|
||
*lineptr++ = '\0';
|
||
|
||
if (strcasecmp (name, printer_name) != 0)
|
||
continue;
|
||
|
||
/* We found our printer, parse the options */
|
||
num_options = cupsParseOptions (lineptr, num_options, options);
|
||
}
|
||
|
||
fclose (fp);
|
||
|
||
return num_options;
|
||
}
|
||
|
||
static int
|
||
cups_get_user_options (const char *printer_name,
|
||
int num_options,
|
||
cups_option_t **options)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
|
||
{
|
||
if (g_path_is_absolute (lpoptions_locations[i]))
|
||
{
|
||
num_options = cups_parse_user_options (lpoptions_locations[i],
|
||
printer_name,
|
||
num_options,
|
||
options);
|
||
}
|
||
else
|
||
{
|
||
char *filename;
|
||
|
||
filename = g_build_filename (g_get_home_dir (),
|
||
lpoptions_locations[i], NULL);
|
||
num_options = cups_parse_user_options (filename, printer_name,
|
||
num_options, options);
|
||
g_free (filename);
|
||
}
|
||
}
|
||
|
||
return num_options;
|
||
}
|
||
|
||
/* This function requests default printer from a CUPS server in regular intervals.
|
||
* In the case of unreachable CUPS server the request is repeated later.
|
||
* The default printer is not requested in the case of previous success.
|
||
*/
|
||
static void
|
||
cups_get_default_printer (GtkPrintBackendCups *backend)
|
||
{
|
||
GtkPrintBackendCups *cups_backend;
|
||
|
||
cups_backend = backend;
|
||
|
||
if (cups_backend->cups_connection_test == NULL)
|
||
cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
|
||
|
||
if (cups_backend->default_printer_poll == 0)
|
||
{
|
||
if (cups_request_default_printer (cups_backend))
|
||
{
|
||
cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
|
||
(GSourceFunc) cups_request_default_printer,
|
||
backend);
|
||
g_source_set_name_by_id (cups_backend->default_printer_poll, "[gtk+] cups_request_default_printer");
|
||
}
|
||
}
|
||
}
|
||
|
||
/* This function gets default printer from local settings.*/
|
||
static void
|
||
cups_get_local_default_printer (GtkPrintBackendCups *backend)
|
||
{
|
||
const char *str;
|
||
char *name = NULL;
|
||
|
||
if ((str = g_getenv ("LPDEST")) != NULL)
|
||
{
|
||
backend->default_printer = g_strdup (str);
|
||
backend->got_default_printer = TRUE;
|
||
return;
|
||
}
|
||
else if ((str = g_getenv ("PRINTER")) != NULL &&
|
||
strcmp (str, "lp") != 0)
|
||
{
|
||
backend->default_printer = g_strdup (str);
|
||
backend->got_default_printer = TRUE;
|
||
return;
|
||
}
|
||
|
||
/* Figure out user setting for default printer */
|
||
cups_get_user_default_printer (&name);
|
||
if (name != NULL)
|
||
{
|
||
backend->default_printer = name;
|
||
backend->got_default_printer = TRUE;
|
||
return;
|
||
}
|
||
}
|
||
|
||
static void
|
||
cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
|
||
GtkCupsResult *result,
|
||
gpointer user_data)
|
||
{
|
||
ipp_t *response;
|
||
ipp_attribute_t *attr;
|
||
GtkPrinter *printer;
|
||
|
||
gdk_threads_enter ();
|
||
|
||
if (gtk_cups_result_is_error (result))
|
||
{
|
||
if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
|
||
gtk_cups_result_get_error_code (result) == 1)
|
||
{
|
||
/* Canceled by user, stop popping up more password dialogs */
|
||
if (print_backend->list_printers_poll > 0)
|
||
g_source_remove (print_backend->list_printers_poll);
|
||
print_backend->list_printers_poll = 0;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
response = gtk_cups_result_get_response (result);
|
||
|
||
if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
|
||
print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
|
||
|
||
print_backend->got_default_printer = TRUE;
|
||
|
||
if (print_backend->default_printer != NULL)
|
||
{
|
||
printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
|
||
if (printer != NULL)
|
||
{
|
||
gtk_printer_set_is_default (printer, TRUE);
|
||
g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
|
||
}
|
||
}
|
||
|
||
/* Make sure to kick off get_printers if we are polling it,
|
||
* as we could have blocked this reading the default printer
|
||
*/
|
||
if (print_backend->list_printers_poll != 0)
|
||
cups_request_printer_list (print_backend);
|
||
|
||
gdk_threads_leave ();
|
||
}
|
||
|
||
static gboolean
|
||
cups_request_default_printer (GtkPrintBackendCups *print_backend)
|
||
{
|
||
GtkCupsConnectionState state;
|
||
GtkCupsRequest *request;
|
||
|
||
state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
|
||
update_backend_status (print_backend, state);
|
||
|
||
if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
|
||
return TRUE;
|
||
|
||
request = gtk_cups_request_new_with_username (NULL,
|
||
GTK_CUPS_POST,
|
||
CUPS_GET_DEFAULT,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
print_backend->username);
|
||
|
||
cups_request_execute (print_backend,
|
||
request,
|
||
(GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
|
||
g_object_ref (print_backend),
|
||
g_object_unref);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
cups_printer_request_details (GtkPrinter *printer)
|
||
{
|
||
GtkPrinterCups *cups_printer;
|
||
|
||
cups_printer = GTK_PRINTER_CUPS (printer);
|
||
if (!cups_printer->reading_ppd &&
|
||
gtk_printer_cups_get_ppd (cups_printer) == NULL)
|
||
{
|
||
if (cups_printer->remote
|
||
#ifdef HAVE_CUPS_API_1_6
|
||
&& !cups_printer->avahi_browsed
|
||
#endif
|
||
)
|
||
{
|
||
if (cups_printer->get_remote_ppd_poll == 0)
|
||
{
|
||
cups_printer->remote_cups_connection_test =
|
||
gtk_cups_connection_test_new (cups_printer->hostname,
|
||
cups_printer->port);
|
||
|
||
if (cups_request_ppd (printer))
|
||
{
|
||
cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
|
||
(GSourceFunc) cups_request_ppd,
|
||
printer);
|
||
g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk+] cups_request_ppd");
|
||
}
|
||
}
|
||
}
|
||
else
|
||
cups_request_ppd (printer);
|
||
}
|
||
}
|
||
|
||
static char *
|
||
ppd_text_to_utf8 (ppd_file_t *ppd_file,
|
||
const char *text)
|
||
{
|
||
const char *encoding = NULL;
|
||
char *res;
|
||
|
||
if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
|
||
{
|
||
return g_strdup (text);
|
||
}
|
||
else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
|
||
{
|
||
encoding = "ISO-8859-1";
|
||
}
|
||
else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
|
||
{
|
||
encoding = "ISO-8859-2";
|
||
}
|
||
else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
|
||
{
|
||
encoding = "ISO-8859-5";
|
||
}
|
||
else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
|
||
{
|
||
encoding = "SHIFT-JIS";
|
||
}
|
||
else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
|
||
{
|
||
encoding = "MACINTOSH";
|
||
}
|
||
else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
|
||
{
|
||
encoding = "WINDOWS-1252";
|
||
}
|
||
else
|
||
{
|
||
/* Fallback, try iso-8859-1... */
|
||
encoding = "ISO-8859-1";
|
||
}
|
||
|
||
res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
|
||
|
||
if (res == NULL)
|
||
{
|
||
GTK_NOTE (PRINTING,
|
||
g_warning ("CUPS Backend: Unable to convert PPD text\n"));
|
||
res = g_strdup ("???");
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
/* TODO: Add more translations for common settings here */
|
||
|
||
static const struct {
|
||
const char *keyword;
|
||
const char *translation;
|
||
} cups_option_translations[] = {
|
||
{ "Duplex", NC_("printing option", "Two Sided") },
|
||
{ "MediaType", NC_("printing option", "Paper Type") },
|
||
{ "InputSlot", NC_("printing option", "Paper Source") },
|
||
{ "OutputBin", NC_("printing option", "Output Tray") },
|
||
{ "Resolution", NC_("printing option", "Resolution") },
|
||
{ "PreFilter", NC_("printing option", "GhostScript pre-filtering") }
|
||
};
|
||
|
||
|
||
static const struct {
|
||
const char *keyword;
|
||
const char *choice;
|
||
const char *translation;
|
||
} cups_choice_translations[] = {
|
||
{ "Duplex", "None", NC_("printing option value", "One Sided") },
|
||
/* Translators: this is an option of "Two Sided" */
|
||
{ "Duplex", "DuplexNoTumble", NC_("printing option value", "Long Edge (Standard)") },
|
||
/* Translators: this is an option of "Two Sided" */
|
||
{ "Duplex", "DuplexTumble", NC_("printing option value", "Short Edge (Flip)") },
|
||
/* Translators: this is an option of "Paper Source" */
|
||
{ "InputSlot", "Auto", NC_("printing option value", "Auto Select") },
|
||
/* Translators: this is an option of "Paper Source" */
|
||
{ "InputSlot", "AutoSelect", NC_("printing option value", "Auto Select") },
|
||
/* Translators: this is an option of "Paper Source" */
|
||
{ "InputSlot", "Default", NC_("printing option value", "Printer Default") },
|
||
/* Translators: this is an option of "Paper Source" */
|
||
{ "InputSlot", "None", NC_("printing option value", "Printer Default") },
|
||
/* Translators: this is an option of "Paper Source" */
|
||
{ "InputSlot", "PrinterDefault", NC_("printing option value", "Printer Default") },
|
||
/* Translators: this is an option of "Paper Source" */
|
||
{ "InputSlot", "Unspecified", NC_("printing option value", "Auto Select") },
|
||
/* Translators: this is an option of "Resolution" */
|
||
{ "Resolution", "default", NC_("printing option value", "Printer Default") },
|
||
/* Translators: this is an option of "GhostScript" */
|
||
{ "PreFilter", "EmbedFonts", NC_("printing option value", "Embed GhostScript fonts only") },
|
||
/* Translators: this is an option of "GhostScript" */
|
||
{ "PreFilter", "Level1", NC_("printing option value", "Convert to PS level 1") },
|
||
/* Translators: this is an option of "GhostScript" */
|
||
{ "PreFilter", "Level2", NC_("printing option value", "Convert to PS level 2") },
|
||
/* Translators: this is an option of "GhostScript" */
|
||
{ "PreFilter", "No", NC_("printing option value", "No pre-filtering") }
|
||
};
|
||
|
||
static const struct {
|
||
const char *name;
|
||
const char *translation;
|
||
} cups_group_translations[] = {
|
||
/* Translators: "Miscellaneous" is the label for a button, that opens
|
||
up an extra panel of settings in a print dialog. */
|
||
{ "Miscellaneous", NC_("printing option group", "Miscellaneous") }
|
||
};
|
||
|
||
static const struct {
|
||
const char *ppd_keyword;
|
||
const char *name;
|
||
} ppd_option_names[] = {
|
||
{ "Duplex", "gtk-duplex" },
|
||
{ "MediaType", "gtk-paper-type" },
|
||
{ "InputSlot", "gtk-paper-source" },
|
||
{ "OutputBin", "gtk-output-tray" }
|
||
};
|
||
|
||
static const struct {
|
||
const char *ipp_option_name;
|
||
const char *gtk_option_name;
|
||
const char *translation;
|
||
} ipp_option_translations[] = {
|
||
{ "sides", "gtk-duplex", NC_("printing option", "Two Sided") },
|
||
{ "output-bin", "gtk-output-tray", NC_("printing option", "Output Tray") }
|
||
};
|
||
|
||
static const struct {
|
||
const char *ipp_option_name;
|
||
const char *ipp_choice;
|
||
const char *translation;
|
||
} ipp_choice_translations[] = {
|
||
{ "sides", "one-sided", NC_("sides", "One Sided") },
|
||
/* Translators: this is an option of "Two Sided" */
|
||
{ "sides", "two-sided-long-edge", NC_("sides", "Long Edge (Standard)") },
|
||
/* Translators: this is an option of "Two Sided" */
|
||
{ "sides", "two-sided-short-edge", NC_("sides", "Short Edge (Flip)") },
|
||
|
||
/* Translators: Top output bin */
|
||
{ "output-bin", "top", NC_("output-bin", "Top Bin") },
|
||
/* Translators: Middle output bin */
|
||
{ "output-bin", "middle", NC_("output-bin", "Middle Bin") },
|
||
/* Translators: Bottom output bin */
|
||
{ "output-bin", "bottom", NC_("output-bin", "Bottom Bin") },
|
||
/* Translators: Side output bin */
|
||
{ "output-bin", "side", NC_("output-bin", "Side Bin") },
|
||
/* Translators: Left output bin */
|
||
{ "output-bin", "left", NC_("output-bin", "Left Bin") },
|
||
/* Translators: Right output bin */
|
||
{ "output-bin", "right", NC_("output-bin", "Right Bin") },
|
||
/* Translators: Center output bin */
|
||
{ "output-bin", "center", NC_("output-bin", "Center Bin") },
|
||
/* Translators: Rear output bin */
|
||
{ "output-bin", "rear", NC_("output-bin", "Rear Bin") },
|
||
/* Translators: Output bin where one sided output is oriented in the face-up position */
|
||
{ "output-bin", "face-up", NC_("output-bin", "Face Up Bin") },
|
||
/* Translators: Output bin where one sided output is oriented in the face-down position */
|
||
{ "output-bin", "face-down", NC_("output-bin", "Face Down Bin") },
|
||
/* Translators: Large capacity output bin */
|
||
{ "output-bin", "large-capacity", NC_("output-bin", "Large Capacity Bin") },
|
||
{ NULL, NULL, NULL }
|
||
};
|
||
|
||
/*
|
||
* Handles "format not a string literal" error
|
||
* https://mail.gnome.org/archives/desktop-devel-list/2016-March/msg00075.html
|
||
*/
|
||
static gchar *
|
||
get_ipp_choice_translation_string (gint index,
|
||
guint i)
|
||
{
|
||
gchar *translation;
|
||
|
||
if (i < G_N_ELEMENTS (ipp_choice_translations))
|
||
translation = g_strdup (_(ipp_choice_translations[i].translation));
|
||
else
|
||
{
|
||
switch (i)
|
||
{
|
||
case 14:
|
||
/* Translators: Output stacker number %d */
|
||
translation = g_strdup_printf (C_("output-bin", "Stacker %d"), index);
|
||
break;
|
||
case 15:
|
||
/* Translators: Output mailbox number %d */
|
||
translation = g_strdup_printf (C_("output-bin", "Mailbox %d"), index);
|
||
break;
|
||
case 16:
|
||
/* Translators: Private mailbox */
|
||
translation = g_strdup (C_("output-bin", "My Mailbox"));
|
||
break;
|
||
case 17:
|
||
/* Translators: Output tray number %d */
|
||
translation = g_strdup_printf (C_("output-bin", "Tray %d"), index);
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
return translation;
|
||
}
|
||
|
||
static const struct {
|
||
const char *lpoption;
|
||
const char *name;
|
||
} lpoption_names[] = {
|
||
{ "number-up", "gtk-n-up" },
|
||
{ "number-up-layout", "gtk-n-up-layout" },
|
||
{ "job-billing", "gtk-billing-info" },
|
||
{ "job-priority", "gtk-job-prio" }
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char *color_option_whitelist[] = {
|
||
"BRColorEnhancement",
|
||
"BRColorMatching",
|
||
"BRColorMatching",
|
||
"BRColorMode",
|
||
"BRGammaValue",
|
||
"BRImprovedGray",
|
||
"BlackSubstitution",
|
||
"ColorModel",
|
||
"HPCMYKInks",
|
||
"HPCSGraphics",
|
||
"HPCSImages",
|
||
"HPCSText",
|
||
"HPColorSmart",
|
||
"RPSBlackMode",
|
||
"RPSBlackOverPrint",
|
||
"Rcmyksimulation",
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char *color_group_whitelist[] = {
|
||
"ColorPage",
|
||
"FPColorWise1",
|
||
"FPColorWise2",
|
||
"FPColorWise3",
|
||
"FPColorWise4",
|
||
"FPColorWise5",
|
||
"HPColorOptionsPanel",
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char *image_quality_option_whitelist[] = {
|
||
"BRDocument",
|
||
"BRHalfTonePattern",
|
||
"BRNormalPrt",
|
||
"BRPrintQuality",
|
||
"BitsPerPixel",
|
||
"Darkness",
|
||
"Dithering",
|
||
"EconoMode",
|
||
"Economode",
|
||
"HPEconoMode",
|
||
"HPEdgeControl",
|
||
"HPGraphicsHalftone",
|
||
"HPHalftone",
|
||
"HPLJDensity",
|
||
"HPPhotoHalftone",
|
||
"OutputMode",
|
||
"REt",
|
||
"RPSBitsPerPixel",
|
||
"RPSDitherType",
|
||
"Resolution",
|
||
"ScreenLock",
|
||
"Smoothing",
|
||
"TonerSaveMode",
|
||
"UCRGCRForImage",
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char *image_quality_group_whitelist[] = {
|
||
"FPImageQuality1",
|
||
"FPImageQuality2",
|
||
"FPImageQuality3",
|
||
"ImageQualityPage",
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char * finishing_option_whitelist[] = {
|
||
"BindColor",
|
||
"BindEdge",
|
||
"BindType",
|
||
"BindWhen",
|
||
"Booklet",
|
||
"FoldType",
|
||
"FoldWhen",
|
||
"HPStaplerOptions",
|
||
"Jog",
|
||
"Slipsheet",
|
||
"Sorter",
|
||
"StapleLocation",
|
||
"StapleOrientation",
|
||
"StapleWhen",
|
||
"StapleX",
|
||
"StapleY",
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char *finishing_group_whitelist[] = {
|
||
"FPFinishing1",
|
||
"FPFinishing2",
|
||
"FPFinishing3",
|
||
"FPFinishing4",
|
||
"FinishingPage",
|
||
"HPFinishingPanel",
|
||
};
|
||
|
||
/* keep sorted when changing */
|
||
static const char *cups_option_blacklist[] = {
|
||
"Collate",
|
||
"Copies",
|
||
"OutputOrder",
|
||
"PageRegion",
|
||
"PageSize",
|
||
};
|
||
|
||
static char *
|
||
get_option_text (ppd_file_t *ppd_file,
|
||
ppd_option_t *option)
|
||
{
|
||
int i;
|
||
char *utf8;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
|
||
{
|
||
if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
|
||
return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
|
||
"printing option",
|
||
cups_option_translations[i].translation));
|
||
}
|
||
|
||
utf8 = ppd_text_to_utf8 (ppd_file, option->text);
|
||
|
||
/* Some ppd files have spaces in the text before the colon */
|
||
g_strchomp (utf8);
|
||
|
||
return utf8;
|
||
}
|
||
|
||
static char *
|
||
get_choice_text (ppd_file_t *ppd_file,
|
||
ppd_choice_t *choice)
|
||
{
|
||
int i;
|
||
ppd_option_t *option = choice->option;
|
||
const char *keyword = option->keyword;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
|
||
{
|
||
if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
|
||
strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
|
||
return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
|
||
"printing option value",
|
||
cups_choice_translations[i].translation));
|
||
}
|
||
return ppd_text_to_utf8 (ppd_file, choice->text);
|
||
}
|
||
|
||
static gboolean
|
||
group_has_option (ppd_group_t *group,
|
||
ppd_option_t *option)
|
||
{
|
||
int i;
|
||
|
||
if (group == NULL)
|
||
return FALSE;
|
||
|
||
if (group->num_options > 0 &&
|
||
option >= group->options && option < group->options + group->num_options)
|
||
return TRUE;
|
||
|
||
for (i = 0; i < group->num_subgroups; i++)
|
||
{
|
||
if (group_has_option (&group->subgroups[i],option))
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
set_option_off (GtkPrinterOption *option)
|
||
{
|
||
/* Any of these will do, _set only applies the value
|
||
* if its allowed of the option */
|
||
gtk_printer_option_set (option, "False");
|
||
gtk_printer_option_set (option, "Off");
|
||
gtk_printer_option_set (option, "None");
|
||
}
|
||
|
||
static gboolean
|
||
value_is_off (const char *value)
|
||
{
|
||
return (strcasecmp (value, "None") == 0 ||
|
||
strcasecmp (value, "Off") == 0 ||
|
||
strcasecmp (value, "False") == 0);
|
||
}
|
||
|
||
static const char *
|
||
ppd_group_name (ppd_group_t *group)
|
||
{
|
||
return group->name;
|
||
}
|
||
|
||
static int
|
||
available_choices (ppd_file_t *ppd,
|
||
ppd_option_t *option,
|
||
ppd_choice_t ***available,
|
||
gboolean keep_if_only_one_option)
|
||
{
|
||
ppd_option_t *other_option;
|
||
int i, j;
|
||
gchar *conflicts;
|
||
ppd_const_t *constraint;
|
||
const char *choice, *other_choice;
|
||
ppd_option_t *option1, *option2;
|
||
ppd_group_t *installed_options;
|
||
int num_conflicts;
|
||
gboolean all_default;
|
||
int add_auto;
|
||
|
||
if (available)
|
||
*available = NULL;
|
||
|
||
conflicts = g_new0 (char, option->num_choices);
|
||
|
||
installed_options = NULL;
|
||
for (i = 0; i < ppd->num_groups; i++)
|
||
{
|
||
const char *name;
|
||
|
||
name = ppd_group_name (&ppd->groups[i]);
|
||
if (strcmp (name, "InstallableOptions") == 0)
|
||
{
|
||
installed_options = &ppd->groups[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
|
||
{
|
||
option1 = ppdFindOption (ppd, constraint->option1);
|
||
if (option1 == NULL)
|
||
continue;
|
||
|
||
option2 = ppdFindOption (ppd, constraint->option2);
|
||
if (option2 == NULL)
|
||
continue;
|
||
|
||
if (option == option1)
|
||
{
|
||
choice = constraint->choice1;
|
||
other_option = option2;
|
||
other_choice = constraint->choice2;
|
||
}
|
||
else if (option == option2)
|
||
{
|
||
choice = constraint->choice2;
|
||
other_option = option1;
|
||
other_choice = constraint->choice1;
|
||
}
|
||
else
|
||
continue;
|
||
|
||
/* We only care of conflicts with installed_options and PageSize */
|
||
if (!group_has_option (installed_options, other_option) &&
|
||
(strcmp (other_option->keyword, "PageSize") != 0))
|
||
continue;
|
||
|
||
if (*other_choice == 0)
|
||
{
|
||
/* Conflict only if the installed option is not off */
|
||
if (value_is_off (other_option->defchoice))
|
||
continue;
|
||
}
|
||
/* Conflict if the installed option has the specified default */
|
||
else if (strcasecmp (other_choice, other_option->defchoice) != 0)
|
||
continue;
|
||
|
||
if (*choice == 0)
|
||
{
|
||
/* Conflict with all non-off choices */
|
||
for (j = 0; j < option->num_choices; j++)
|
||
{
|
||
if (!value_is_off (option->choices[j].choice))
|
||
conflicts[j] = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (j = 0; j < option->num_choices; j++)
|
||
{
|
||
if (strcasecmp (option->choices[j].choice, choice) == 0)
|
||
conflicts[j] = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
num_conflicts = 0;
|
||
all_default = TRUE;
|
||
for (j = 0; j < option->num_choices; j++)
|
||
{
|
||
if (conflicts[j])
|
||
num_conflicts++;
|
||
else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
|
||
all_default = FALSE;
|
||
}
|
||
|
||
if ((all_default && !keep_if_only_one_option) ||
|
||
(num_conflicts == option->num_choices))
|
||
{
|
||
g_free (conflicts);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Some ppds don't have a "use printer default" option for
|
||
* InputSlot. This means you always have to select a particular slot,
|
||
* and you can't auto-pick source based on the paper size. To support
|
||
* this we always add an auto option if there isn't one already. If
|
||
* the user chooses the generated option we don't send any InputSlot
|
||
* value when printing. The way we detect existing auto-cases is based
|
||
* on feedback from Michael Sweet of cups fame.
|
||
*/
|
||
add_auto = 0;
|
||
if (strcmp (option->keyword, "InputSlot") == 0)
|
||
{
|
||
gboolean found_auto = FALSE;
|
||
for (j = 0; j < option->num_choices; j++)
|
||
{
|
||
if (!conflicts[j])
|
||
{
|
||
if (strcmp (option->choices[j].choice, "Auto") == 0 ||
|
||
strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
|
||
strcmp (option->choices[j].choice, "Default") == 0 ||
|
||
strcmp (option->choices[j].choice, "None") == 0 ||
|
||
strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
|
||
strcmp (option->choices[j].choice, "Unspecified") == 0 ||
|
||
option->choices[j].code == NULL ||
|
||
option->choices[j].code[0] == 0)
|
||
{
|
||
found_auto = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!found_auto)
|
||
add_auto = 1;
|
||
}
|
||
|
||
if (available)
|
||
{
|
||
*available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
|
||
|
||
i = 0;
|
||
for (j = 0; j < option->num_choices; j++)
|
||
{
|
||
if (!conflicts[j])
|
||
(*available)[i++] = &option->choices[j];
|
||
}
|
||
|
||
if (add_auto)
|
||
(*available)[i++] = NULL;
|
||
}
|
||
|
||
g_free (conflicts);
|
||
|
||
return option->num_choices - num_conflicts + add_auto;
|
||
}
|
||
|
||
static GtkPrinterOption *
|
||
create_pickone_option (ppd_file_t *ppd_file,
|
||
ppd_option_t *ppd_option,
|
||
const gchar *gtk_name)
|
||
{
|
||
GtkPrinterOption *option;
|
||
ppd_choice_t **available;
|
||
char *label;
|
||
int n_choices;
|
||
int i;
|
||
ppd_coption_t *coption;
|
||
|
||
g_assert (ppd_option->ui == PPD_UI_PICKONE);
|
||
|
||
option = NULL;
|
||
|
||
n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
|
||
if (n_choices > 0)
|
||
{
|
||
|
||
/* right now only support one parameter per custom option
|
||
* if more than one print warning and only offer the default choices
|
||
*/
|
||
|
||
label = get_option_text (ppd_file, ppd_option);
|
||
|
||
coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
|
||
|
||
if (coption)
|
||
{
|
||
ppd_cparam_t *cparam;
|
||
|
||
cparam = ppdFirstCustomParam (coption);
|
||
|
||
if (ppdNextCustomParam (coption) == NULL)
|
||
{
|
||
switch (cparam->type)
|
||
{
|
||
case PPD_CUSTOM_INT:
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
|
||
break;
|
||
case PPD_CUSTOM_PASSCODE:
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
|
||
break;
|
||
case PPD_CUSTOM_PASSWORD:
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
|
||
break;
|
||
case PPD_CUSTOM_REAL:
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
|
||
break;
|
||
case PPD_CUSTOM_STRING:
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
|
||
break;
|
||
#ifdef PRINT_IGNORED_OPTIONS
|
||
case PPD_CUSTOM_POINTS:
|
||
g_warning ("CUPS Backend: PPD Custom Points Option not supported");
|
||
break;
|
||
case PPD_CUSTOM_CURVE:
|
||
g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
|
||
break;
|
||
case PPD_CUSTOM_INVCURVE:
|
||
g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
|
||
break;
|
||
#endif
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
#ifdef PRINT_IGNORED_OPTIONS
|
||
else
|
||
g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
|
||
#endif
|
||
}
|
||
|
||
if (!option)
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
g_free (label);
|
||
|
||
gtk_printer_option_allocate_choices (option, n_choices);
|
||
for (i = 0; i < n_choices; i++)
|
||
{
|
||
if (available[i] == NULL)
|
||
{
|
||
/* This was auto-added */
|
||
option->choices[i] = g_strdup ("gtk-ignore-value");
|
||
option->choices_display[i] = g_strdup (_("Printer Default"));
|
||
}
|
||
else
|
||
{
|
||
option->choices[i] = g_strdup (available[i]->choice);
|
||
option->choices_display[i] = get_choice_text (ppd_file, available[i]);
|
||
}
|
||
}
|
||
|
||
if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
|
||
{
|
||
if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
|
||
gtk_printer_option_set (option, ppd_option->defchoice + 7);
|
||
else
|
||
gtk_printer_option_set (option, ppd_option->defchoice);
|
||
}
|
||
else
|
||
{
|
||
gtk_printer_option_set (option, ppd_option->defchoice);
|
||
}
|
||
}
|
||
#ifdef PRINT_IGNORED_OPTIONS
|
||
else
|
||
g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
|
||
#endif
|
||
g_free (available);
|
||
|
||
return option;
|
||
}
|
||
|
||
static GtkPrinterOption *
|
||
create_boolean_option (ppd_file_t *ppd_file,
|
||
ppd_option_t *ppd_option,
|
||
const gchar *gtk_name)
|
||
{
|
||
GtkPrinterOption *option;
|
||
ppd_choice_t **available;
|
||
char *label;
|
||
int n_choices;
|
||
|
||
g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
|
||
|
||
option = NULL;
|
||
|
||
n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
|
||
if (n_choices == 2)
|
||
{
|
||
label = get_option_text (ppd_file, ppd_option);
|
||
option = gtk_printer_option_new (gtk_name, label,
|
||
GTK_PRINTER_OPTION_TYPE_BOOLEAN);
|
||
g_free (label);
|
||
|
||
gtk_printer_option_allocate_choices (option, 2);
|
||
option->choices[0] = g_strdup ("True");
|
||
option->choices_display[0] = g_strdup ("True");
|
||
option->choices[1] = g_strdup ("False");
|
||
option->choices_display[1] = g_strdup ("False");
|
||
|
||
gtk_printer_option_set (option, ppd_option->defchoice);
|
||
}
|
||
#ifdef PRINT_IGNORED_OPTIONS
|
||
else
|
||
g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
|
||
#endif
|
||
g_free (available);
|
||
|
||
return option;
|
||
}
|
||
|
||
static gchar *
|
||
get_ppd_option_name (const gchar *keyword)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
|
||
if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
|
||
return g_strdup (ppd_option_names[i].name);
|
||
|
||
return g_strdup_printf ("cups-%s", keyword);
|
||
}
|
||
|
||
static gchar *
|
||
get_lpoption_name (const gchar *lpoption)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
|
||
if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
|
||
return g_strdup (ppd_option_names[i].name);
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
|
||
if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
|
||
return g_strdup (lpoption_names[i].name);
|
||
|
||
return g_strdup_printf ("cups-%s", lpoption);
|
||
}
|
||
|
||
static int
|
||
strptr_cmp (const void *a,
|
||
const void *b)
|
||
{
|
||
char **aa = (char **)a;
|
||
char **bb = (char **)b;
|
||
return strcmp (*aa, *bb);
|
||
}
|
||
|
||
|
||
static gboolean
|
||
string_in_table (const gchar *str,
|
||
const gchar *table[],
|
||
gint table_len)
|
||
{
|
||
return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
|
||
}
|
||
|
||
#define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
|
||
|
||
static void
|
||
handle_option (GtkPrinterOptionSet *set,
|
||
ppd_file_t *ppd_file,
|
||
ppd_option_t *ppd_option,
|
||
ppd_group_t *toplevel_group,
|
||
GtkPrintSettings *settings)
|
||
{
|
||
GtkPrinterOption *option;
|
||
char *option_name;
|
||
int i;
|
||
|
||
if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
|
||
return;
|
||
|
||
option_name = get_ppd_option_name (ppd_option->keyword);
|
||
|
||
option = NULL;
|
||
if (ppd_option->ui == PPD_UI_PICKONE)
|
||
option = create_pickone_option (ppd_file, ppd_option, option_name);
|
||
else if (ppd_option->ui == PPD_UI_BOOLEAN)
|
||
option = create_boolean_option (ppd_file, ppd_option, option_name);
|
||
#ifdef PRINT_IGNORED_OPTIONS
|
||
else
|
||
g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
|
||
#endif
|
||
|
||
if (option)
|
||
{
|
||
const char *name;
|
||
|
||
name = ppd_group_name (toplevel_group);
|
||
if (STRING_IN_TABLE (name, color_group_whitelist) ||
|
||
STRING_IN_TABLE (ppd_option->keyword, color_option_whitelist))
|
||
{
|
||
option->group = g_strdup ("ColorPage");
|
||
}
|
||
else if (STRING_IN_TABLE (name, image_quality_group_whitelist) ||
|
||
STRING_IN_TABLE (ppd_option->keyword, image_quality_option_whitelist))
|
||
{
|
||
option->group = g_strdup ("ImageQualityPage");
|
||
}
|
||
else if (STRING_IN_TABLE (name, finishing_group_whitelist) ||
|
||
STRING_IN_TABLE (ppd_option->keyword, finishing_option_whitelist))
|
||
{
|
||
option->group = g_strdup ("FinishingPage");
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
|
||
{
|
||
if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
|
||
{
|
||
option->group = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
|
||
"printing option group",
|
||
cups_group_translations[i].translation));
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i == G_N_ELEMENTS (cups_group_translations))
|
||
option->group = g_strdup (toplevel_group->text);
|
||
}
|
||
|
||
set_option_from_settings (option, settings);
|
||
|
||
gtk_printer_option_set_add (set, option);
|
||
}
|
||
|
||
g_free (option_name);
|
||
}
|
||
|
||
static void
|
||
handle_group (GtkPrinterOptionSet *set,
|
||
ppd_file_t *ppd_file,
|
||
ppd_group_t *group,
|
||
ppd_group_t *toplevel_group,
|
||
GtkPrintSettings *settings)
|
||
{
|
||
gint i;
|
||
const gchar *name;
|
||
|
||
/* Ignore installable options */
|
||
name = ppd_group_name (toplevel_group);
|
||
if (strcmp (name, "InstallableOptions") == 0)
|
||
return;
|
||
|
||
for (i = 0; i < group->num_options; i++)
|
||
handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
|
||
|
||
for (i = 0; i < group->num_subgroups; i++)
|
||
handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
|
||
|
||
}
|
||
|
||
#ifdef HAVE_COLORD
|
||
|
||
typedef struct {
|
||
GtkPrintSettings *settings;
|
||
GtkPrinter *printer;
|
||
} GtkPrintBackendCupsColordHelper;
|
||
|
||
static void
|
||
colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
|
||
GtkPrintBackendCupsColordHelper *helper)
|
||
{
|
||
gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
|
||
helper->settings,
|
||
set);
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* Lookup translation and Gtk+ name of given IPP option name.
|
||
*/
|
||
static gboolean
|
||
get_ipp_option_translation (const gchar *ipp_option_name,
|
||
gchar **gtk_option_name,
|
||
gchar **translation)
|
||
{
|
||
gint i;
|
||
|
||
*gtk_option_name = NULL;
|
||
*translation = NULL;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (ipp_option_translations); i++)
|
||
{
|
||
if (g_strcmp0 (ipp_option_translations[i].ipp_option_name, ipp_option_name) == 0)
|
||
{
|
||
*gtk_option_name = g_strdup (ipp_option_translations[i].gtk_option_name);
|
||
*translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
|
||
"printing option",
|
||
ipp_option_translations[i].translation));
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* Lookup translation of given IPP choice.
|
||
*/
|
||
static gchar *
|
||
get_ipp_choice_translation (const gchar *ipp_option_name,
|
||
const gchar *ipp_choice)
|
||
{
|
||
const gchar *nptr;
|
||
guint64 index;
|
||
gchar *translation = NULL;
|
||
gsize ipp_choice_length;
|
||
gchar *endptr;
|
||
gint i;
|
||
|
||
for (i = 0; ipp_choice_translations[i].ipp_option_name != NULL; i++)
|
||
{
|
||
if (g_strcmp0 (ipp_choice_translations[i].ipp_option_name, ipp_option_name) == 0)
|
||
{
|
||
ipp_choice_length = strlen (ipp_choice_translations[i].ipp_choice);
|
||
|
||
if (g_strcmp0 (ipp_choice_translations[i].ipp_choice, ipp_choice) == 0)
|
||
{
|
||
translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
|
||
ipp_option_name,
|
||
ipp_choice_translations[i].translation));
|
||
break;
|
||
}
|
||
else if (g_str_has_suffix (ipp_choice_translations[i].ipp_choice, "-N") &&
|
||
g_ascii_strncasecmp (ipp_choice_translations[i].ipp_choice,
|
||
ipp_choice,
|
||
ipp_choice_length - 2) == 0)
|
||
{
|
||
/* Find out index of the ipp_choice if it is supported for the choice. */
|
||
endptr = NULL;
|
||
nptr = ipp_choice + ipp_choice_length - 1;
|
||
index = g_ascii_strtoull (nptr,
|
||
&endptr,
|
||
10);
|
||
|
||
if (index != 0 || endptr != nptr)
|
||
{
|
||
translation = get_ipp_choice_translation_string (index, i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return translation;
|
||
}
|
||
|
||
/*
|
||
* Format an IPP choice to a displayable string.
|
||
*/
|
||
static gchar *
|
||
format_ipp_choice (const gchar *ipp_choice)
|
||
{
|
||
gboolean after_space = TRUE;
|
||
gchar *result = NULL;
|
||
gsize i;
|
||
|
||
if (ipp_choice != NULL)
|
||
{
|
||
result = g_strdup (ipp_choice);
|
||
/* Replace all '-' by spaces. */
|
||
result = g_strdelimit (result, "-", ' ');
|
||
if (g_str_is_ascii (result))
|
||
{
|
||
/* Convert all leading characters to upper case. */
|
||
for (i = 0; i < strlen (result); i++)
|
||
{
|
||
if (after_space && g_ascii_isalpha (result[i]))
|
||
result[i] = g_ascii_toupper (result[i]);
|
||
|
||
after_space = g_ascii_isspace (result[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
* Look the IPP option up in given set of options.
|
||
* Create it if it doesn't exist and set its default value
|
||
* if available.
|
||
*/
|
||
static GtkPrinterOption *
|
||
setup_ipp_option (gchar *ipp_option_name,
|
||
gchar *ipp_choice_default,
|
||
GList *ipp_choices,
|
||
GtkPrinterOptionSet *set)
|
||
{
|
||
GtkPrinterOption *option = NULL;
|
||
gchar *gtk_option_name = NULL;
|
||
gchar *translation = NULL;
|
||
gchar *ipp_choice;
|
||
gsize i;
|
||
|
||
get_ipp_option_translation (ipp_option_name,
|
||
>k_option_name,
|
||
&translation);
|
||
|
||
/* Look the option up in the given set of options. */
|
||
if (gtk_option_name != NULL)
|
||
option = gtk_printer_option_set_lookup (set, gtk_option_name);
|
||
|
||
/* The option was not found, create it from given choices. */
|
||
if (option == NULL &&
|
||
ipp_choices != NULL)
|
||
{
|
||
GList *iter;
|
||
gsize length;
|
||
char **choices = NULL;
|
||
char **choices_display = NULL;
|
||
|
||
option = gtk_printer_option_new (gtk_option_name,
|
||
translation,
|
||
GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
|
||
length = g_list_length (ipp_choices);
|
||
|
||
choices = g_new0 (char *, length);
|
||
choices_display = g_new0 (char *, length);
|
||
|
||
i = 0;
|
||
for (iter = ipp_choices; iter != NULL; iter = iter->next)
|
||
{
|
||
ipp_choice = (gchar *) iter->data;
|
||
|
||
choices[i] = g_strdup (ipp_choice);
|
||
|
||
translation = get_ipp_choice_translation (ipp_option_name,
|
||
ipp_choice);
|
||
if (translation != NULL)
|
||
choices_display[i] = translation;
|
||
else
|
||
choices_display[i] = format_ipp_choice (ipp_choice);
|
||
|
||
i++;
|
||
}
|
||
|
||
if (choices != NULL &&
|
||
choices_display != NULL)
|
||
{
|
||
gtk_printer_option_choices_from_array (option,
|
||
length,
|
||
choices,
|
||
choices_display);
|
||
}
|
||
|
||
option_set_is_ipp_option (option, TRUE);
|
||
|
||
gtk_printer_option_set_add (set, option);
|
||
|
||
g_free (choices);
|
||
g_free (choices_display);
|
||
}
|
||
|
||
/* The option exists. Set its default value if available. */
|
||
if (option != NULL &&
|
||
ipp_choice_default != NULL)
|
||
{
|
||
gtk_printer_option_set (option, ipp_choice_default);
|
||
}
|
||
|
||
return option;
|
||
}
|
||
|
||
static GtkPrinterOptionSet *
|
||
cups_printer_get_options (GtkPrinter *printer,
|
||
GtkPrintSettings *settings,
|
||
GtkPageSetup *page_setup,
|
||
GtkPrintCapabilities capabilities)
|
||
{
|
||
GtkPrinterOptionSet *set;
|
||
GtkPrinterOption *option;
|
||
ppd_file_t *ppd_file;
|
||
int i;
|
||
char *print_at[] = { "now", "at", "on-hold" };
|
||
char *n_up[] = {"1", "2", "4", "6", "9", "16" };
|
||
char *prio[] = {"100", "80", "50", "30" };
|
||
/* Translators: These strings name the possible values of the
|
||
* job priority option in the print dialog
|
||
*/
|
||
char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
|
||
char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
|
||
/* Translators: These strings name the possible arrangements of
|
||
* multiple pages on a sheet when printing
|
||
*/
|
||
char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
|
||
N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
|
||
N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
|
||
N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
|
||
char *name;
|
||
int num_opts;
|
||
cups_option_t *opts = NULL;
|
||
GtkPrintBackendCups *backend;
|
||
GtkTextDirection text_direction;
|
||
GtkPrinterCups *cups_printer = NULL;
|
||
#ifdef HAVE_COLORD
|
||
GtkPrintBackendCupsColordHelper *helper;
|
||
#endif
|
||
char *default_number_up;
|
||
|
||
set = gtk_printer_option_set_new ();
|
||
|
||
/* Cups specific, non-ppd related settings */
|
||
|
||
for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
|
||
prio_display[i] = _(prio_display[i]);
|
||
|
||
/* Translators, this string is used to label the job priority option
|
||
* in the print dialog
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
|
||
prio, prio_display);
|
||
gtk_printer_option_set (option, "50");
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
/* Translators, this string is used to label the billing info entry
|
||
* in the print dialog
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
|
||
gtk_printer_option_set (option, "");
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
|
||
cups_printer = GTK_PRINTER_CUPS (printer);
|
||
|
||
if (backend != NULL && printer != NULL)
|
||
{
|
||
char *cover_default[] = {
|
||
"none",
|
||
"classified",
|
||
"confidential",
|
||
"secret",
|
||
"standard",
|
||
"topsecret",
|
||
"unclassified"
|
||
};
|
||
/* Translators, these strings are names for various 'standard' cover
|
||
* pages that the printing system may support.
|
||
*/
|
||
char *cover_display_default[] = {
|
||
NC_("cover page", "None"),
|
||
NC_("cover page", "Classified"),
|
||
NC_("cover page", "Confidential"),
|
||
NC_("cover page", "Secret"),
|
||
NC_("cover page", "Standard"),
|
||
NC_("cover page", "Top Secret"),
|
||
NC_("cover page", "Unclassified")
|
||
};
|
||
char **cover = NULL;
|
||
char **cover_display = NULL;
|
||
char **cover_display_translated = NULL;
|
||
gint num_of_covers = 0;
|
||
gpointer value;
|
||
gint j;
|
||
|
||
/* Translators, this string is used to label the pages-per-sheet option
|
||
* in the print dialog
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-n-up", C_("printer option", "Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), n_up, n_up);
|
||
default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
|
||
gtk_printer_option_set (option, default_number_up);
|
||
g_free (default_number_up);
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
|
||
{
|
||
for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
|
||
n_up_layout_display[i] = _(n_up_layout_display[i]);
|
||
|
||
/* Translators, this string is used to label the option in the print
|
||
* dialog that controls in what order multiple pages are arranged
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-n-up-layout", C_("printer option", "Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
|
||
n_up_layout, n_up_layout_display);
|
||
|
||
text_direction = gtk_widget_get_default_direction ();
|
||
if (text_direction == GTK_TEXT_DIR_LTR)
|
||
gtk_printer_option_set (option, "lrtb");
|
||
else
|
||
gtk_printer_option_set (option, "rltb");
|
||
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
}
|
||
|
||
num_of_covers = cups_printer->number_of_covers;
|
||
cover = g_new (char *, num_of_covers + 1);
|
||
cover[num_of_covers] = NULL;
|
||
cover_display = g_new (char *, num_of_covers + 1);
|
||
cover_display[num_of_covers] = NULL;
|
||
cover_display_translated = g_new (char *, num_of_covers + 1);
|
||
cover_display_translated[num_of_covers] = NULL;
|
||
|
||
for (i = 0; i < num_of_covers; i++)
|
||
{
|
||
cover[i] = g_strdup (cups_printer->covers[i]);
|
||
value = NULL;
|
||
for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
|
||
if (strcmp (cover_default[j], cover[i]) == 0)
|
||
{
|
||
value = cover_display_default[j];
|
||
break;
|
||
}
|
||
cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (cups_printer->covers[i]);
|
||
}
|
||
|
||
for (i = 0; i < num_of_covers; i++)
|
||
cover_display_translated[i] = (gchar *)g_dpgettext2 (GETTEXT_PACKAGE, "cover page", cover_display[i]);
|
||
|
||
/* Translators, this is the label used for the option in the print
|
||
* dialog that controls the front cover page.
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-cover-before", C_("printer option", "Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
gtk_printer_option_choices_from_array (option, num_of_covers,
|
||
cover, cover_display_translated);
|
||
|
||
if (cups_printer->default_cover_before != NULL)
|
||
gtk_printer_option_set (option, cups_printer->default_cover_before);
|
||
else
|
||
gtk_printer_option_set (option, "none");
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
/* Translators, this is the label used for the option in the print
|
||
* dialog that controls the back cover page.
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-cover-after", C_("printer option", "After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
gtk_printer_option_choices_from_array (option, num_of_covers,
|
||
cover, cover_display_translated);
|
||
if (cups_printer->default_cover_after != NULL)
|
||
gtk_printer_option_set (option, cups_printer->default_cover_after);
|
||
else
|
||
gtk_printer_option_set (option, "none");
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
g_strfreev (cover);
|
||
g_strfreev (cover_display);
|
||
g_free (cover_display_translated);
|
||
}
|
||
|
||
/* Translators: this is the name of the option that controls when
|
||
* a print job is printed. Possible values are 'now', a specified time,
|
||
* or 'on hold'
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-print-time", C_("printer option", "Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
|
||
gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
|
||
print_at, print_at);
|
||
gtk_printer_option_set (option, "now");
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
/* Translators: this is the name of the option that allows the user
|
||
* to specify a time when a print job will be printed.
|
||
*/
|
||
option = gtk_printer_option_new ("gtk-print-time-text", C_("printer option", "Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
|
||
gtk_printer_option_set (option, "");
|
||
set_option_from_settings (option, settings);
|
||
gtk_printer_option_set_add (set, option);
|
||
g_object_unref (option);
|
||
|
||
/* Printer (ppd) specific settings */
|
||
ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
|
||
if (ppd_file)
|
||
{
|
||
GtkPaperSize *paper_size;
|
||
ppd_option_t *ppd_option;
|
||
const gchar *ppd_name;
|
||
|
||
ppdMarkDefaults (ppd_file);
|
||
|
||
paper_size = gtk_page_setup_get_paper_size (page_setup);
|
||
|
||
ppd_option = ppdFindOption (ppd_file, "PageSize");
|
||
if (ppd_option)
|
||
{
|
||
ppd_name = gtk_paper_size_get_ppd_name (paper_size);
|
||
|
||
if (ppd_name)
|
||
strncpy (ppd_option->defchoice, ppd_name, PPD_MAX_NAME);
|
||
else
|
||
{
|
||
gchar *custom_name;
|
||
char width[G_ASCII_DTOSTR_BUF_SIZE];
|
||
char height[G_ASCII_DTOSTR_BUF_SIZE];
|
||
|
||
g_ascii_formatd (width, sizeof (width), "%.2f",
|
||
gtk_paper_size_get_width (paper_size,
|
||
GTK_UNIT_POINTS));
|
||
g_ascii_formatd (height, sizeof (height), "%.2f",
|
||
gtk_paper_size_get_height (paper_size,
|
||
GTK_UNIT_POINTS));
|
||
/* Translators: this format is used to display a custom
|
||
* paper size. The two placeholders are replaced with
|
||
* the width and height in points. E.g: "Custom
|
||
* 230.4x142.9"
|
||
*/
|
||
custom_name = g_strdup_printf (_("Custom %s×%s"), width, height);
|
||
strncpy (ppd_option->defchoice, custom_name, PPD_MAX_NAME);
|
||
g_free (custom_name);
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < ppd_file->num_groups; i++)
|
||
handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
|
||
}
|
||
else
|
||
{
|
||
/* Try IPP options */
|
||
|
||
option = setup_ipp_option ("sides",
|
||
cups_printer->sides_default,
|
||
cups_printer->sides_supported,
|
||
set);
|
||
|
||
if (option != NULL)
|
||
set_option_from_settings (option, settings);
|
||
|
||
option = setup_ipp_option ("output-bin",
|
||
cups_printer->output_bin_default,
|
||
cups_printer->output_bin_supported,
|
||
set);
|
||
|
||
if (option != NULL)
|
||
set_option_from_settings (option, settings);
|
||
}
|
||
|
||
/* Now honor the user set defaults for this printer */
|
||
num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
|
||
|
||
for (i = 0; i < num_opts; i++)
|
||
{
|
||
if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
|
||
continue;
|
||
|
||
name = get_lpoption_name (opts[i].name);
|
||
if (strcmp (name, "cups-job-sheets") == 0)
|
||
{
|
||
gchar **values;
|
||
gint num_values;
|
||
|
||
values = g_strsplit (opts[i].value, ",", 2);
|
||
num_values = g_strv_length (values);
|
||
|
||
option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
|
||
if (option && num_values > 0)
|
||
gtk_printer_option_set (option, g_strstrip (values[0]));
|
||
|
||
option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
|
||
if (option && num_values > 1)
|
||
gtk_printer_option_set (option, g_strstrip (values[1]));
|
||
|
||
g_strfreev (values);
|
||
}
|
||
else if (strcmp (name, "cups-job-hold-until") == 0)
|
||
{
|
||
GtkPrinterOption *option2 = NULL;
|
||
|
||
option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
|
||
if (option && opts[i].value)
|
||
{
|
||
option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
|
||
if (option2)
|
||
{
|
||
if (strcmp (opts[i].value, "indefinite") == 0)
|
||
gtk_printer_option_set (option2, "on-hold");
|
||
else
|
||
{
|
||
gtk_printer_option_set (option2, "at");
|
||
gtk_printer_option_set (option, opts[i].value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (strcmp (name, "cups-sides") == 0)
|
||
{
|
||
option = gtk_printer_option_set_lookup (set, "gtk-duplex");
|
||
if (option && opts[i].value)
|
||
{
|
||
if (!option_is_ipp_option (option))
|
||
{
|
||
if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
|
||
gtk_printer_option_set (option, "DuplexTumble");
|
||
else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
|
||
gtk_printer_option_set (option, "DuplexNoTumble");
|
||
}
|
||
else
|
||
{
|
||
gtk_printer_option_set (option, opts[i].value);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
option = gtk_printer_option_set_lookup (set, name);
|
||
if (option)
|
||
gtk_printer_option_set (option, opts[i].value);
|
||
}
|
||
g_free (name);
|
||
}
|
||
|
||
cupsFreeOptions (num_opts, opts);
|
||
|
||
#ifdef HAVE_COLORD
|
||
/* TRANSLATORS: this this the ICC color profile to use for this job */
|
||
option = gtk_printer_option_new ("colord-profile",
|
||
C_("printer option", "Printer Profile"),
|
||
GTK_PRINTER_OPTION_TYPE_INFO);
|
||
|
||
/* assign it to the color page */
|
||
option->group = g_strdup ("ColorPage");
|
||
|
||
/* TRANSLATORS: this is when color profile information is unavailable */
|
||
gtk_printer_option_set (option, C_("printer option value", "Unavailable"));
|
||
gtk_printer_option_set_add (set, option);
|
||
|
||
/* watch to see if the user changed the options */
|
||
helper = g_new (GtkPrintBackendCupsColordHelper, 1);
|
||
helper->printer = printer;
|
||
helper->settings = settings;
|
||
g_signal_connect_data (set, "changed",
|
||
G_CALLBACK (colord_printer_option_set_changed_cb),
|
||
helper,
|
||
(GClosureNotify) g_free,
|
||
0);
|
||
|
||
/* initial coldplug */
|
||
gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
|
||
settings, set);
|
||
g_object_bind_property (printer, "profile-title",
|
||
option, "value",
|
||
G_BINDING_DEFAULT);
|
||
|
||
#endif
|
||
|
||
return set;
|
||
}
|
||
|
||
|
||
static void
|
||
mark_option_from_set (GtkPrinterOptionSet *set,
|
||
ppd_file_t *ppd_file,
|
||
ppd_option_t *ppd_option)
|
||
{
|
||
GtkPrinterOption *option;
|
||
char *name = get_ppd_option_name (ppd_option->keyword);
|
||
|
||
option = gtk_printer_option_set_lookup (set, name);
|
||
|
||
if (option)
|
||
ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
|
||
|
||
g_free (name);
|
||
}
|
||
|
||
|
||
static void
|
||
mark_group_from_set (GtkPrinterOptionSet *set,
|
||
ppd_file_t *ppd_file,
|
||
ppd_group_t *group)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < group->num_options; i++)
|
||
mark_option_from_set (set, ppd_file, &group->options[i]);
|
||
|
||
for (i = 0; i < group->num_subgroups; i++)
|
||
mark_group_from_set (set, ppd_file, &group->subgroups[i]);
|
||
}
|
||
|
||
static void
|
||
set_conflicts_from_option (GtkPrinterOptionSet *set,
|
||
ppd_file_t *ppd_file,
|
||
ppd_option_t *ppd_option)
|
||
{
|
||
GtkPrinterOption *option;
|
||
char *name;
|
||
|
||
if (ppd_option->conflicted)
|
||
{
|
||
name = get_ppd_option_name (ppd_option->keyword);
|
||
option = gtk_printer_option_set_lookup (set, name);
|
||
|
||
if (option)
|
||
gtk_printer_option_set_has_conflict (option, TRUE);
|
||
#ifdef PRINT_IGNORED_OPTIONS
|
||
else
|
||
g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
|
||
#endif
|
||
|
||
g_free (name);
|
||
}
|
||
}
|
||
|
||
static void
|
||
set_conflicts_from_group (GtkPrinterOptionSet *set,
|
||
ppd_file_t *ppd_file,
|
||
ppd_group_t *group)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < group->num_options; i++)
|
||
set_conflicts_from_option (set, ppd_file, &group->options[i]);
|
||
|
||
for (i = 0; i < group->num_subgroups; i++)
|
||
set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
|
||
}
|
||
|
||
static gboolean
|
||
cups_printer_mark_conflicts (GtkPrinter *printer,
|
||
GtkPrinterOptionSet *options)
|
||
{
|
||
ppd_file_t *ppd_file;
|
||
int num_conflicts;
|
||
int i;
|
||
|
||
ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
|
||
|
||
if (ppd_file == NULL)
|
||
return FALSE;
|
||
|
||
ppdMarkDefaults (ppd_file);
|
||
|
||
for (i = 0; i < ppd_file->num_groups; i++)
|
||
mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
|
||
|
||
num_conflicts = ppdConflicts (ppd_file);
|
||
|
||
if (num_conflicts > 0)
|
||
{
|
||
for (i = 0; i < ppd_file->num_groups; i++)
|
||
set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
|
||
}
|
||
|
||
return num_conflicts > 0;
|
||
}
|
||
|
||
struct OptionData {
|
||
GtkPrinter *printer;
|
||
GtkPrinterOptionSet *options;
|
||
GtkPrintSettings *settings;
|
||
ppd_file_t *ppd_file;
|
||
};
|
||
|
||
typedef struct {
|
||
const char *cups;
|
||
const char *standard;
|
||
} NameMapping;
|
||
|
||
static void
|
||
map_settings_to_option (GtkPrinterOption *option,
|
||
const NameMapping table[],
|
||
gint n_elements,
|
||
GtkPrintSettings *settings,
|
||
const gchar *standard_name,
|
||
const gchar *cups_name,
|
||
const gchar *ipp_name)
|
||
{
|
||
int i;
|
||
char *name;
|
||
const char *cups_value;
|
||
const char *ipp_value;
|
||
const char *standard_value;
|
||
|
||
/* If the cups-specific setting is set, always use that */
|
||
name = g_strdup_printf ("cups-%s", cups_name);
|
||
cups_value = gtk_print_settings_get (settings, name);
|
||
g_free (name);
|
||
|
||
if (cups_value != NULL)
|
||
{
|
||
gtk_printer_option_set (option, cups_value);
|
||
return;
|
||
}
|
||
|
||
/* If the IPP-specific setting is set, use that */
|
||
name = g_strdup_printf ("cups-%s", ipp_name);
|
||
ipp_value = gtk_print_settings_get (settings, name);
|
||
g_free (name);
|
||
|
||
if (ipp_value != NULL)
|
||
{
|
||
gtk_printer_option_set (option, ipp_value);
|
||
return;
|
||
}
|
||
|
||
/* Otherwise we try to convert from the general setting */
|
||
standard_value = gtk_print_settings_get (settings, standard_name);
|
||
if (standard_value == NULL)
|
||
return;
|
||
|
||
for (i = 0; i < n_elements; i++)
|
||
{
|
||
if (table[i].cups == NULL && table[i].standard == NULL)
|
||
{
|
||
gtk_printer_option_set (option, standard_value);
|
||
break;
|
||
}
|
||
else if (table[i].cups == NULL &&
|
||
strcmp (table[i].standard, standard_value) == 0)
|
||
{
|
||
set_option_off (option);
|
||
break;
|
||
}
|
||
else if (strcmp (table[i].standard, standard_value) == 0)
|
||
{
|
||
gtk_printer_option_set (option, table[i].cups);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
map_option_to_settings (const gchar *value,
|
||
const NameMapping table[],
|
||
gint n_elements,
|
||
GtkPrintSettings *settings,
|
||
const gchar *standard_name,
|
||
const gchar *cups_name,
|
||
const gchar *ipp_name,
|
||
gboolean is_ipp_option)
|
||
{
|
||
int i;
|
||
char *name;
|
||
|
||
for (i = 0; i < n_elements; i++)
|
||
{
|
||
if (table[i].cups == NULL && table[i].standard == NULL)
|
||
{
|
||
gtk_print_settings_set (settings,
|
||
standard_name,
|
||
value);
|
||
break;
|
||
}
|
||
else if (table[i].cups == NULL && table[i].standard != NULL)
|
||
{
|
||
if (value_is_off (value))
|
||
{
|
||
gtk_print_settings_set (settings,
|
||
standard_name,
|
||
table[i].standard);
|
||
break;
|
||
}
|
||
}
|
||
else if (strcmp (table[i].cups, value) == 0)
|
||
{
|
||
gtk_print_settings_set (settings,
|
||
standard_name,
|
||
table[i].standard);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Always set the corresponding cups-specific setting */
|
||
if (is_ipp_option)
|
||
name = g_strdup_printf ("cups-%s", ipp_name);
|
||
else
|
||
name = g_strdup_printf ("cups-%s", cups_name);
|
||
|
||
gtk_print_settings_set (settings, name, value);
|
||
|
||
g_free (name);
|
||
}
|
||
|
||
|
||
static const NameMapping paper_source_map[] = {
|
||
{ "Lower", "lower"},
|
||
{ "Middle", "middle"},
|
||
{ "Upper", "upper"},
|
||
{ "Rear", "rear"},
|
||
{ "Envelope", "envelope"},
|
||
{ "Cassette", "cassette"},
|
||
{ "LargeCapacity", "large-capacity"},
|
||
{ "AnySmallFormat", "small-format"},
|
||
{ "AnyLargeFormat", "large-format"},
|
||
{ NULL, NULL}
|
||
};
|
||
|
||
static const NameMapping output_tray_map[] = {
|
||
{ "Upper", "upper"},
|
||
{ "Lower", "lower"},
|
||
{ "Rear", "rear"},
|
||
{ NULL, NULL}
|
||
};
|
||
|
||
static const NameMapping duplex_map[] = {
|
||
{ "DuplexTumble", "vertical" },
|
||
{ "DuplexNoTumble", "horizontal" },
|
||
{ NULL, "simplex" }
|
||
};
|
||
|
||
static const NameMapping output_mode_map[] = {
|
||
{ "Standard", "normal" },
|
||
{ "Normal", "normal" },
|
||
{ "Draft", "draft" },
|
||
{ "Fast", "draft" },
|
||
};
|
||
|
||
static const NameMapping media_type_map[] = {
|
||
{ "Transparency", "transparency"},
|
||
{ "Standard", "stationery"},
|
||
{ NULL, NULL}
|
||
};
|
||
|
||
static const NameMapping all_map[] = {
|
||
{ NULL, NULL}
|
||
};
|
||
|
||
|
||
static void
|
||
set_option_from_settings (GtkPrinterOption *option,
|
||
GtkPrintSettings *settings)
|
||
{
|
||
const char *cups_value;
|
||
char *value;
|
||
|
||
if (settings == NULL)
|
||
return;
|
||
|
||
if (strcmp (option->name, "gtk-paper-source") == 0)
|
||
map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
|
||
settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
|
||
"InputSlot", NULL);
|
||
else if (strcmp (option->name, "gtk-output-tray") == 0)
|
||
map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
|
||
settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
|
||
"OutputBin", "output-bin");
|
||
else if (strcmp (option->name, "gtk-duplex") == 0)
|
||
map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
|
||
settings, GTK_PRINT_SETTINGS_DUPLEX,
|
||
"Duplex", "sides");
|
||
else if (strcmp (option->name, "cups-OutputMode") == 0)
|
||
map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
|
||
settings, GTK_PRINT_SETTINGS_QUALITY,
|
||
"OutputMode", NULL);
|
||
else if (strcmp (option->name, "cups-Resolution") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, option->name);
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
else
|
||
{
|
||
if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
|
||
gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
|
||
gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
|
||
option->value == NULL || option->value[0] == '\0')
|
||
{
|
||
int res = gtk_print_settings_get_resolution (settings);
|
||
int res_x = gtk_print_settings_get_resolution_x (settings);
|
||
int res_y = gtk_print_settings_get_resolution_y (settings);
|
||
|
||
if (res_x != res_y)
|
||
{
|
||
value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
|
||
gtk_printer_option_set (option, value);
|
||
g_free (value);
|
||
}
|
||
else if (res != 0)
|
||
{
|
||
value = g_strdup_printf ("%ddpi", res);
|
||
gtk_printer_option_set (option, value);
|
||
g_free (value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (strcmp (option->name, "gtk-paper-type") == 0)
|
||
map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
|
||
settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
|
||
"MediaType", NULL);
|
||
else if (strcmp (option->name, "gtk-n-up") == 0)
|
||
{
|
||
map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
|
||
settings, GTK_PRINT_SETTINGS_NUMBER_UP,
|
||
"number-up", NULL);
|
||
}
|
||
else if (strcmp (option->name, "gtk-n-up-layout") == 0)
|
||
{
|
||
map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
|
||
settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
|
||
"number-up-layout", NULL);
|
||
}
|
||
else if (strcmp (option->name, "gtk-billing-info") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, "cups-job-billing");
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
else if (strcmp (option->name, "gtk-job-prio") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, "cups-job-priority");
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
else if (strcmp (option->name, "gtk-cover-before") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, "cover-before");
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
else if (strcmp (option->name, "gtk-cover-after") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, "cover-after");
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
else if (strcmp (option->name, "gtk-print-time") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, "print-at");
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
else if (strcmp (option->name, "gtk-print-time-text") == 0)
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, "print-at-time");
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
else if (g_str_has_prefix (option->name, "cups-"))
|
||
{
|
||
cups_value = gtk_print_settings_get (settings, option->name);
|
||
if (cups_value)
|
||
gtk_printer_option_set (option, cups_value);
|
||
}
|
||
}
|
||
|
||
static void
|
||
foreach_option_get_settings (GtkPrinterOption *option,
|
||
gpointer user_data)
|
||
{
|
||
struct OptionData *data = user_data;
|
||
GtkPrintSettings *settings = data->settings;
|
||
const char *value;
|
||
|
||
value = option->value;
|
||
|
||
if (strcmp (option->name, "gtk-paper-source") == 0)
|
||
map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
|
||
settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
|
||
"InputSlot", NULL, FALSE);
|
||
else if (strcmp (option->name, "gtk-output-tray") == 0)
|
||
map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
|
||
settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
|
||
"OutputBin", "output-bin", option_is_ipp_option (option));
|
||
else if (strcmp (option->name, "gtk-duplex") == 0)
|
||
map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
|
||
settings, GTK_PRINT_SETTINGS_DUPLEX,
|
||
"Duplex", "sides", option_is_ipp_option (option));
|
||
else if (strcmp (option->name, "cups-OutputMode") == 0)
|
||
map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
|
||
settings, GTK_PRINT_SETTINGS_QUALITY,
|
||
"OutputMode", NULL, FALSE);
|
||
else if (strcmp (option->name, "cups-Resolution") == 0)
|
||
{
|
||
int res, res_x, res_y;
|
||
|
||
if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
|
||
{
|
||
if (res_x > 0 && res_y > 0)
|
||
gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
|
||
}
|
||
else if (sscanf (value, "%ddpi", &res) == 1)
|
||
{
|
||
if (res > 0)
|
||
gtk_print_settings_set_resolution (settings, res);
|
||
}
|
||
|
||
gtk_print_settings_set (settings, option->name, value);
|
||
}
|
||
else if (strcmp (option->name, "gtk-paper-type") == 0)
|
||
map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
|
||
settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
|
||
"MediaType", NULL, FALSE);
|
||
else if (strcmp (option->name, "gtk-n-up") == 0)
|
||
map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
|
||
settings, GTK_PRINT_SETTINGS_NUMBER_UP,
|
||
"number-up", NULL, FALSE);
|
||
else if (strcmp (option->name, "gtk-n-up-layout") == 0)
|
||
map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
|
||
settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
|
||
"number-up-layout", NULL, FALSE);
|
||
else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
|
||
gtk_print_settings_set (settings, "cups-job-billing", value);
|
||
else if (strcmp (option->name, "gtk-job-prio") == 0)
|
||
gtk_print_settings_set (settings, "cups-job-priority", value);
|
||
else if (strcmp (option->name, "gtk-cover-before") == 0)
|
||
gtk_print_settings_set (settings, "cover-before", value);
|
||
else if (strcmp (option->name, "gtk-cover-after") == 0)
|
||
gtk_print_settings_set (settings, "cover-after", value);
|
||
else if (strcmp (option->name, "gtk-print-time") == 0)
|
||
gtk_print_settings_set (settings, "print-at", value);
|
||
else if (strcmp (option->name, "gtk-print-time-text") == 0)
|
||
gtk_print_settings_set (settings, "print-at-time", value);
|
||
else if (g_str_has_prefix (option->name, "cups-"))
|
||
gtk_print_settings_set (settings, option->name, value);
|
||
}
|
||
|
||
static gboolean
|
||
supports_am_pm (void)
|
||
{
|
||
struct tm tmp_tm = { 0 };
|
||
char time[8];
|
||
int length;
|
||
|
||
length = strftime (time, sizeof (time), "%p", &tmp_tm);
|
||
|
||
return length != 0;
|
||
}
|
||
|
||
/* Converts local time to UTC time. Local time has to be in one of these
|
||
* formats: HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
|
||
* {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
|
||
* Returns a newly allocated string holding UTC time in HH:MM:SS format
|
||
* or NULL.
|
||
*/
|
||
gchar *
|
||
localtime_to_utctime (const char *local_time)
|
||
{
|
||
const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
|
||
" %H : %M : %S ",
|
||
" %I : %M %p ", " %p %I : %M ",
|
||
" %H : %M ",
|
||
" %I %p ", " %p %I "};
|
||
const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
|
||
const char *end = NULL;
|
||
struct tm *actual_local_time;
|
||
struct tm *actual_utc_time;
|
||
struct tm local_print_time;
|
||
struct tm utc_print_time;
|
||
struct tm diff_time;
|
||
gchar *utc_time = NULL;
|
||
int i, n;
|
||
|
||
if (local_time == NULL || local_time[0] == '\0')
|
||
return NULL;
|
||
|
||
n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
|
||
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
local_print_time.tm_hour = 0;
|
||
local_print_time.tm_min = 0;
|
||
local_print_time.tm_sec = 0;
|
||
|
||
if (supports_am_pm ())
|
||
end = strptime (local_time, formats_0[i], &local_print_time);
|
||
else
|
||
end = strptime (local_time, formats_1[i], &local_print_time);
|
||
|
||
if (end != NULL && end[0] == '\0')
|
||
break;
|
||
}
|
||
|
||
if (end != NULL && end[0] == '\0')
|
||
{
|
||
time_t rawtime;
|
||
time (&rawtime);
|
||
|
||
actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
|
||
actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
|
||
|
||
diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
|
||
diff_time.tm_min = actual_utc_time->tm_min - actual_local_time->tm_min;
|
||
diff_time.tm_sec = actual_utc_time->tm_sec - actual_local_time->tm_sec;
|
||
|
||
utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
|
||
utc_print_time.tm_min = ((local_print_time.tm_min + diff_time.tm_min) + 60) % 60;
|
||
utc_print_time.tm_sec = ((local_print_time.tm_sec + diff_time.tm_sec) + 60) % 60;
|
||
|
||
utc_time = g_strdup_printf ("%02d:%02d:%02d",
|
||
utc_print_time.tm_hour,
|
||
utc_print_time.tm_min,
|
||
utc_print_time.tm_sec);
|
||
}
|
||
|
||
return utc_time;
|
||
}
|
||
|
||
static void
|
||
cups_printer_get_settings_from_options (GtkPrinter *printer,
|
||
GtkPrinterOptionSet *options,
|
||
GtkPrintSettings *settings)
|
||
{
|
||
struct OptionData data;
|
||
const char *print_at, *print_at_time;
|
||
|
||
data.printer = printer;
|
||
data.options = options;
|
||
data.settings = settings;
|
||
data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
|
||
|
||
gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
|
||
if (data.ppd_file != NULL)
|
||
{
|
||
GtkPrinterOption *cover_before, *cover_after;
|
||
|
||
cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
|
||
cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
|
||
if (cover_before && cover_after)
|
||
{
|
||
char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
|
||
gtk_print_settings_set (settings, "cups-job-sheets", value);
|
||
g_free (value);
|
||
}
|
||
|
||
print_at = gtk_print_settings_get (settings, "print-at");
|
||
print_at_time = gtk_print_settings_get (settings, "print-at-time");
|
||
|
||
if (strcmp (print_at, "at") == 0)
|
||
{
|
||
gchar *utc_time = NULL;
|
||
|
||
utc_time = localtime_to_utctime (print_at_time);
|
||
|
||
if (utc_time != NULL)
|
||
{
|
||
gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
|
||
g_free (utc_time);
|
||
}
|
||
else
|
||
gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
|
||
}
|
||
else if (strcmp (print_at, "on-hold") == 0)
|
||
gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
|
||
}
|
||
}
|
||
|
||
static void
|
||
cups_printer_prepare_for_print (GtkPrinter *printer,
|
||
GtkPrintJob *print_job,
|
||
GtkPrintSettings *settings,
|
||
GtkPageSetup *page_setup)
|
||
{
|
||
GtkPrintPages pages;
|
||
GtkPageRange *ranges;
|
||
gint n_ranges;
|
||
GtkPageSet page_set;
|
||
GtkPaperSize *paper_size;
|
||
const char *ppd_paper_name;
|
||
double scale;
|
||
GtkPrintCapabilities capabilities;
|
||
|
||
capabilities = cups_printer_get_capabilities (printer);
|
||
pages = gtk_print_settings_get_print_pages (settings);
|
||
gtk_print_job_set_pages (print_job, pages);
|
||
|
||
if (pages == GTK_PRINT_PAGES_RANGES)
|
||
ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
|
||
else
|
||
{
|
||
ranges = NULL;
|
||
n_ranges = 0;
|
||
}
|
||
|
||
gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
|
||
|
||
if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
|
||
{
|
||
if (gtk_print_settings_get_collate (settings))
|
||
gtk_print_settings_set (settings, "cups-Collate", "True");
|
||
else
|
||
gtk_print_settings_set (settings, "cups-Collate", "False");
|
||
gtk_print_job_set_collate (print_job, FALSE);
|
||
}
|
||
else
|
||
{
|
||
gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
|
||
}
|
||
|
||
if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
|
||
{
|
||
if (gtk_print_settings_get_reverse (settings))
|
||
gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
|
||
gtk_print_job_set_reverse (print_job, FALSE);
|
||
}
|
||
else
|
||
{
|
||
gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
|
||
}
|
||
|
||
if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
|
||
{
|
||
if (gtk_print_settings_get_n_copies (settings) > 1)
|
||
gtk_print_settings_set_int (settings, "cups-copies",
|
||
gtk_print_settings_get_n_copies (settings));
|
||
gtk_print_job_set_num_copies (print_job, 1);
|
||
}
|
||
else
|
||
{
|
||
gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings));
|
||
}
|
||
|
||
scale = gtk_print_settings_get_scale (settings);
|
||
if (scale != 100.0)
|
||
gtk_print_job_set_scale (print_job, scale / 100.0);
|
||
|
||
page_set = gtk_print_settings_get_page_set (settings);
|
||
if (page_set == GTK_PAGE_SET_EVEN)
|
||
gtk_print_settings_set (settings, "cups-page-set", "even");
|
||
else if (page_set == GTK_PAGE_SET_ODD)
|
||
gtk_print_settings_set (settings, "cups-page-set", "odd");
|
||
gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
|
||
|
||
paper_size = gtk_page_setup_get_paper_size (page_setup);
|
||
ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
|
||
if (ppd_paper_name != NULL)
|
||
gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
|
||
else if (gtk_paper_size_is_ipp (paper_size))
|
||
gtk_print_settings_set (settings, "cups-media", gtk_paper_size_get_name (paper_size));
|
||
else
|
||
{
|
||
char width[G_ASCII_DTOSTR_BUF_SIZE];
|
||
char height[G_ASCII_DTOSTR_BUF_SIZE];
|
||
char *custom_name;
|
||
|
||
g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
|
||
g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
|
||
custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
|
||
gtk_print_settings_set (settings, "cups-PageSize", custom_name);
|
||
g_free (custom_name);
|
||
}
|
||
|
||
if (gtk_print_settings_get_number_up (settings) > 1)
|
||
{
|
||
GtkNumberUpLayout layout = gtk_print_settings_get_number_up_layout (settings);
|
||
GEnumClass *enum_class;
|
||
GEnumValue *enum_value;
|
||
|
||
switch (gtk_page_setup_get_orientation (page_setup))
|
||
{
|
||
case GTK_PAGE_ORIENTATION_PORTRAIT:
|
||
break;
|
||
case GTK_PAGE_ORIENTATION_LANDSCAPE:
|
||
if (layout < 4)
|
||
layout = layout + 2 + 4 * (1 - layout / 2);
|
||
else
|
||
layout = layout - 3 - 2 * (layout % 2);
|
||
break;
|
||
case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
|
||
layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
|
||
break;
|
||
case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
|
||
if (layout < 4)
|
||
layout = layout + 5 - 2 * (layout % 2);
|
||
else
|
||
layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
|
||
break;
|
||
}
|
||
|
||
enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
|
||
enum_value = g_enum_get_value (enum_class, layout);
|
||
gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
|
||
g_type_class_unref (enum_class);
|
||
|
||
if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
|
||
{
|
||
gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
|
||
gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
|
||
}
|
||
}
|
||
|
||
gtk_print_job_set_rotate (print_job, TRUE);
|
||
}
|
||
|
||
static GtkPageSetup *
|
||
create_page_setup (ppd_file_t *ppd_file,
|
||
ppd_size_t *size)
|
||
{
|
||
char *display_name;
|
||
GtkPageSetup *page_setup;
|
||
GtkPaperSize *paper_size;
|
||
ppd_option_t *option;
|
||
ppd_choice_t *choice;
|
||
|
||
display_name = NULL;
|
||
option = ppdFindOption (ppd_file, "PageSize");
|
||
if (option)
|
||
{
|
||
choice = ppdFindChoice (option, size->name);
|
||
if (choice)
|
||
display_name = ppd_text_to_utf8 (ppd_file, choice->text);
|
||
}
|
||
|
||
if (display_name == NULL)
|
||
display_name = g_strdup (size->name);
|
||
|
||
page_setup = gtk_page_setup_new ();
|
||
paper_size = gtk_paper_size_new_from_ppd (size->name,
|
||
display_name,
|
||
size->width,
|
||
size->length);
|
||
gtk_page_setup_set_paper_size (page_setup, paper_size);
|
||
gtk_paper_size_free (paper_size);
|
||
|
||
gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
|
||
gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
|
||
gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
|
||
gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
|
||
|
||
g_free (display_name);
|
||
|
||
return page_setup;
|
||
}
|
||
|
||
static GtkPageSetup *
|
||
create_page_setup_from_media (gchar *media,
|
||
MediaSize *media_size,
|
||
gboolean media_margin_default_set,
|
||
gint media_bottom_margin_default,
|
||
gint media_top_margin_default,
|
||
gint media_left_margin_default,
|
||
gint media_right_margin_default)
|
||
{
|
||
GtkPageSetup *page_setup;
|
||
GtkPaperSize *paper_size;
|
||
|
||
page_setup = gtk_page_setup_new ();
|
||
paper_size = gtk_paper_size_new_from_ipp (media,
|
||
POINTS_PER_INCH * (media_size->x_dimension / MM_PER_INCH),
|
||
POINTS_PER_INCH * (media_size->y_dimension / MM_PER_INCH));
|
||
gtk_page_setup_set_paper_size (page_setup, paper_size);
|
||
gtk_paper_size_free (paper_size);
|
||
|
||
if (media_margin_default_set)
|
||
{
|
||
gtk_page_setup_set_bottom_margin (page_setup, media_bottom_margin_default, GTK_UNIT_MM);
|
||
gtk_page_setup_set_top_margin (page_setup, media_top_margin_default, GTK_UNIT_MM);
|
||
gtk_page_setup_set_left_margin (page_setup, media_left_margin_default, GTK_UNIT_MM);
|
||
gtk_page_setup_set_right_margin (page_setup, media_right_margin_default, GTK_UNIT_MM);
|
||
}
|
||
|
||
return page_setup;
|
||
}
|
||
|
||
static GList *
|
||
cups_printer_list_papers (GtkPrinter *printer)
|
||
{
|
||
ppd_file_t *ppd_file;
|
||
ppd_size_t *size;
|
||
GtkPageSetup *page_setup;
|
||
GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
|
||
GList *result = NULL;
|
||
int i;
|
||
|
||
ppd_file = gtk_printer_cups_get_ppd (cups_printer);
|
||
if (ppd_file != NULL)
|
||
{
|
||
for (i = 0; i < ppd_file->num_sizes; i++)
|
||
{
|
||
size = &ppd_file->sizes[i];
|
||
|
||
page_setup = create_page_setup (ppd_file, size);
|
||
|
||
result = g_list_prepend (result, page_setup);
|
||
}
|
||
}
|
||
else if (cups_printer->media_supported != NULL &&
|
||
cups_printer->media_size_supported != NULL &&
|
||
/*
|
||
* 'media_supported' list can contain names of minimal and maximal sizes
|
||
* for which we don't create item in 'media_size_supported' list.
|
||
*/
|
||
g_list_length (cups_printer->media_supported) >=
|
||
g_list_length (cups_printer->media_size_supported))
|
||
{
|
||
MediaSize *media_size;
|
||
GList *media_iter;
|
||
GList *media_size_iter;
|
||
gchar *media;
|
||
|
||
for (media_iter = cups_printer->media_supported,
|
||
media_size_iter = cups_printer->media_size_supported;
|
||
media_size_iter != NULL;
|
||
media_iter = media_iter->next,
|
||
media_size_iter = media_size_iter->next)
|
||
{
|
||
media = (gchar *) media_iter->data;
|
||
media_size = (MediaSize *) media_size_iter->data;
|
||
|
||
page_setup = create_page_setup_from_media (media,
|
||
media_size,
|
||
cups_printer->media_margin_default_set,
|
||
cups_printer->media_bottom_margin_default,
|
||
cups_printer->media_top_margin_default,
|
||
cups_printer->media_left_margin_default,
|
||
cups_printer->media_right_margin_default);
|
||
|
||
result = g_list_prepend (result, page_setup);
|
||
}
|
||
}
|
||
|
||
result = g_list_reverse (result);
|
||
|
||
return result;
|
||
}
|
||
|
||
static GtkPageSetup *
|
||
cups_printer_get_default_page_size (GtkPrinter *printer)
|
||
{
|
||
GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
|
||
GtkPageSetup *result = NULL;
|
||
ppd_option_t *option;
|
||
ppd_file_t *ppd_file;
|
||
ppd_size_t *size;
|
||
|
||
ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
|
||
if (ppd_file != NULL)
|
||
{
|
||
option = ppdFindOption (ppd_file, "PageSize");
|
||
if (option == NULL)
|
||
return NULL;
|
||
|
||
size = ppdPageSize (ppd_file, option->defchoice);
|
||
if (size == NULL)
|
||
return NULL;
|
||
|
||
result = create_page_setup (ppd_file, size);
|
||
}
|
||
else if (cups_printer->media_default != NULL)
|
||
{
|
||
MediaSize *media_size;
|
||
GList *media_iter;
|
||
GList *media_size_iter;
|
||
gchar *media;
|
||
|
||
for (media_iter = cups_printer->media_supported,
|
||
media_size_iter = cups_printer->media_size_supported;
|
||
media_size_iter != NULL;
|
||
media_iter = media_iter->next,
|
||
media_size_iter = media_size_iter->next)
|
||
{
|
||
media = (gchar *) media_iter->data;
|
||
media_size = (MediaSize *) media_size_iter->data;
|
||
|
||
if (g_strcmp0 (cups_printer->media_default, media) == 0)
|
||
{
|
||
result = create_page_setup_from_media (media,
|
||
media_size,
|
||
cups_printer->media_margin_default_set,
|
||
cups_printer->media_bottom_margin_default,
|
||
cups_printer->media_top_margin_default,
|
||
cups_printer->media_left_margin_default,
|
||
cups_printer->media_right_margin_default);
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static gboolean
|
||
cups_printer_get_hard_margins (GtkPrinter *printer,
|
||
gdouble *top,
|
||
gdouble *bottom,
|
||
gdouble *left,
|
||
gdouble *right)
|
||
{
|
||
GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
|
||
ppd_file_t *ppd_file;
|
||
gboolean result = FALSE;
|
||
|
||
ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
|
||
if (ppd_file != NULL)
|
||
{
|
||
*left = ppd_file->custom_margins[0];
|
||
*bottom = ppd_file->custom_margins[1];
|
||
*right = ppd_file->custom_margins[2];
|
||
*top = ppd_file->custom_margins[3];
|
||
result = TRUE;
|
||
}
|
||
else if (cups_printer->media_margin_default_set)
|
||
{
|
||
*left = POINTS_PER_INCH * cups_printer->media_left_margin_default / MM_PER_INCH;
|
||
*bottom = POINTS_PER_INCH * cups_printer->media_bottom_margin_default / MM_PER_INCH;
|
||
*right = POINTS_PER_INCH * cups_printer->media_right_margin_default / MM_PER_INCH;
|
||
*top = POINTS_PER_INCH * cups_printer->media_top_margin_default / MM_PER_INCH;
|
||
result = TRUE;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static GtkPrintCapabilities
|
||
cups_printer_get_capabilities (GtkPrinter *printer)
|
||
{
|
||
GtkPrintCapabilities capabilities = 0;
|
||
GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
|
||
|
||
if (gtk_printer_cups_get_ppd (cups_printer))
|
||
{
|
||
capabilities = GTK_PRINT_CAPABILITY_REVERSE;
|
||
}
|
||
|
||
if (cups_printer->supports_copies)
|
||
{
|
||
capabilities |= GTK_PRINT_CAPABILITY_COPIES;
|
||
}
|
||
|
||
if (cups_printer->supports_collate)
|
||
{
|
||
capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
|
||
}
|
||
|
||
if (cups_printer->supports_number_up)
|
||
{
|
||
capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
|
||
GTK_PRINT_CAPABILITY_NUMBER_UP;
|
||
}
|
||
|
||
return capabilities;
|
||
}
|
||
|
||
static void
|
||
secrets_service_appeared_cb (GDBusConnection *connection,
|
||
const gchar *name,
|
||
const gchar *name_owner,
|
||
gpointer user_data)
|
||
{
|
||
GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
|
||
|
||
backend->secrets_service_available = TRUE;
|
||
}
|
||
|
||
static void
|
||
secrets_service_vanished_cb (GDBusConnection *connection,
|
||
const gchar *name,
|
||
gpointer user_data)
|
||
{
|
||
GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
|
||
|
||
backend->secrets_service_available = FALSE;
|
||
}
|