From a057ed26dc623dff0fc0c62ef287f6583b2710d0 Mon Sep 17 00:00:00 2001 From: Marek Kasik Date: Tue, 25 Jun 2013 14:34:15 +0200 Subject: [PATCH] printing: List Avahi printers Show printers advertised by avahi on local network. CUPS backend now looks for _ipps._tcp and _ipp._tcp services offered by avahi. If it finds such a service (printer) it requests its attributes through IPP_GET_PRINTER_ATTRIBUTES ipp request and adds it to the list of printers. Such printer behaves like a remote printer then. If an avahi printer is a default printer then it is considered default by the backend only if there is no local or remote default printer. This functionality is enabled when building Gtk+ with CUPS 1.6 or later because it replaces browsing protocol removed in CUPS 1.6. https://bugzilla.gnome.org/show_bug.cgi?id=702455 --- configure.ac | 2 +- modules/printbackends/cups/gtkcupsutils.c | 22 + modules/printbackends/cups/gtkcupsutils.h | 3 + modules/printbackends/cups/gtkprintbackendcups.c | 1290 +++++++++++++++++++--- modules/printbackends/cups/gtkprintercups.c | 29 +- modules/printbackends/cups/gtkprintercups.h | 11 + 6 files changed, 1205 insertions(+), 152 deletions(-) diff --git a/configure.ac b/configure.ac index 5f8a787..50e27b0 100644 --- a/configure.ac +++ b/configure.ac @@ -1479,7 +1479,7 @@ else $CUPS_API_MAJOR -eq 1 -a $CUPS_API_MINOR -ge 6; then AC_DEFINE(HAVE_CUPS_API_1_6, 1, [Define to 1 if CUPS 1.6 API is available]) - + have_cups_api_1_6=yes fi AC_SUBST(CUPS_API_MAJOR) diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c index 4a3f0ad..6b10db3 100644 --- a/modules/printbackends/cups/gtkcupsutils.c +++ b/modules/printbackends/cups/gtkcupsutils.c @@ -89,6 +89,20 @@ static GtkCupsRequestStateFunc get_states[] = { #define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state #define ippGetString(attr, index, foo) attr->values[index].string.text #define ippGetCount(attr) attr->num_values + +int +ippSetVersion (ipp_t *ipp, + int major, + int minor) +{ + if (!ipp || major < 0 || minor < 0) + return 0; + + ipp->request.any.version[0] = major; + ipp->request.any.version[1] = minor; + + return 1; +} #endif static void @@ -659,6 +673,14 @@ gtk_cups_request_encode_option (GtkCupsRequest *request, } +void +gtk_cups_request_set_ipp_version (GtkCupsRequest *request, + gint major, + gint minor) +{ + ippSetVersion (request->ipp_request, major, minor); +} + static void _connect (GtkCupsRequest *request) { diff --git a/modules/printbackends/cups/gtkcupsutils.h b/modules/printbackends/cups/gtkcupsutils.h index 2adfc16..53171ed 100644 --- a/modules/printbackends/cups/gtkcupsutils.h +++ b/modules/printbackends/cups/gtkcupsutils.h @@ -182,6 +182,9 @@ gboolean gtk_cups_request_is_done (GtkCupsRequest * void gtk_cups_request_encode_option (GtkCupsRequest *request, const gchar *option, const gchar *value); +void gtk_cups_request_set_ipp_version (GtkCupsRequest *request, + gint major, + gint minor); gboolean gtk_cups_result_is_error (GtkCupsResult *result); ipp_t * gtk_cups_result_get_response (GtkCupsResult *result); GtkCupsErrorType gtk_cups_result_get_error_type (GtkCupsResult *result); diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c index 79033dd..60c2f7e 100644 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ b/modules/printbackends/cups/gtkprintbackendcups.c @@ -71,6 +71,17 @@ typedef struct _GtkPrintBackendCupsClass 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" +#endif + /* define this to see warnings about ignored ppd options */ #undef PRINT_IGNORED_OPTIONS @@ -133,6 +144,14 @@ struct _GtkPrintBackendCups GHashTable *auth; gchar *username; gboolean authentication_lock; +#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 }; static GObjectClass *backend_parent_class; @@ -199,6 +218,10 @@ void overwrite_and_free (gpointer static gboolean is_address_local (const gchar *address); static gboolean request_auth_info (gpointer data); +#ifdef HAVE_CUPS_API_1_6 +static void avahi_request_printer_list (GtkPrintBackendCups *cups_backend); +#endif + static void gtk_print_backend_cups_register_type (GTypeModule *module) { @@ -257,6 +280,15 @@ pb_module_create (void) #define ippGetName(attr) attr->name #define ippGetCount(attr) attr->num_values #define ippGetGroupTag(attr) attr->group_tag + +static int +ippGetRange (ipp_attribute_t *attr, + int element, + int *upper) +{ + *upper = attr->values[element].range.upper; + return (attr->values[element].range.lower); +} #endif /* * GtkPrintBackendCups @@ -580,7 +612,7 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend, GtkPrinterCups *cups_printer; CupsPrintStreamData *ps; CupsOptionsData *options_data; - GtkCupsRequest *request; + GtkCupsRequest *request = NULL; GtkPrintSettings *settings; const gchar *title; char printer_absolute_uri[HTTP_MAX_URI]; @@ -599,23 +631,82 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend, cups_printer->device_uri, GTK_PRINT_BACKEND_CUPS (print_backend)->username); +#ifdef HAVE_CUPS_API_1_6 + if (cups_printer->avahi_browsed) + { + http_t *http; + + 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); + #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1 - 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))); + 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))); #else - g_snprintf (printer_absolute_uri, - sizeof (printer_absolute_uri), - "ipp://localhost:%d/printers/%s", - ippPort (), - gtk_printer_get_name (gtk_print_job_get_printer (job))); + g_snprintf (printer_absolute_uri, + sizeof (printer_absolute_uri), + "ipp://localhost:%d/printers/%s", + ippPort (), + gtk_printer_get_name (gtk_print_job_get_printer (job))); #endif + } + + 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", @@ -663,6 +754,10 @@ void overwrite_and_free (gpointer data) 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; @@ -681,6 +776,17 @@ gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups) backend_cups->username = NULL; +#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); } @@ -707,6 +813,12 @@ gtk_print_backend_cups_finalize (GObject *object) g_free (backend_cups->username); +#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 + backend_parent_class->finalize (object); } @@ -714,6 +826,9 @@ 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)); @@ -729,6 +844,44 @@ gtk_print_backend_cups_dispose (GObject *object) 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); } @@ -1716,6 +1869,30 @@ static const char * printer_strings[] = N_("There is a problem on printer '%s'.") }; +/* 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" + }; + typedef enum { GTK_PRINTER_STATE_LEVEL_NONE = 0, @@ -1743,10 +1920,88 @@ typedef struct gboolean default_printer; gboolean got_printer_type; gboolean remote_printer; +#ifdef HAVE_CUPS_API_1_6 + gboolean avahi_printer; +#endif gchar **auth_info_required; + guchar ipp_version_major; + guchar ipp_version_minor; + gboolean supports_copies; + gboolean supports_collate; + gboolean supports_number_up; } PrinterSetupInfo; 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) @@ -1867,6 +2122,63 @@ cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend, info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL)); } } + 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 { GTK_NOTE (PRINTING, @@ -1955,22 +2267,722 @@ cups_create_printer (GtkPrintBackendCups *cups_backend, 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 void +set_info_state_message (PrinterSetupInfo *info) +{ + gint i; + + if (info->state_msg && 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 = g_strdup_printf (printer_strings[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 +typedef struct +{ + gchar *name; + gchar *type; + gchar *domain; + gchar *host; + gint port; +} AvahiService; + +void +avahi_service_free (AvahiService *service) +{ + if (service) + { + g_free (service->name); + g_free (service->type); + g_free (service->domain); + g_free (service->host); + g_free (service); + } +} + +static void +cups_request_avahi_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; + AvahiService *service = (AvahiService *) user_data; + GtkPrinter *printer; + gboolean list_has_changed = FALSE; + 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) + { + info->avahi_printer = TRUE; + + if (info->got_printer_type && + info->default_printer && + cups_backend->avahi_default_printer == NULL) + cups_backend->avahi_default_printer = g_strdup (info->printer_name); + + set_info_state_message (info); + + 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_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; + } + + GTK_PRINTER_CUPS (printer)->remote = info->remote_printer; + GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (service->name); + GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (service->type); + GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (service->domain); + GTK_PRINTER_CUPS (printer)->hostname = g_strdup (service->host); + GTK_PRINTER_CUPS (printer)->port = service->port; + 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; + 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); + + if (gtk_printer_is_new (printer)) + { + g_signal_emit_by_name (backend, "printer-added", printer); + gtk_printer_set_is_new (printer, FALSE); + } + + 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 (list_has_changed) + g_signal_emit_by_name (backend, "printer-list-changed"); + + 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); + } + + g_slice_free (PrinterSetupInfo, info); + + gdk_threads_leave (); +} + +static void +cups_request_avahi_printer_info (const gchar *printer_uri, + const gchar *host, + gint port, + const gchar *name, + const gchar *type, + const gchar *domain, + GtkPrintBackendCups *backend) +{ + GtkCupsRequest *request; + AvahiService *service; + http_t *http; + + http = httpConnect (host, port); + if (http) + { + service = (AvahiService *) g_new0 (AvahiService, 1); + service->name = g_strdup (name); + service->type = g_strdup (type); + service->domain = g_strdup (domain); + service->host = g_strdup (host); + service->port = port; + + 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), + NULL, printer_attrs); + + cups_request_execute (backend, + request, + (GtkPrintCupsResponseCallbackFunc) cups_request_avahi_printer_info_cb, + service, + (GDestroyNotify) avahi_service_free); + } +} + +typedef struct +{ + gchar *printer_uri; + gchar *host; + gint port; + gchar *name; + gchar *type; + gchar *domain; + GtkPrintBackendCups *backend; +} AvahiConnectionTestData; + +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); + + cups_request_avahi_printer_info (data->printer_uri, + data->host, + data->port, + data->name, + data->type, + data->domain, + data->backend); + } + + g_free (data->printer_uri); + g_free (data->host); + g_free (data->name); + g_free (data->type); + g_free (data->domain); + g_free (data); +} + +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 *suffix = NULL; + gchar *tmp; + gint interface; + gint protocol; + gint aprotocol; + gint i, j; + + 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); + + for (i = 0; i < g_variant_n_children (txt); i++) + { + child = g_variant_get_child_value (txt, i); + + tmp = g_new0 (gchar, g_variant_n_children (child) + 1); + for (j = 0; j < g_variant_n_children (child); j++) + { + tmp[j] = g_variant_get_byte (g_variant_get_child_value (child, j)); + } + + if (g_str_has_prefix (tmp, "rp=")) + { + suffix = g_strdup (tmp + 3); + g_free (tmp); + break; + } + + g_free (tmp); + } + + if (suffix) + { + if (g_strcmp0 (type, "_ipp._tcp") == 0) + protocol_string = "ipp"; + else + protocol_string = "ipps"; + + data = g_new0 (AvahiConnectionTestData, 1); + + if (aprotocol == AVAHI_PROTO_INET6) + data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, suffix); + else + data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, suffix); + + 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 (suffix); + } + + 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) + { + GtkPrinterCups *printer; + GList *list; + GList *iter; + + 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) + { + 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) @@ -1981,6 +2993,7 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, gboolean list_has_changed; GList *removed_printer_checklist; gchar *remote_default_printer = NULL; + GList *iter; GDK_THREADS_ENTER (); @@ -2025,7 +3038,6 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, GtkPrinter *printer; gboolean status_changed = FALSE; GList *node; - gint i; PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo); /* Skip leading attributes until we hit a printer... @@ -2046,7 +3058,6 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, GtkPrinter *printer; gboolean status_changed = FALSE; GList *node; - gint i; PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo); /* Skip leading attributes until we hit a printer... @@ -2137,88 +3148,22 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, #endif 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; 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); - 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 ( N_("Paused ; Rejecting Jobs")); - if (info->is_paused && info->is_accepting_jobs) - /* Translators: this is a printer status. */ - tmp_msg2 = g_strdup ( N_("Paused")); - if (!info->is_paused && !info->is_accepting_jobs) - /* Translators: this is a printer status. */ - tmp_msg2 = g_strdup ( N_("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 != NULL)) - { - 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 = g_strdup_printf (printer_strings[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 (strlen (info->state_msg) == 0) - { - g_free (info->state_msg); - info->state_msg = reason_msg_desc; - reason_msg_desc = NULL; - } - else - { - gchar *tmp_msg = NULL; - tmp_msg = g_strjoin (" ; ", info->state_msg, - reason_msg_desc, NULL); - g_free (info->state_msg); - info->state_msg = tmp_msg; - } - } - if (reason_msg_desc != NULL) - g_free (reason_msg_desc); - } + 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 according to importance - (none, report, warning, error - report is omitted). */ - if (info->reason_level == 3) - gtk_printer_set_icon_name (printer, "gtk-print-error"); - else if (info->reason_level == 2) - gtk_printer_set_icon_name (printer, "gtk-print-warning"); - else if (gtk_printer_is_paused (printer)) - gtk_printer_set_icon_name (printer, "gtk-print-paused"); - else - gtk_printer_set_icon_name (printer, "gtk-print"); + set_printer_icon_name_from_info (printer, info); if (status_changed) g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), @@ -2237,9 +3182,18 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, as inactive if it is in the list, emitting a printer_removed signal */ if (removed_printer_checklist != NULL) { - g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, backend); + 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); - list_has_changed = TRUE; } done: @@ -2250,23 +3204,16 @@ done: if (!cups_backend->got_default_printer && remote_default_printer != NULL) { - cups_backend->default_printer = g_strdup (remote_default_printer); - cups_backend->got_default_printer = TRUE; + set_default_printer (cups_backend, remote_default_printer); g_free (remote_default_printer); + } - 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 + 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 (); } @@ -2292,23 +3239,6 @@ cups_request_printer_list (GtkPrintBackendCups *cups_backend) { GtkCupsConnectionState state; GtkCupsRequest *request; - static const char * const pattrs[] = /* Attributes we're interested in */ - { - "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" - }; if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending) return TRUE; @@ -2345,8 +3275,8 @@ cups_request_printer_list (GtkPrintBackendCups *cups_backend) cups_backend->username); gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", G_N_ELEMENTS (pattrs), - NULL, pattrs); + "requested-attributes", G_N_ELEMENTS (printer_attrs), + NULL, printer_attrs); cups_request_execute (cups_backend, request, @@ -2373,6 +3303,10 @@ cups_get_printer_list (GtkPrintBackend *backend) cups_backend->list_printers_poll = gdk_threads_add_timeout (50, (GSourceFunc) cups_request_printer_list, backend); + +#ifdef HAVE_CUPS_API_1_6 + avahi_request_printer_list (cups_backend); +#endif } } @@ -2414,10 +3348,15 @@ cups_request_ppd_cb (GtkPrintBackendCups *print_backend, { 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)) + /* If we get a 404 then it is just a raw printer without a ppd + and not an error. Standalone Avahi printers also don't have + PPD files. */ + if (((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) && + (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND)) +#ifdef HAVE_CUPS_API_1_6 + || GTK_PRINTER_CUPS (printer)->avahi_browsed +#endif + ) { gtk_printer_set_has_details (printer, TRUE); success = TRUE; @@ -2550,6 +3489,10 @@ cups_request_ppd (GtkPrinter *printer) 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)); @@ -4501,11 +5444,10 @@ cups_printer_get_settings_from_options (GtkPrinter *printer, 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; - - gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data); cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before"); cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after"); @@ -4548,7 +5490,9 @@ cups_printer_prepare_for_print (GtkPrinter *printer, GtkPaperSize *paper_size; const char *ppd_paper_name; double scale; + GtkPrintCapabilities capabilities; + capabilities = cups_printer_get_capabilities (printer); print_job->print_pages = gtk_print_settings_get_print_pages (settings); print_job->page_ranges = NULL; print_job->num_page_ranges = 0; @@ -4558,18 +5502,39 @@ cups_printer_prepare_for_print (GtkPrinter *printer, gtk_print_settings_get_page_ranges (settings, &print_job->num_page_ranges); - if (gtk_print_settings_get_collate (settings)) - gtk_print_settings_set (settings, "cups-Collate", "True"); - print_job->collate = FALSE; + if (capabilities & GTK_PRINT_CAPABILITY_COLLATE) + { + if (gtk_print_settings_get_collate (settings)) + gtk_print_settings_set (settings, "cups-Collate", "True"); + print_job->collate = FALSE; + } + else + { + print_job->collate = gtk_print_settings_get_collate (settings); + } - if (gtk_print_settings_get_reverse (settings)) - gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse"); - print_job->reverse = FALSE; + if (capabilities & GTK_PRINT_CAPABILITY_REVERSE) + { + if (gtk_print_settings_get_reverse (settings)) + gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse"); + print_job->reverse = FALSE; + } + else + { + print_job->reverse = gtk_print_settings_get_reverse (settings); + } - if (gtk_print_settings_get_n_copies (settings) > 1) - gtk_print_settings_set_int (settings, "cups-copies", - gtk_print_settings_get_n_copies (settings)); - print_job->num_copies = 1; + 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)); + print_job->num_copies = 1; + } + else + { + print_job->num_copies = gtk_print_settings_get_n_copies (settings); + } scale = gtk_print_settings_get_scale (settings); print_job->scale = 1.0; @@ -4631,6 +5596,12 @@ cups_printer_prepare_for_print (GtkPrinter *printer, 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)) + { + print_job->number_up = gtk_print_settings_get_number_up (settings); + print_job->number_up_layout = gtk_print_settings_get_number_up_layout (settings); + } } print_job->rotate_to_orientation = TRUE; @@ -4750,12 +5721,31 @@ cups_printer_get_hard_margins (GtkPrinter *printer, static GtkPrintCapabilities cups_printer_get_capabilities (GtkPrinter *printer) { - return - GTK_PRINT_CAPABILITY_COPIES | - GTK_PRINT_CAPABILITY_COLLATE | - GTK_PRINT_CAPABILITY_REVERSE | + 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; #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 1 && CUPS_VERSION_PATCH >= 15) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || CUPS_VERSION_MAJOR > 1 - GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT | + capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT; #endif - GTK_PRINT_CAPABILITY_NUMBER_UP; + } + + return capabilities; } diff --git a/modules/printbackends/cups/gtkprintercups.c b/modules/printbackends/cups/gtkprintercups.c index 8b3f85c..f957558 100644 --- a/modules/printbackends/cups/gtkprintercups.c +++ b/modules/printbackends/cups/gtkprintercups.c @@ -82,6 +82,17 @@ gtk_printer_cups_init (GtkPrinterCups *printer) printer->get_remote_ppd_attempts = 0; printer->remote_cups_connection_test = NULL; printer->auth_info_required = NULL; +#ifdef HAVE_CUPS_API_1_6 + printer->avahi_browsed = FALSE; + printer->avahi_name = NULL; + printer->avahi_type = NULL; + printer->avahi_domain = NULL; +#endif + printer->ipp_version_major = 1; + printer->ipp_version_minor = 1; + printer->supports_copies = FALSE; + printer->supports_collate = FALSE; + printer->supports_number_up = FALSE; } static void @@ -101,6 +112,12 @@ gtk_printer_cups_finalize (GObject *object) g_free (printer->default_cover_after); g_strfreev (printer->auth_info_required); +#ifdef HAVE_CUPS_API_1_6 + g_free (printer->avahi_name); + g_free (printer->avahi_type); + g_free (printer->avahi_domain); +#endif + if (printer->ppd_file) ppdClose (printer->ppd_file); @@ -128,6 +145,7 @@ gtk_printer_cups_new (const char *name, { GObject *result; gboolean accepts_pdf; + GtkPrinterCups *printer; #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1 accepts_pdf = TRUE; @@ -142,7 +160,16 @@ gtk_printer_cups_new (const char *name, "accepts-pdf", accepts_pdf, NULL); - return (GtkPrinterCups *) result; + printer = GTK_PRINTER_CUPS (result); + + /* + * IPP version 1.1 has to be supported + * by all implementations according to rfc 2911 + */ + printer->ipp_version_major = 1; + printer->ipp_version_minor = 1; + + return printer; } ppd_file_t * diff --git a/modules/printbackends/cups/gtkprintercups.h b/modules/printbackends/cups/gtkprintercups.h index 6868232..d10bd7e 100644 --- a/modules/printbackends/cups/gtkprintercups.h +++ b/modules/printbackends/cups/gtkprintercups.h @@ -62,6 +62,17 @@ struct _GtkPrinterCups guint get_remote_ppd_poll; gint get_remote_ppd_attempts; GtkCupsConnectionTest *remote_cups_connection_test; +#ifdef HAVE_CUPS_API_1_6 + gboolean avahi_browsed; + gchar *avahi_name; + gchar *avahi_type; + gchar *avahi_domain; +#endif + guchar ipp_version_major; + guchar ipp_version_minor; + gboolean supports_copies; + gboolean supports_collate; + gboolean supports_number_up; }; struct _GtkPrinterCupsClass -- 1.8.2.1