diff --git a/0001-printing-List-Avahi-printers.patch b/0001-printing-List-Avahi-printers.patch new file mode 100644 index 0000000..8000b28 --- /dev/null +++ b/0001-printing-List-Avahi-printers.patch @@ -0,0 +1,1730 @@ +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 + diff --git a/gtk2.spec b/gtk2.spec index 25d8014..899b010 100644 --- a/gtk2.spec +++ b/gtk2.spec @@ -18,7 +18,7 @@ Summary: The GIMP ToolKit (GTK+), a library for creating GUIs for X Name: gtk2 Version: 2.24.19 -Release: 2%{?dist} +Release: 3%{?dist} License: LGPLv2+ Group: System Environment/Libraries URL: http://www.gtk.org @@ -38,6 +38,9 @@ Patch8: tooltip-positioning.patch #Patch14: gtk2-landscape-pdf-print.patch # https://bugzilla.gnome.org/show_bug.cgi?id=611313 Patch15: window-dragging.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=973730 +# https://bugzilla.gnome.org/show_bug.cgi?id=702455 +Patch16: 0001-printing-List-Avahi-printers.patch BuildRequires: atk-devel >= %{atk_version} BuildRequires: glib2-devel >= %{glib2_version} @@ -151,6 +154,7 @@ This package contains developer documentation for the GTK+ widget toolkit. %patch8 -p1 -b .tooltip-positioning #%patch14 -p1 -b .landscape-pdf-print %patch15 -p1 -b .window-dragging +%patch16 -p1 -b .avahi-printers %build (if ! test -x configure; then NOCONFIGURE=1 ./autogen.sh; CONFIGFLAGS=--enable-gtk-doc; fi; @@ -337,6 +341,10 @@ fi %doc tmpdocs/examples %changelog +* Wed Jun 26 2013 Marek Kasik - 2.24.19-3 +- Backport listing of Avahi printers from gtk-3.x +- Resolves: #973730 + * Sat Jun 22 2013 Matthias Clasen - 2.24.19-2 - Trim %%changelog