/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright 2009-2010 Red Hat, Inc, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "pp-new-printer-dialog.h" #include "pp-utils.h" #include #include #ifdef GDK_WINDOWING_X11 #include #endif #define MECHANISM_BUS "org.opensuse.CupsPkHelper.Mechanism" #define PACKAGE_KIT_BUS "org.freedesktop.PackageKit" #define PACKAGE_KIT_PATH "/org/freedesktop/PackageKit" #define PACKAGE_KIT_IFACE "org.freedesktop.PackageKit.Modify" #define FIREWALLD_BUS "org.fedoraproject.FirewallD" #define FIREWALLD_PATH "/org/fedoraproject/FirewallD" #define FIREWALLD_IFACE "org.fedoraproject.FirewallD" #define ALLOWED_CHARACTERS "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/" static void pp_new_printer_dialog_hide (PpNewPrinterDialog *pp); static void actualize_devices_list (PpNewPrinterDialog *pp); enum { NOTEBOOK_LOCAL_PAGE = 0, NOTEBOOK_NETWORK_PAGE, NOTEBOOK_N_PAGES }; enum { DEVICE_TYPE_ID_COLUMN = 0, DEVICE_TYPE_NAME_COLUMN, DEVICE_TYPE_TYPE_COLUMN, DEVICE_TYPE_N_COLUMNS }; enum { DEVICE_ID_COLUMN = 0, DEVICE_NAME_COLUMN, DEVICE_N_COLUMNS }; enum { DEVICE_TYPE_LOCAL = 0, DEVICE_TYPE_NETWORK, DEVICE_TYPE_N }; typedef struct{ gchar *device_class; gchar *device_id; gchar *device_info; gchar *device_make_and_model; gchar *device_uri; gchar *device_location; gchar *device_ppd_uri; gchar *display_name; gchar *hostname; gint host_port; gboolean show; gboolean found; } CupsDevice; struct _PpNewPrinterDialog { GtkBuilder *builder; GtkWidget *parent; GtkWidget *dialog; gchar **device_connection_types; gint num_device_connection_types; CupsDevice *devices; gint num_devices; UserResponseCallback user_callback; gpointer user_data; GCancellable *cancellable; gchar *warning; gboolean show_warning; }; static void show_notification (gchar *primary_text, gchar *secondary_text, gchar *icon_name) { if (primary_text) { NotifyNotification *notification; notification = notify_notification_new (primary_text, secondary_text, icon_name); notify_notification_set_app_name (notification, _("Printers")); notify_notification_set_hint (notification, "transient", g_variant_new_boolean (TRUE)); notify_notification_show (notification, NULL); g_object_unref (notification); } } static void device_type_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data) { PpNewPrinterDialog *pp = (PpNewPrinterDialog *) user_data; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *treeview = NULL; GtkWidget *widget; gchar *device_type_name = NULL; gint device_type_id = -1; gint device_type = -1; if (gtk_tree_selection_get_selected (selection, &model, &iter)) { gtk_tree_model_get (model, &iter, DEVICE_TYPE_ID_COLUMN, &device_type_id, DEVICE_TYPE_NAME_COLUMN, &device_type_name, DEVICE_TYPE_TYPE_COLUMN, &device_type, -1); } if (device_type >= 0) { widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-type-notebook"); if (pp->show_warning && device_type == DEVICE_TYPE_NETWORK) gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 2); else gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), device_type); if (device_type == DEVICE_TYPE_LOCAL) treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "local-devices-treeview"); else if (device_type == DEVICE_TYPE_NETWORK) treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "network-devices-treeview"); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "new-printer-add-button"); if (treeview) gtk_widget_set_sensitive (widget, gtk_tree_selection_get_selected ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter)); } } static void device_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data) { PpNewPrinterDialog *pp = (PpNewPrinterDialog *) user_data; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *treeview = NULL; GtkWidget *widget; gchar *device_type_name = NULL; gint device_type_id = -1; gint device_type = -1; treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-types-treeview"); if (gtk_tree_selection_get_selected ( gtk_tree_view_get_selection ( GTK_TREE_VIEW (treeview)), &model, &iter)) gtk_tree_model_get (model, &iter, DEVICE_TYPE_ID_COLUMN, &device_type_id, DEVICE_TYPE_NAME_COLUMN, &device_type_name, DEVICE_TYPE_TYPE_COLUMN, &device_type, -1); if (device_type == DEVICE_TYPE_LOCAL) treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "local-devices-treeview"); else if (device_type == DEVICE_TYPE_NETWORK) treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "network-devices-treeview"); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "new-printer-add-button"); if (treeview) gtk_widget_set_sensitive (widget, gtk_tree_selection_get_selected ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter)); } static void free_devices (PpNewPrinterDialog *pp) { int i; for (i = 0; i < pp->num_devices; i++) { g_free (pp->devices[i].device_class); g_free (pp->devices[i].device_id); g_free (pp->devices[i].device_info); g_free (pp->devices[i].device_make_and_model); g_free (pp->devices[i].device_uri); g_free (pp->devices[i].device_location); g_free (pp->devices[i].device_ppd_uri); g_free (pp->devices[i].display_name); g_free (pp->devices[i].hostname); } pp->num_devices = 0; pp->devices = NULL; } static void store_device_parameter (gpointer key, gpointer value, gpointer user_data) { PpNewPrinterDialog *pp = (PpNewPrinterDialog *) user_data; gchar *cut; gint index = -1; cut = g_strrstr ((gchar *)key, ":"); if (cut) index = atoi ((gchar *)cut + 1); if (index >= 0) { if (g_str_has_prefix ((gchar *)key, "device-class")) pp->devices[index].device_class = g_strdup ((gchar *)value); else if (g_str_has_prefix ((gchar *)key, "device-id")) pp->devices[index].device_id = g_strdup ((gchar *)value); else if (g_str_has_prefix ((gchar *)key, "device-info")) pp->devices[index].device_info = g_strdup ((gchar *)value); else if (g_str_has_prefix ((gchar *)key, "device-make-and-model")) pp->devices[index].device_make_and_model = g_strdup ((gchar *)value); else if (g_str_has_prefix ((gchar *)key, "device-uri")) pp->devices[index].device_uri = g_strdup ((gchar *)value); else if (g_str_has_prefix ((gchar *)key, "device-location")) pp->devices[index].device_location = g_strdup ((gchar *)value); } } static void devices_get_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { PpNewPrinterDialog *pp = user_data; cups_dest_t *dests; GHashTable *devices = NULL; GtkWidget *widget = NULL; GVariant *dg_output = NULL; gboolean already_present; GError *error = NULL; gchar *new_name = NULL; char *ret_error = NULL; gint i, j; gint name_index; gint num_dests; dg_output = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { if (dg_output && g_variant_n_children (dg_output) == 2) { GVariant *devices_variant = NULL; g_variant_get (dg_output, "(&s@a{ss})", &ret_error, &devices_variant); if (devices_variant) { if (g_variant_is_of_type (devices_variant, G_VARIANT_TYPE ("a{ss}"))) { GVariantIter *iter; GVariant *item; g_variant_get (devices_variant, "a{ss}", &iter); devices = g_hash_table_new (g_str_hash, g_str_equal); while ((item = g_variant_iter_next_value (iter))) { gchar *key; gchar *value; g_variant_get (item, "{ss}", &key, &value); g_hash_table_insert (devices, key, value); } } g_variant_unref (devices_variant); } g_variant_unref (dg_output); } g_object_unref (source_object); if (error || (ret_error && ret_error[0] != '\0')) { if (error) g_warning ("%s", error->message); if (ret_error && ret_error[0] != '\0') g_warning ("%s", ret_error); } free_devices (pp); if (devices) { GList *keys; GList *iter; gchar *cut; gint max_index = -1; gint index; keys = g_hash_table_get_keys (devices); for (iter = keys; iter; iter = iter->next) { index = -1; cut = g_strrstr ((gchar *)iter->data, ":"); if (cut) index = atoi (cut + 1); if (index > max_index) max_index = index; } if (max_index >= 0) { pp->num_devices = max_index + 1; pp->devices = g_new0 (CupsDevice, pp->num_devices); g_hash_table_foreach (devices, store_device_parameter, pp); /* Assign names to devices */ for (i = 0; i < pp->num_devices; i++) { gchar *name = NULL; if (pp->devices[i].device_id) { name = get_tag_value (pp->devices[i].device_id, "mdl"); if (!name) name = get_tag_value (pp->devices[i].device_id, "model"); if (name) name = g_strcanon (name, ALLOWED_CHARACTERS, '-'); } if (!name && pp->devices[i].device_info) { name = g_strdup (pp->devices[i].device_info); if (name) name = g_strcanon (name, ALLOWED_CHARACTERS, '-'); } name_index = 2; already_present = FALSE; num_dests = cupsGetDests (&dests); do { if (already_present) { new_name = g_strdup_printf ("%s-%d", name, name_index); name_index++; } else new_name = g_strdup (name); already_present = FALSE; for (j = 0; j < num_dests; j++) if (g_strcmp0 (dests[j].name, new_name) == 0) already_present = TRUE; if (already_present) g_free (new_name); else { g_free (name); name = new_name; } } while (already_present); cupsFreeDests (num_dests, dests); pp->devices[i].display_name = name; } /* Set show bool * Don't show duplicates. * Show devices with device-id. * Other preferences should apply here. */ for (i = 0; i < pp->num_devices; i++) { for (j = 0; j < pp->num_devices; j++) { if (i != j) { if (g_strcmp0 (pp->devices[i].display_name, pp->devices[j].display_name) == 0) { if (pp->devices[i].device_id && !pp->devices[j].show) { pp->devices[i].show = TRUE; } } } } } } g_hash_table_destroy (devices); actualize_devices_list (pp); } widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "get-devices-status-label"); gtk_label_set_text (GTK_LABEL (widget), " "); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "spinner"); gtk_spinner_stop (GTK_SPINNER (widget)); gtk_widget_set_sensitive (widget, FALSE); if (pp->cancellable != NULL) { g_object_unref (pp->cancellable); pp->cancellable = NULL; } g_clear_error (&error); } } static void devices_get (PpNewPrinterDialog *pp) { GDBusProxy *proxy; GError *error = NULL; GVariant *dg_input = NULL; GVariantBuilder *in_include = NULL; GVariantBuilder *in_exclude = NULL; GtkWidget *widget = NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, MECHANISM_BUS, "/", MECHANISM_BUS, NULL, &error); if (proxy) { if (pp->show_warning) { widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-type-notebook"); gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 2); } in_include = g_variant_builder_new (G_VARIANT_TYPE ("as")); in_exclude = g_variant_builder_new (G_VARIANT_TYPE ("as")); dg_input = g_variant_new ("(iiasas)", 0, 60, in_include, in_exclude); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "get-devices-status-label"); gtk_label_set_text (GTK_LABEL (widget), _("Getting devices...")); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "spinner"); gtk_spinner_start (GTK_SPINNER (widget)); gtk_widget_set_sensitive (widget, TRUE); pp->cancellable = g_cancellable_new (); g_dbus_proxy_call (proxy, "DevicesGet", dg_input, G_DBUS_CALL_FLAGS_NONE, 60000, pp->cancellable, devices_get_cb, pp); g_variant_builder_unref (in_exclude); g_variant_builder_unref (in_include); g_variant_unref (dg_input); } } static gchar ** line_split (gchar *line) { gboolean escaped = FALSE; gboolean quoted = FALSE; gboolean in_word = FALSE; gchar **words = NULL; gchar **result = NULL; gchar *buffer = NULL; gchar ch; gint n = 0; gint i, j = 0, k = 0; if (line) { n = strlen (line); words = g_new0 (gchar *, n + 1); buffer = g_new0 (gchar, n + 1); for (i = 0; i < n; i++) { ch = line[i]; if (escaped) { buffer[k++] = ch; escaped = FALSE; continue; } if (ch == '\\') { in_word = TRUE; escaped = TRUE; continue; } if (in_word) { if (quoted) { if (ch == '"') quoted = FALSE; else buffer[k++] = ch; } else if (g_ascii_isspace (ch)) { words[j++] = g_strdup (buffer); memset (buffer, 0, n + 1); k = 0; in_word = FALSE; } else if (ch == '"') quoted = TRUE; else buffer[k++] = ch; } else { if (ch == '"') { in_word = TRUE; quoted = TRUE; } else if (!g_ascii_isspace (ch)) { in_word = TRUE; buffer[k++] = ch; } } } } if (buffer[0] != '\0') words[j++] = g_strdup (buffer); result = g_strdupv (words); g_strfreev (words); g_free (buffer); return result; } static void service_enable (gchar *service_name, gint service_timeout) { GDBusProxy *proxy; GVariant *input = NULL; GVariant *output = NULL; GError *error = NULL; gint result; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, FIREWALLD_BUS, FIREWALLD_PATH, FIREWALLD_IFACE, NULL, &error); if (proxy) { input = g_variant_new ("(si)", service_name, service_timeout); output = g_dbus_proxy_call_sync (proxy, "enableService", input, G_DBUS_CALL_FLAGS_NONE, 60000, NULL, &error); if (output && g_variant_n_children (output) == 1) g_variant_get (output, "(i)", &result); if (output) g_variant_unref (output); g_variant_unref (input); g_object_unref (proxy); } } static void service_disable (gchar *service_name) { GDBusProxy *proxy; GVariant *input = NULL; GVariant *output = NULL; GError *error = NULL; gint result; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, FIREWALLD_BUS, FIREWALLD_PATH, FIREWALLD_IFACE, NULL, &error); if (proxy) { input = g_variant_new ("(s)", service_name); output = g_dbus_proxy_call_sync (proxy, "disableService", input, G_DBUS_CALL_FLAGS_NONE, 60000, NULL, &error); if (output && g_variant_n_children (output) == 1) g_variant_get (output, "(i)", &result); if (output) g_variant_unref (output); g_variant_unref (input); g_object_unref (proxy); } } static gboolean service_enabled (gchar *service_name) { GDBusProxy *proxy; GVariant *input = NULL; GVariant *output = NULL; GError *error = NULL; gint query_result = 0; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, FIREWALLD_BUS, FIREWALLD_PATH, FIREWALLD_IFACE, NULL, &error); if (proxy) { input = g_variant_new ("(s)", service_name); output = g_dbus_proxy_call_sync (proxy, "queryService", input, G_DBUS_CALL_FLAGS_NONE, 60000, NULL, &error); if (output && g_variant_n_children (output) == 1) g_variant_get (output, "(i)", &query_result); if (output) g_variant_unref (output); g_variant_unref (input); g_object_unref (proxy); } if (query_result > 0) return TRUE; else return FALSE; } static gboolean dbus_method_available (gchar *name, gchar *path, gchar *iface, gchar *method) { GDBusProxy *proxy; GError *error = NULL; GVariant *output = NULL; gboolean result = FALSE; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, name, path, iface, NULL, NULL); if (proxy) { output = g_dbus_proxy_call_sync (proxy, method, NULL, G_DBUS_CALL_FLAGS_NONE, 60000, NULL, &error); if (error && error->domain == G_DBUS_ERROR && error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) result = FALSE; else result = TRUE; if (output) g_variant_unref (output); g_object_unref (proxy); } return result; } static void search_address_cb (GtkToggleButton *togglebutton, gpointer user_data) { PpNewPrinterDialog *pp = (PpNewPrinterDialog*) user_data; GtkWidget *widget; gint i; widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "search-by-address-checkbutton"); if (widget && gtk_toggle_button_get_active (togglebutton)) { gchar *uri = NULL; widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "address-entry"); uri = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget))); if (uri && uri[0] != '\0') { cups_dest_t *dests = NULL; http_t *http; GError *error = NULL; gchar *tmp = NULL; gchar *host = NULL; gchar *port_string = NULL; gchar *position; gchar *command; gchar *standard_output = NULL; gint exit_status = -1; gint num_dests = 0; gint length; int port = 631; if (g_strrstr (uri, "://")) tmp = g_strrstr (uri, "://") + 3; else tmp = uri; if (g_strrstr (tmp, "@")) tmp = g_strrstr (tmp, "@") + 1; if ((position = g_strrstr (tmp, "/"))) { *position = '\0'; host = g_strdup (tmp); *position = '/'; } else host = g_strdup (tmp); if ((position = g_strrstr (host, ":"))) { *position = '\0'; port_string = position + 1; } if (port_string) port = atoi (port_string); if (host) { /* Use CUPS to get printer's informations */ http = httpConnectEncrypt (host, port, cupsEncryption ()); if (http) { gchar *device_uri = NULL; gchar *device_ppd_uri = NULL; num_dests = cupsGetDests2 (http, &dests); if (num_dests > 0) { CupsDevice *devices = NULL; devices = g_new0 (CupsDevice, pp->num_devices + num_dests); for (i = 0; i < pp->num_devices; i++) { devices[i] = pp->devices[i]; pp->devices[i].device_class = NULL; pp->devices[i].device_id = NULL; pp->devices[i].device_info = NULL; pp->devices[i].device_make_and_model = NULL; pp->devices[i].device_uri = NULL; pp->devices[i].device_location = NULL; pp->devices[i].device_ppd_uri = NULL; pp->devices[i].display_name = NULL; pp->devices[i].hostname = NULL; } g_free (pp->devices); pp->devices = devices; for (i = 0; i < num_dests; i++) { device_uri = g_strdup_printf ("ipp://%s:%d/printers/%s", host, port, dests[i].name); device_ppd_uri = g_strdup_printf ("%s.ppd", device_uri); pp->devices[pp->num_devices + i].device_class = g_strdup ("network"); pp->devices[pp->num_devices + i].device_uri = device_uri; pp->devices[pp->num_devices + i].display_name = g_strdup (dests[i].name); pp->devices[pp->num_devices + i].device_ppd_uri = device_ppd_uri; pp->devices[pp->num_devices + i].show = TRUE; pp->devices[pp->num_devices + i].hostname = g_strdup (host); pp->devices[pp->num_devices + i].host_port = port; pp->devices[pp->num_devices + i].found = TRUE; } pp->num_devices += num_dests; } httpClose (http); } /* Use SNMP to get printer's informations */ command = g_strdup_printf ("/usr/lib/cups/backend/snmp %s", host); if (g_spawn_command_line_sync (command, &standard_output, NULL, &exit_status, &error)) { if (exit_status == 0 && standard_output) { gchar **printer_informations = NULL; printer_informations = line_split (standard_output); length = g_strv_length (printer_informations); if (length >= 4) { CupsDevice *devices = NULL; devices = g_new0 (CupsDevice, pp->num_devices + 1); for (i = 0; i < pp->num_devices; i++) { devices[i] = pp->devices[i]; pp->devices[i].device_class = NULL; pp->devices[i].device_id = NULL; pp->devices[i].device_info = NULL; pp->devices[i].device_make_and_model = NULL; pp->devices[i].device_uri = NULL; pp->devices[i].device_location = NULL; pp->devices[i].device_ppd_uri = NULL; pp->devices[i].display_name = NULL; pp->devices[i].hostname = NULL; } g_free (pp->devices); pp->devices = devices; pp->devices[pp->num_devices].device_class = g_strdup (printer_informations[0]); pp->devices[pp->num_devices].device_uri = g_strdup (printer_informations[1]); pp->devices[pp->num_devices].device_make_and_model = g_strdup (printer_informations[2]); pp->devices[pp->num_devices].device_info = g_strdup (printer_informations[3]); pp->devices[pp->num_devices].display_name = g_strdup (printer_informations[3]); pp->devices[pp->num_devices].display_name = g_strcanon (pp->devices[pp->num_devices].display_name, ALLOWED_CHARACTERS, '-'); pp->devices[pp->num_devices].show = TRUE; pp->devices[pp->num_devices].hostname = g_strdup (host); pp->devices[pp->num_devices].host_port = port; pp->devices[pp->num_devices].found = TRUE; if (length >= 5 && printer_informations[4][0] != '\0') pp->devices[pp->num_devices].device_id = g_strdup (printer_informations[4]); if (length >= 6 && printer_informations[5][0] != '\0') pp->devices[pp->num_devices].device_location = g_strdup (printer_informations[5]); pp->num_devices++; } g_strfreev (printer_informations); g_free (standard_output); } } else { if (error) g_warning ("%s", error->message); } g_free (command); g_free (host); } } g_free (uri); } else { gint length = 0; gint j = 0; for (i = 0; i < pp->num_devices; i++) if (!pp->devices[i].found) length++; CupsDevice *devices = NULL; devices = g_new0 (CupsDevice, length); for (i = 0; i < pp->num_devices; i++) { if (!pp->devices[i].found) { devices[j] = pp->devices[i]; pp->devices[i].device_class = NULL; pp->devices[i].device_id = NULL; pp->devices[i].device_info = NULL; pp->devices[i].device_make_and_model = NULL; pp->devices[i].device_uri = NULL; pp->devices[i].device_location = NULL; pp->devices[i].device_ppd_uri = NULL; pp->devices[i].display_name = NULL; pp->devices[i].hostname = NULL; j++; } } g_free (pp->devices); pp->devices = devices; pp->num_devices = length; } actualize_devices_list (pp); } static void actualize_devices_list (PpNewPrinterDialog *pp) { GtkListStore *network_store; GtkListStore *local_store; GtkTreeModel *model; GtkTreeView *network_treeview; GtkTreeView *local_treeview; GtkTreeIter iter; GtkWidget *treeview; GtkWidget *widget; gint i; gint device_type = -1; network_treeview = (GtkTreeView*) gtk_builder_get_object (pp->builder, "network-devices-treeview"); local_treeview = (GtkTreeView*) gtk_builder_get_object (pp->builder, "local-devices-treeview"); network_store = gtk_list_store_new (DEVICE_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING); local_store = gtk_list_store_new (DEVICE_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING); for (i = 0; i < pp->num_devices; i++) { if (pp->devices[i].device_id || pp->devices[i].device_ppd_uri) { if (g_strcmp0 (pp->devices[i].device_class, "network") == 0) { gtk_list_store_append (network_store, &iter); gtk_list_store_set (network_store, &iter, DEVICE_ID_COLUMN, i, DEVICE_NAME_COLUMN, pp->devices[i].display_name, -1); pp->show_warning = FALSE; } else if (g_strcmp0 (pp->devices[i].device_class, "direct") == 0) { gtk_list_store_append (local_store, &iter); gtk_list_store_set (local_store, &iter, DEVICE_ID_COLUMN, i, DEVICE_NAME_COLUMN, pp->devices[i].display_name, -1); } } } gtk_tree_view_set_model (network_treeview, GTK_TREE_MODEL (network_store)); gtk_tree_view_set_model (local_treeview, GTK_TREE_MODEL (local_store)); if (gtk_tree_model_get_iter_first ((GtkTreeModel *) network_store, &iter)) gtk_tree_selection_select_iter ( gtk_tree_view_get_selection (GTK_TREE_VIEW (network_treeview)), &iter); if (gtk_tree_model_get_iter_first ((GtkTreeModel *) local_store, &iter)) gtk_tree_selection_select_iter ( gtk_tree_view_get_selection (GTK_TREE_VIEW (local_treeview)), &iter); treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-types-treeview"); if (gtk_tree_selection_get_selected ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter)) gtk_tree_model_get (model, &iter, DEVICE_TYPE_TYPE_COLUMN, &device_type, -1); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-type-notebook"); if (pp->show_warning && device_type == DEVICE_TYPE_NETWORK) gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 2); else gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), device_type); g_object_unref (network_store); g_object_unref (local_store); } static void populate_devices_list (PpNewPrinterDialog *pp) { GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTextBuffer *text_buffer; GtkTextView *warning_textview; GtkTextIter text_iter; GtkWidget *network_treeview; GtkWidget *local_treeview; network_treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "network-devices-treeview"); local_treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "local-devices-treeview"); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (network_treeview)), "changed", G_CALLBACK (device_selection_changed_cb), pp); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (local_treeview)), "changed", G_CALLBACK (device_selection_changed_cb), pp); actualize_devices_list (pp); if (dbus_method_available (FIREWALLD_BUS, FIREWALLD_PATH, FIREWALLD_IFACE, "getServices")) { if (!service_enabled ("mdns")) service_enable ("mdns", 300); if (!service_enabled ("ipp")) service_enable ("ipp", 300); if (!service_enabled ("ipp-client")) service_enable ("ipp-client", 300); if (!service_enabled ("samba-client")) service_enable ("samba-client", 300); } else { pp->warning = g_strdup (_("FirewallD is not running. \ Network printer detection needs services mdns, ipp, ipp-client \ and samba-client enabled on firewall.")); warning_textview = (GtkTextView*) gtk_builder_get_object (pp->builder, "warning-textview"); text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (warning_textview)); gtk_text_buffer_set_text (text_buffer, "", 0); gtk_text_buffer_get_iter_at_offset (text_buffer, &text_iter, 0); gtk_text_buffer_insert (text_buffer, &text_iter, pp->warning, -1); pp->show_warning = TRUE; } devices_get (pp); renderer = gtk_cell_renderer_text_new (); /* Translators: Column of devices which can be installed */ column = gtk_tree_view_column_new_with_attributes (_("Devices"), renderer, "text", DEVICE_NAME_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (network_treeview), column); /* Translators: Column of devices which can be installed */ column = gtk_tree_view_column_new_with_attributes (_("Devices"), renderer, "text", DEVICE_NAME_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (local_treeview), column); } static void actualize_device_types_list (PpNewPrinterDialog *pp) { GtkListStore *store; GtkTreeView *treeview; GtkTreeIter iter; gint i; treeview = (GtkTreeView*) gtk_builder_get_object (pp->builder, "device-types-treeview"); store = gtk_list_store_new (DEVICE_TYPE_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT); pp->device_connection_types = g_new (gchar*, 2); pp->num_device_connection_types = 2; /* Translators: Local means local printers */ pp->device_connection_types[0] = g_strdup (C_("printer type", "Local")); /* Translators: Network means network printers */ pp->device_connection_types[1] = g_strdup (C_("printer type", "Network")); for (i = 0; i < pp->num_device_connection_types; i++) { gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, DEVICE_TYPE_ID_COLUMN, i, DEVICE_TYPE_NAME_COLUMN, pp->device_connection_types[i], DEVICE_TYPE_TYPE_COLUMN, i, -1); } gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store)); gtk_tree_model_get_iter_first ((GtkTreeModel *) store, &iter); gtk_tree_selection_select_iter ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &iter); g_object_unref (store); } static void populate_device_types_list (PpNewPrinterDialog *pp) { GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkWidget *treeview; treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-types-treeview"); actualize_device_types_list (pp); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), "changed", G_CALLBACK (device_type_selection_changed_cb), pp); renderer = gtk_cell_renderer_text_new (); /* Translators: Device types column (network or local) */ column = gtk_tree_view_column_new_with_attributes (_("Device types"), renderer, "text", DEVICE_TYPE_NAME_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); } static void dialog_closed (GtkWidget *dialog, gint response_id, PpNewPrinterDialog *pp) { gtk_widget_destroy (dialog); } static void new_printer_add_button_cb (GtkButton *button, gpointer user_data) { PpNewPrinterDialog *pp = (PpNewPrinterDialog*) user_data; GtkResponseType dialog_response = GTK_RESPONSE_OK; GtkTreeModel *model; cups_dest_t *dests; GtkTreeIter iter; GtkWidget *treeview; gboolean success = FALSE; PPDName *ppd_name = NULL; gchar *device_name = NULL; gint device_id = -1; gint device_type = -1; gint i, j, k; int num_dests; treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "device-types-treeview"); if (gtk_tree_selection_get_selected ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter)) gtk_tree_model_get (model, &iter, DEVICE_TYPE_TYPE_COLUMN, &device_type, -1); switch (device_type) { case DEVICE_TYPE_LOCAL: treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "local-devices-treeview"); break; case DEVICE_TYPE_NETWORK: treeview = (GtkWidget*) gtk_builder_get_object (pp->builder, "network-devices-treeview"); break; default: treeview = NULL; break; } if (treeview && gtk_tree_selection_get_selected ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter)) { gtk_tree_model_get (model, &iter, DEVICE_ID_COLUMN, &device_id, DEVICE_NAME_COLUMN, &device_name, -1); } if (device_id >= 0) { if (pp->devices[device_id].device_ppd_uri) { http_t *http; http = httpConnectEncrypt (pp->devices[device_id].hostname, pp->devices[device_id].host_port, cupsEncryption ()); if (http) { const char *ppd_file_name; ppd_file_name = cupsGetPPD2 (http, pp->devices[device_id].display_name); if (ppd_file_name) { DBusGProxy *proxy; GError *error = NULL; char *ret_error = NULL; proxy = get_dbus_proxy (MECHANISM_BUS, "/", MECHANISM_BUS, TRUE); if (proxy) { dbus_g_proxy_call (proxy, "PrinterAddWithPpdFile", &error, G_TYPE_STRING, pp->devices[device_id].display_name, G_TYPE_STRING, pp->devices[device_id].device_uri, G_TYPE_STRING, ppd_file_name, G_TYPE_STRING, pp->devices[device_id].device_info, G_TYPE_STRING, pp->devices[device_id].device_location, G_TYPE_INVALID, G_TYPE_STRING, &ret_error, G_TYPE_INVALID); if (error || (ret_error && ret_error[0] != '\0')) { dialog_response = GTK_RESPONSE_REJECT; if (error) g_warning ("%s", error->message); if (ret_error && ret_error[0] != '\0') g_warning ("%s", ret_error); g_clear_error (&error); } else success = TRUE; g_object_unref (proxy); } g_unlink (ppd_file_name); } else { dialog_response = GTK_RESPONSE_REJECT; g_warning ("Getting of PPD for %s from %s:%d failed.", pp->devices[device_id].display_name, pp->devices[device_id].hostname, pp->devices[device_id].host_port); } } } else if (pp->devices[device_id].device_id) { /* Try whether CUPS has a driver for the new printer */ ppd_name = get_ppd_name (pp->devices[device_id].device_id, pp->devices[device_id].device_make_and_model, pp->devices[device_id].device_uri); if (ppd_name == NULL || ppd_name->ppd_match_level < PPD_EXACT_MATCH) { /* Try PackageKit to install printer driver */ DBusGProxy *proxy; GError *error = NULL; proxy = get_dbus_proxy (PACKAGE_KIT_BUS, PACKAGE_KIT_PATH, PACKAGE_KIT_IFACE, FALSE); if (proxy) { gchar **device_ids = NULL; device_ids = g_new (gchar *, 2); device_ids[0] = pp->devices[device_id].device_id; device_ids[1] = NULL; dbus_g_proxy_call_with_timeout (proxy, "InstallPrinterDrivers", 3600000, &error, #ifdef GDK_WINDOWING_X11 G_TYPE_UINT, GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (pp->dialog))), #else G_TYPE_UINT, 0, #endif G_TYPE_STRV, device_ids, G_TYPE_STRING, "hide-finished", G_TYPE_INVALID, G_TYPE_INVALID); g_object_unref (proxy); if (error) g_warning ("%s", error->message); g_clear_error (&error); if (ppd_name) { g_free (ppd_name->ppd_name); g_free (ppd_name); } /* Search CUPS for driver */ ppd_name = get_ppd_name (pp->devices[device_id].device_id, pp->devices[device_id].device_make_and_model, pp->devices[device_id].device_uri); g_free (device_ids); } } /* Add the new printer */ if (ppd_name && ppd_name->ppd_name) { DBusGProxy *proxy; GError *error = NULL; char *ret_error = NULL; proxy = get_dbus_proxy (MECHANISM_BUS, "/", MECHANISM_BUS, TRUE); if (proxy) { dbus_g_proxy_call (proxy, "PrinterAdd", &error, G_TYPE_STRING, pp->devices[device_id].display_name, G_TYPE_STRING, pp->devices[device_id].device_uri, G_TYPE_STRING, ppd_name->ppd_name, G_TYPE_STRING, pp->devices[device_id].device_info, G_TYPE_STRING, pp->devices[device_id].device_location, G_TYPE_INVALID, G_TYPE_STRING, &ret_error, G_TYPE_INVALID); if (error || (ret_error && ret_error[0] != '\0')) { dialog_response = GTK_RESPONSE_REJECT; if (error) g_warning ("%s", error->message); if (ret_error && ret_error[0] != '\0') g_warning ("%s", ret_error); g_clear_error (&error); } g_object_unref (proxy); } g_free (ppd_name->ppd_name); g_free (ppd_name); } num_dests = cupsGetDests (&dests); for (i = 0; i < num_dests; i++) if (g_strcmp0 (dests[i].name, pp->devices[device_id].display_name) == 0) success = TRUE; cupsFreeDests (num_dests, dests); } /* Set some options of the new printer */ if (success) { DBusGProxy *proxy; GError *error = NULL; char *ret_error = NULL; char *locale = NULL; proxy = get_dbus_proxy (MECHANISM_BUS, "/", MECHANISM_BUS, TRUE); if (proxy) { printer_set_accepting_jobs (pp->devices[device_id].display_name, TRUE, NULL); printer_set_enabled (pp->devices[device_id].display_name, TRUE); if (g_strcmp0 (pp->devices[device_id].device_class, "direct") == 0) { gchar *commands = get_dest_attr (pp->devices[device_id].display_name, "printer-commands"); gchar *commands_lowercase = g_ascii_strdown (commands, -1); ipp_t *response = NULL; if (g_strrstr (commands_lowercase, "AutoConfigure")) { response = execute_maintenance_command (pp->devices[device_id].display_name, "AutoConfigure", /* Translators: Name of job which makes printer to autoconfigure itself */ _("Automatic configuration")); if (response) { if (response->state == IPP_ERROR) g_warning ("An error has occured during automatic configuration of new printer."); ippDelete (response); } } g_free (commands); g_free (commands_lowercase); } /* Set default PaperSize according to the locale */ locale = setlocale (LC_PAPER, NULL); if (locale == NULL) locale = setlocale (LC_MESSAGES, NULL); if (locale) { const char *ppd_file_name = NULL; ppd_file_t *ppd_file = NULL; gchar **value = NULL; gchar *paper_size; /* CLDR 2.0 alpha * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/territory_language_information.html */ if (g_regex_match_simple ("[^_.@]{2,3}_(BZ|CA|CL|CO|CR|GT|MX|NI|PA|PH|PR|SV|US|VE)", locale, G_REGEX_ANCHORED, G_REGEX_MATCH_ANCHORED)) paper_size = g_strdup ("Letter"); else paper_size = g_strdup ("A4"); ppd_file_name = cupsGetPPD (pp->devices[device_id].display_name); if (ppd_file_name) { ppd_file = ppdOpenFile (ppd_file_name); if (ppd_file) { ppdMarkDefaults (ppd_file); for (i = 0; i < ppd_file->num_groups; i++) for (j = 0; j < ppd_file->groups[i].num_options; j++) if (g_strcmp0 ("PageSize", ppd_file->groups[i].options[j].keyword) == 0) { for (k = 0; k < ppd_file->groups[i].options[j].num_choices; k++) { if (g_ascii_strncasecmp (paper_size, ppd_file->groups[i].options[j].choices[k].choice, strlen (paper_size)) == 0 && !ppd_file->groups[i].options[j].choices[k].marked) { value = g_new0 (gchar *, 2); value[0] = g_strdup (ppd_file->groups[i].options[j].choices[k].choice); break; } } break; } ppdClose (ppd_file); } g_unlink (ppd_file_name); } if (value) { dbus_g_proxy_call (proxy, "PrinterAddOptionDefault", &error, G_TYPE_STRING, pp->devices[device_id].display_name, G_TYPE_STRING, "PageSize-default", G_TYPE_STRV, value, G_TYPE_INVALID, G_TYPE_STRING, &ret_error, G_TYPE_INVALID); if (error) { g_warning ("%s", error->message); g_clear_error (&error); } if (ret_error && ret_error[0] != '\0') g_warning ("%s", ret_error); g_strfreev (value); } g_free (paper_size); } g_object_unref (proxy); } if (pp->devices[device_id].device_uri && dbus_method_available (FIREWALLD_BUS, FIREWALLD_PATH, FIREWALLD_IFACE, "getServices")) { if (g_str_has_prefix (pp->devices[device_id].device_uri, "dnssd:") || g_str_has_prefix (pp->devices[device_id].device_uri, "mdns:")) { show_notification (_("Opening firewall for mDNS connections"), NULL, "dialog-information-symbolic"); service_disable ("mdns"); service_enable ("mdns", 0); } if (g_strrstr (pp->devices[device_id].device_uri, "smb:") != NULL) { show_notification (_("Opening firewall for Samba connections"), NULL, "dialog-information-symbolic"); service_disable ("samba-client"); service_enable ("samba-client", 0); } if (g_strrstr (pp->devices[device_id].device_uri, "ipp:") != NULL) { show_notification (_("Opening firewall for IPP connections"), NULL, "dialog-information-symbolic"); service_disable ("ipp"); service_enable ("ipp", 0); service_disable ("ipp-client"); service_enable ("ipp-client", 0); } } } } pp_new_printer_dialog_hide (pp); pp->user_callback (GTK_DIALOG (pp->dialog), dialog_response, pp->user_data); } static void new_printer_cancel_button_cb (GtkButton *button, gpointer user_data) { PpNewPrinterDialog *pp = (PpNewPrinterDialog*) user_data; pp_new_printer_dialog_hide (pp); pp->user_callback (GTK_DIALOG (pp->dialog), GTK_RESPONSE_CANCEL, pp->user_data); } PpNewPrinterDialog * pp_new_printer_dialog_new (GtkWindow *parent, UserResponseCallback user_callback, gpointer user_data) { PpNewPrinterDialog *pp; GtkWidget *widget; GError *error = NULL; gchar *objects[] = { "dialog", "main-vbox", NULL }; pp = g_new0 (PpNewPrinterDialog, 1); pp->builder = gtk_builder_new (); pp->parent = GTK_WIDGET (parent); gtk_builder_add_objects_from_file (pp->builder, DATADIR"/new-printer-dialog.ui", objects, &error); if (error) { g_warning ("Could not load ui: %s", error->message); g_error_free (error); return NULL; } pp->device_connection_types = NULL; pp->num_device_connection_types = 0; pp->devices = NULL; pp->num_devices = 0; pp->dialog = (GtkWidget *) gtk_builder_get_object (pp->builder, "dialog"); pp->user_callback = user_callback; pp->user_data = user_data; pp->cancellable = NULL; pp->warning = NULL; pp->show_warning = FALSE; /* connect signals */ g_signal_connect (pp->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "new-printer-add-button"); g_signal_connect (widget, "clicked", G_CALLBACK (new_printer_add_button_cb), pp); gtk_widget_set_sensitive (widget, FALSE); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "new-printer-cancel-button"); g_signal_connect (widget, "clicked", G_CALLBACK (new_printer_cancel_button_cb), pp); widget = (GtkWidget*) gtk_builder_get_object (pp->builder, "search-by-address-checkbutton"); g_signal_connect (widget, "toggled", G_CALLBACK (search_address_cb), pp); gtk_window_set_transient_for (GTK_WINDOW (pp->dialog), GTK_WINDOW (parent)); gtk_window_set_modal (GTK_WINDOW (pp->dialog), TRUE); gtk_window_present (GTK_WINDOW (pp->dialog)); gtk_widget_show_all (GTK_WIDGET (pp->dialog)); populate_device_types_list (pp); populate_devices_list (pp); return pp; } void pp_new_printer_dialog_free (PpNewPrinterDialog *pp) { gint i; for (i = 0; i < pp->num_device_connection_types; i++) g_free (pp->device_connection_types[i]); g_free (pp->device_connection_types); pp->device_connection_types = NULL; free_devices (pp); gtk_widget_destroy (GTK_WIDGET (pp->dialog)); pp->dialog = NULL; g_object_unref (pp->builder); pp->builder = NULL; if (pp->cancellable) { g_cancellable_cancel (pp->cancellable); g_object_unref (pp->cancellable); } g_free (pp->warning); g_free (pp); } static void pp_new_printer_dialog_hide (PpNewPrinterDialog *pp) { gtk_widget_hide (GTK_WIDGET (pp->dialog)); }