+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 ( 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)
+ {
+ 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;
+ }
+ }
+
+ 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_AVAHI_BROWSING
+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
+avahi_data_free (GtkPrintBackendCups *cups_backend)
+{
+ if (cups_backend)
+ {
+ g_clear_pointer (&cups_backend->avahi_default_printer, g_free);
+ g_list_free_full (cups_backend->avahi_resolvers, g_object_unref);
+ cups_backend->avahi_resolvers = NULL;
+ g_clear_object (&cups_backend->avahi_ipp_browser);
+ g_clear_object (&cups_backend->avahi_ipps_browser);
+ g_clear_object (&cups_backend->avahi_client);
+ }
+}
+
+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);
+ }
+}
+
+static void
+avahi_resolver_found_cb (GaServiceResolver *resolver,
+ int interface,
+ GaProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ guint16 port,
+ AvahiStringList *txt,
+ glong flags,
+ gpointer *user_data)
+{
+ GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+ AvahiStringList *item;
+ gchar host[AVAHI_ADDRESS_STR_MAX];
+ gchar *suffix = NULL;
+ gchar *printer_uri;
+
+ avahi_address_snprint (host, sizeof (host), address);
+
+ item = avahi_string_list_find (txt, "rp");
+ if (item)
+ avahi_string_list_get_pair (item, NULL, &suffix, NULL);
+
+ if (suffix)
+ {
+ if (g_strcmp0 (type, "_ipp._tcp") == 0)
+ printer_uri = g_strdup_printf ("ipp://%s:%u/%s", host, port, suffix);
+ else
+ printer_uri = g_strdup_printf ("ipps://%s:%u/%s", host, port, suffix);
+
+ cups_request_avahi_printer_info (printer_uri,
+ host,
+ port,
+ name,
+ type,
+ domain,
+ backend);
+
+ g_free (printer_uri);
+ g_free (suffix);
+ }
+}
+
+static void
+avahi_browser_new_service_cb (GaServiceBrowser *browser,
+ int interface,
+ GaProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ glong flags,
+ gpointer user_data)
+{
+ GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+ GaServiceResolver *resolver;
+ GError *error = NULL;
+
+ resolver = ga_service_resolver_new (AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ name, type, domain,
+ AVAHI_PROTO_UNSPEC,
+ GA_LOOKUP_USE_MULTICAST);
+
+ g_signal_connect (resolver, "found",
+ G_CALLBACK (avahi_resolver_found_cb), user_data);
+
+ if (!ga_service_resolver_attach (resolver,
+ backend->avahi_client,
+ &error))
+ {
+ GTK_NOTE (PRINTING,
+ g_warning ("CUPS Backend: Error resolving Avahi service %s: %s",
+ name, error->message));
+ g_clear_object (&resolver);
+ g_clear_error (&error);
+ return;
+ }
+
+ backend->avahi_resolvers = g_list_append (backend->avahi_resolvers, resolver);
+}
+
+static void
+avahi_browser_removed_service_cb (GaServiceBrowser *browser,
+ int interface,
+ GaProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ glong flags,
+ gpointer user_data)
+{
+ GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+ GtkPrinterCups *printer;
+ GList *list;
+ GList *iter;
+
+ list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
+ for (iter = list; iter; iter = iter->next)
+ {
+ printer = GTK_PRINTER_CUPS (iter->data);
+ if (g_strcmp0 (printer->avahi_name, name) == 0 &&
+ g_strcmp0 (printer->avahi_type, type) == 0 &&
+ g_strcmp0 (printer->avahi_domain, domain) == 0)
+ {
+ if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
+ backend->avahi_default_printer) == 0)
+ g_clear_pointer (&backend->avahi_default_printer, g_free);
+
+ g_signal_emit_by_name (backend, "printer-removed", printer);
+ gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
+ GTK_PRINTER (printer));
+ g_signal_emit_by_name (backend, "printer-list-changed");
+ break;
+ }
+ }
+
+ g_list_free (list);
+}
+
+static gboolean
+avahi_client_renew (gpointer user_data)
+{
+ GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+ gboolean avahi_printers_removed = TRUE;
+ GList *list;
+ GList *iter;
+
+ list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
+ for (iter = list; iter; iter = iter->next)
+ if (GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
+ avahi_printers_removed = FALSE;
+
+ g_list_free (list);
+
+ if (avahi_printers_removed)
+ {
+ avahi_data_free (backend);
+ avahi_request_printer_list (backend);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+static void
+avahi_client_state_changed_cb (GaClient *ga_client,
+ GaClientState state,
+ gpointer user_data)
+{
+ GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+ GtkPrinterCups *printer;
+ gboolean list_has_changed = FALSE;
+ GError *error = NULL;
+ GList *list;
+ GList *iter;
+
+ switch (state)
+ {
+ case GA_CLIENT_STATE_FAILURE:
+ if (avahi_client_errno (ga_client->avahi_client) == AVAHI_ERR_DISCONNECTED)
+ {
+ 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 (printer->avahi_browsed)
+ {
+ g_signal_emit_by_name (backend, "printer-removed", printer);
+ gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
+ GTK_PRINTER (printer));
+ list_has_changed = TRUE;
+ }
+ }
+
+ if (list_has_changed)
+ g_signal_emit_by_name (backend, "printer-list-changed");
+
+ g_idle_add (avahi_client_renew, backend);
+
+ g_list_free (list);
+ }
+ break;
+
+ case GA_CLIENT_STATE_S_RUNNING:
+ backend->avahi_ipp_browser = ga_service_browser_new ("_ipp._tcp");
+
+ g_signal_connect (backend->avahi_ipp_browser, "new-service",
+ G_CALLBACK (avahi_browser_new_service_cb),
+ user_data);
+
+ g_signal_connect (backend->avahi_ipp_browser, "removed-service",
+ G_CALLBACK (avahi_browser_removed_service_cb),
+ user_data);
+
+ if (!ga_service_browser_attach (backend->avahi_ipp_browser,
+ backend->avahi_client,
+ &error))
+ {
+ GTK_NOTE (PRINTING,
+ g_warning ("CUPS Backend: Error getting printer list from Avahi: %s",
+ error->message));
+ g_clear_object (&backend->avahi_ipp_browser);
+ g_clear_error (&error);
+ }
+
+ backend->avahi_ipps_browser = ga_service_browser_new ("_ipps._tcp");
+
+ g_signal_connect (backend->avahi_ipps_browser, "new-service",
+ G_CALLBACK (avahi_browser_new_service_cb),
+ user_data);
+
+ g_signal_connect (backend->avahi_ipps_browser, "removed-service",
+ G_CALLBACK (avahi_browser_removed_service_cb),
+ user_data);
+
+ if (!ga_service_browser_attach (backend->avahi_ipps_browser,
+ backend->avahi_client,
+ &error))
+ {
+ GTK_NOTE (PRINTING,
+ g_warning ("CUPS Backend: Error getting printer list from Avahi: %s",
+ error->message));
+ g_clear_object (&backend->avahi_ipps_browser);
+ g_clear_error (&error);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
+{
+ GError *error = NULL;
+
+ if (!cups_backend->avahi_client)
+ {
+ cups_backend->avahi_client = ga_client_new (GA_CLIENT_FLAG_NO_FAIL);
+
+ g_signal_connect (cups_backend->avahi_client, "state-changed",
+ G_CALLBACK (avahi_client_state_changed_cb),
+ cups_backend);
+
+ if (!ga_client_start (cups_backend->avahi_client, &error))
+ {
+ GTK_NOTE (PRINTING,
+ g_warning ("CUPS Backend: Error getting printer list from Avahi: %s",
+ error->message));
+ g_clear_object (&cups_backend->avahi_client);
+ g_clear_error (&error);
+ }
+ }
+}
+#endif