diff options
author | Marek Kasik <mkasik@redhat.com> | 2011-03-07 17:09:00 +0100 |
---|---|---|
committer | Marek Kasik <mkasik@redhat.com> | 2011-03-07 17:09:00 +0100 |
commit | 3fb4d4af2745124c3d370c0fcc94fdb5886af245 (patch) | |
tree | d400d7b8b742c7ff823275cacb507b9e1f202f21 | |
parent | c96cc691026084eea4db47ee8050f553a8cbfcc2 (diff) | |
download | gnome-control-center-3fb4d4af2745124c3d370c0fcc94fdb5886af245.tar.gz |
printers: Add dialog for adding new printers
The dialog is able to add printers discovered by cupsGetDevices().
It can also add printers from a remote CUPS server by typing
its address and selecting printer we want to add (#640734).
-rw-r--r-- | panels/printers/Makefile.am | 5 | ||||
-rw-r--r-- | panels/printers/cc-printers-panel.c | 273 | ||||
-rw-r--r-- | panels/printers/new-printer-dialog.ui | 247 | ||||
-rw-r--r-- | panels/printers/pp-new-printer-dialog.c | 1066 | ||||
-rw-r--r-- | panels/printers/pp-new-printer-dialog.h | 39 | ||||
-rw-r--r-- | panels/printers/pp-utils.c | 385 | ||||
-rw-r--r-- | panels/printers/pp-utils.h | 59 |
7 files changed, 1887 insertions, 187 deletions
diff --git a/panels/printers/Makefile.am b/panels/printers/Makefile.am index 96caf475c..4f319eb6f 100644 --- a/panels/printers/Makefile.am +++ b/panels/printers/Makefile.am @@ -2,6 +2,7 @@ cappletname = printers uidir = $(pkgdatadir)/ui/printers dist_ui_DATA = \ + new-printer-dialog.ui \ printers.ui INCLUDES = \ @@ -17,6 +18,10 @@ ccpanels_LTLIBRARIES = libprinters.la libprinters_la_SOURCES = \ printers-module.c \ + pp-utils.c \ + pp-utils.h \ + pp-new-printer-dialog.c \ + pp-new-printer-dialog.h \ cc-printers-panel.c \ cc-printers-panel.h diff --git a/panels/printers/cc-printers-panel.c b/panels/printers/cc-printers-panel.c index 84bd7f7ec..9dc64011f 100644 --- a/panels/printers/cc-printers-panel.c +++ b/panels/printers/cc-printers-panel.c @@ -33,6 +33,8 @@ #include <math.h> #include "cc-lockbutton.h" +#include "pp-new-printer-dialog.h" +#include "pp-utils.h" G_DEFINE_DYNAMIC_TYPE (CcPrintersPanel, cc_printers_panel, CC_TYPE_PANEL) @@ -71,6 +73,8 @@ struct _CcPrintersPanelPrivate GPermission *permission; + PpNewPrinterDialog *pp_new_printer_dialog; + gpointer dummy; }; @@ -172,32 +176,6 @@ cc_printers_panel_class_finalize (CcPrintersPanelClass *klass) { } -gchar * -get_ppd_attribute (const gchar *printer_name, const gchar *attribute_name) -{ - gchar *file_name = NULL; - ppd_file_t *ppd_file = NULL; - ppd_attr_t *ppd_attr = NULL; - gchar *result = NULL; - - file_name = cupsGetPPD (printer_name); - - if (file_name) - { - ppd_file = ppdOpenFile (file_name); - if (ppd_file) - { - ppd_attr = ppdFindAttr (ppd_file, attribute_name, NULL); - if (ppd_attr != NULL) - result = g_strdup (ppd_attr->value); - ppdClose (ppd_file); - } - g_unlink (file_name); - } - - return result; -} - enum { PRINTER_ID_COLUMN, @@ -1168,73 +1146,6 @@ enum ALLOWED_USERS_N_COLUMNS }; -static int -ccGetAllowedUsers (gchar ***allowed_users, char *printer_name) -{ - const char * const attrs[1] = { "requesting-user-name-allowed" }; - http_t *http; - ipp_t *request = NULL; - gchar **users = NULL; - ipp_t *response; - char uri[HTTP_MAX_URI + 1]; - int num_allowed_users = 0; - - http = httpConnectEncrypt (cupsServer (), - ippPort (), - cupsEncryption ()); - - if (http || !allowed_users) - { - request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES); - - g_snprintf (uri, sizeof (uri), "ipp://localhost/printers/%s", printer_name); - ippAddString (request, - IPP_TAG_OPERATION, - IPP_TAG_URI, - "printer-uri", - NULL, - uri); - ippAddStrings (request, - IPP_TAG_OPERATION, - IPP_TAG_KEYWORD, - "requested-attributes", - 1, - NULL, - attrs); - - response = cupsDoRequest (http, request, "/"); - if (response) - { - ipp_attribute_t *attr = NULL; - ipp_attribute_t *allowed = NULL; - - for (attr = response->attrs; attr != NULL; attr = attr->next) - { - if (attr->group_tag == IPP_TAG_PRINTER && - attr->value_tag == IPP_TAG_NAME && - !g_strcmp0 (attr->name, "requesting-user-name-allowed")) - allowed = attr; - } - - if (allowed && allowed->num_values > 0) - { - int i; - - num_allowed_users = allowed->num_values; - users = g_new (gchar*, num_allowed_users); - - for (i = 0; i < num_allowed_users; i ++) - users[i] = g_strdup (allowed->values[i].string.text); - } - ippDelete(response); - } - httpClose (http); - } - - *allowed_users = users; - return num_allowed_users; -} - static void actualize_allowed_users_list (CcPrintersPanel *self) { @@ -1330,33 +1241,6 @@ populate_allowed_users_list (CcPrintersPanel *self) "changed", G_CALLBACK (allowed_users_selection_changed_cb), self); } -static DBusGProxy * -get_dbus_proxy () -{ - DBusGConnection *system_bus; - DBusGProxy *proxy; - GError *error; - - error = NULL; - system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); - if (system_bus == NULL) - { - /* Translators: Program cannot connect to DBus' system bus */ - g_warning (_("Could not connect to system bus: %s"), - error->message); - g_error_free (error); - return NULL; - } - - error = NULL; - - proxy = dbus_g_proxy_new_for_name (system_bus, - MECHANISM_BUS, - "/", - MECHANISM_BUS); - return proxy; -} - static void job_process_cb (GtkButton *button, gpointer user_data) @@ -1378,7 +1262,10 @@ job_process_cb (GtkButton *button, if (id >= 0) { - proxy = get_dbus_proxy (); + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); if (!proxy) return; @@ -1471,7 +1358,10 @@ printer_disable_cb (GObject *gobject, if (name) { - proxy = get_dbus_proxy (); + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); if (!proxy) return; @@ -1718,7 +1608,10 @@ allowed_user_remove_cb (GtkToolButton *button, if (name && printer_name) { - proxy = get_dbus_proxy (); + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); if (!proxy) return; @@ -1788,7 +1681,10 @@ allowed_user_add_cb (GtkCellRendererText *renderer, if (new_text && new_text[0] != '\0' && printer_name) { - proxy = get_dbus_proxy (); + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); if (!proxy) return; @@ -1889,7 +1785,10 @@ printer_set_default_cb (GtkToggleButton *button, if (name) { - proxy = get_dbus_proxy (); + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); if (!proxy) return; @@ -1923,6 +1822,58 @@ printer_set_default_cb (GtkToggleButton *button, } static void +new_printer_dialog_response_cb (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + CcPrintersPanelPrivate *priv; + CcPrintersPanel *self = (CcPrintersPanel*) user_data; + + priv = PRINTERS_PANEL_PRIVATE (self); + + pp_new_printer_dialog_free (priv->pp_new_printer_dialog); + priv->pp_new_printer_dialog = NULL; + + if (response_id == GTK_RESPONSE_OK) + actualize_printers_list (self); + else if (response_id == GTK_RESPONSE_REJECT) + { + GtkWidget *message_dialog; + + message_dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + /* Translators: Addition of the new printer failed. */ + _("Failed to add new printer.")); + g_signal_connect (message_dialog, + "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + gtk_widget_show (message_dialog); + } +} + +static void +printer_add_cb (GtkToolButton *toolbutton, + gpointer user_data) +{ + CcPrintersPanelPrivate *priv; + CcPrintersPanel *self = (CcPrintersPanel*) user_data; + GtkWidget *widget; + + priv = PRINTERS_PANEL_PRIVATE (self); + + widget = (GtkWidget*) + gtk_builder_get_object (priv->builder, "main-vbox"); + + priv->pp_new_printer_dialog = pp_new_printer_dialog_new ( + GTK_WINDOW (gtk_widget_get_toplevel (widget)), + new_printer_dialog_response_cb, + self); +} + +static void printer_remove_cb (GtkToolButton *toolbutton, gpointer user_data) { @@ -1942,7 +1893,10 @@ printer_remove_cb (GtkToolButton *toolbutton, if (name) { - proxy = get_dbus_proxy (); + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); if (!proxy) return; @@ -1966,67 +1920,6 @@ printer_remove_cb (GtkToolButton *toolbutton, } } -static ipp_t * -execute_maintenance_command (const char *printer_name, - const char *command, - const char *title) -{ - http_t *http; - GError *error = NULL; - ipp_t *request = NULL; - ipp_t *response = NULL; - char uri[HTTP_MAX_URI + 1]; - int fd = -1; - - http = httpConnectEncrypt (cupsServer (), - ippPort (), - cupsEncryption ()); - - if (http) - { - request = ippNewRequest (IPP_PRINT_JOB); - - g_snprintf (uri, - sizeof (uri), - "ipp://localhost/printers/%s", - printer_name); - - ippAddString (request, - IPP_TAG_OPERATION, - IPP_TAG_URI, - "printer-uri", - NULL, - uri); - - ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", - NULL, title); - - ippAddString (request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format", - NULL, "application/vnd.cups-command"); - - gchar *file_name = NULL; - fd = g_file_open_tmp ("ccXXXXXX", &file_name, &error); - - if (fd != -1 && !error) - { - FILE *file; - - file = fdopen (fd, "w"); - fprintf (file, "#CUPS-COMMAND\n"); - fprintf (file, "%s\n", command); - fclose (file); - - response = cupsDoFileRequest (http, request, "/", file_name); - g_unlink (file_name); - } - - g_free (file_name); - httpClose (http); - } - - return response; -} - static void printer_maintenance_cb (GtkButton *button, gpointer user_data) @@ -2185,6 +2078,8 @@ cc_printers_panel_init (CcPrintersPanel *self) priv->num_allowed_users = 0; priv->current_allowed_user = -1; + priv->pp_new_printer_dialog = NULL; + gtk_builder_add_objects_from_file (priv->builder, DATADIR"/printers.ui", objects, &error); @@ -2215,6 +2110,10 @@ cc_printers_panel_init (CcPrintersPanel *self) g_signal_connect (widget, "clicked", G_CALLBACK (job_process_cb), self); widget = (GtkWidget*) + gtk_builder_get_object (priv->builder, "printer-add-button"); + g_signal_connect (widget, "clicked", G_CALLBACK (printer_add_cb), self); + + widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-remove-button"); g_signal_connect (widget, "clicked", G_CALLBACK (printer_remove_cb), self); diff --git a/panels/printers/new-printer-dialog.ui b/panels/printers/new-printer-dialog.ui new file mode 100644 index 000000000..f76c1051b --- /dev/null +++ b/panels/printers/new-printer-dialog.ui @@ -0,0 +1,247 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="dialog"> + <property name="width_request">500</property> + <property name="height_request">350</property> + <property name="border_width">5</property> + <property name="resizable">False</property> + <property name="type_hint">normal</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">10</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Add a New Printer</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="spacing">10</property> + <child> + <object class="GtkTreeView" id="device-types-treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">False</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkNotebook" id="device-type-notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">False</property> + <property name="show_border">False</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <object class="GtkTreeView" id="local-devices-treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + </object> + </child> + </object> + </child> + <child type="tab"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes">page 1</property> + </object> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">never</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <object class="GtkTreeView" id="network-devices-treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">False</property> + </object> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="label" translatable="yes">Address</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="address-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3"> + <property name="visible">True</property> + <child> + <object class="GtkCheckButton" id="search-by-address-checkbutton"> + <property name="label" translatable="yes">Search by Address</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="search-state-label"> + <property name="visible">True</property> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes">page 2</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child type="tab"> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">page 3</property> + </object> + <packing> + <property name="position">2</property> + <property name="tab_fill">False</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="new-printer-cancel-button"> + <property name="label" translatable="yes">Cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="new-printer-add-button"> + <property name="label" translatable="yes">Add</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="0">new-printer-cancel-button</action-widget> + <action-widget response="0">new-printer-add-button</action-widget> + </action-widgets> + </object> +</interface> diff --git a/panels/printers/pp-new-printer-dialog.c b/panels/printers/pp-new-printer-dialog.c new file mode 100644 index 000000000..7c0f3187c --- /dev/null +++ b/panels/printers/pp-new-printer-dialog.c @@ -0,0 +1,1066 @@ +/* -*- 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 <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> + +#include <cups/cups.h> + +#include "pp-new-printer-dialog.h" +#include "pp-utils.h" + +#include <dbus/dbus-glib.h> + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#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 ALLOWED_CHARACTERS "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/" + +static void pp_new_printer_dialog_hide (PpNewPrinterDialog *pp); +static void actualize_devices_list (PpNewPrinterDialog *pp, gboolean get_devices); + +enum +{ + NOTEBOOK_LOCAL_PAGE = 0, + NOTEBOOK_NETWORK_PAGE, + NOTEBOOK_HP_JETDIRECT_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_HP_JETDIRECT, + 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; +}; + +static void +device_type_selection_changed_cb (GtkTreeSelection *selection, + gpointer user_data) +{ + PpNewPrinterDialog *pp = (PpNewPrinterDialog *) user_data; + GtkTreeModel *model; + GtkTreeIter iter; + 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) + { + GtkWidget *widget; + + widget = (GtkWidget*) + gtk_builder_get_object (pp->builder, "device-type-notebook"); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), device_type); + } +} + +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 (PpNewPrinterDialog *pp) +{ + DBusGProxy *proxy; + GError *error = NULL; + char *ret_error = NULL; + gint i, j; + + proxy = get_dbus_proxy (MECHANISM_BUS, + "/", + MECHANISM_BUS, + TRUE); + + if (!proxy) + return; + + GHashTable *devices = NULL; + dbus_g_proxy_call (proxy, "DevicesGet", &error, + G_TYPE_INT, 60, + G_TYPE_STRING, CUPS_INCLUDE_ALL, + G_TYPE_STRING, CUPS_EXCLUDE_NONE, + G_TYPE_INVALID, + G_TYPE_STRING, &ret_error, + DBUS_TYPE_G_STRING_STRING_HASHTABLE, &devices, + G_TYPE_INVALID); + + g_object_unref (proxy); + + 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"); + name = g_strcanon (name, ALLOWED_CHARACTERS, '-'); + } + else if (pp->devices[i].device_info) + { + name = g_strdup (pp->devices[i].device_info); + name = g_strcanon (name, ALLOWED_CHARACTERS, '-'); + } + + 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); + } + + g_clear_error (&error); +} + +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') + { + http_t *http; + cups_dest_t *dests = NULL; + gint num_dests = 0; + gchar *tmp = NULL; + gchar *host = NULL; + gchar *port_string = NULL; + int port = 631; + gchar *position; + + 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); + + /* Search for CUPS server */ + if (host) + { + 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); + } + 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, FALSE); +} + +static void +actualize_devices_list (PpNewPrinterDialog *pp, gboolean get_devices) +{ + GtkListStore *network_store; + GtkListStore *local_store; + GtkTreeView *network_treeview; + GtkTreeView *local_treeview; + GtkTreeIter iter; + gint i; + + if (get_devices) + devices_get (pp); + + 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); + } + 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)); + + 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); + + 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); + + g_object_unref (network_store); + g_object_unref (local_store); +} + +static void +populate_devices_list (PpNewPrinterDialog *pp) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + 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"); + + actualize_devices_list (pp, TRUE); + + 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 (_("Local")); + /* Translators: Network means network printers */ + pp->device_connection_types[1] = g_strdup (_("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; + GtkTreeIter iter; + GtkWidget *treeview; + gchar *device_name = NULL; + gchar *ppd_name = NULL; + gint device_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_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 + { + ret_error = NULL; + + dbus_g_proxy_call (proxy, "PrinterSetAcceptJobs", &error, + G_TYPE_STRING, pp->devices[device_id].display_name, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_STRING, "none", + G_TYPE_INVALID, + G_TYPE_STRING, &ret_error, + G_TYPE_INVALID); + + dbus_g_proxy_call (proxy, "PrinterSetEnabled", &error, + G_TYPE_STRING, pp->devices[device_id].display_name, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID, + G_TYPE_STRING, &ret_error, + G_TYPE_INVALID); + + 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); + + g_clear_error (&error); + } + else + { + 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) + /* Translators: An error has occured during execution of AutoConfigure CUPS maintenance command */ + g_warning ("An error has occured during automatic configuration of new printer."); + ippDelete (response); + } + } + g_free (commands); + g_free (commands_lowercase); + } + } + } + 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_class, + pp->devices[device_id].device_id, + pp->devices[device_id].device_info, + pp->devices[device_id].device_make_and_model, + pp->devices[device_id].device_uri, + pp->devices[device_id].device_location); + + if (ppd_name == NULL) + { + /* 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 (proxy, "InstallPrinterDrivers", &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); + + /* Search CUPS for driver */ + ppd_name = get_ppd_name (pp->devices[device_id].device_class, + pp->devices[device_id].device_id, + pp->devices[device_id].device_info, + pp->devices[device_id].device_make_and_model, + pp->devices[device_id].device_uri, + pp->devices[device_id].device_location); + + g_free (device_ids); + } + } + + /* Add the new printer */ + if (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, + 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 + { + ret_error = NULL; + + dbus_g_proxy_call (proxy, "PrinterSetAcceptJobs", &error, + G_TYPE_STRING, pp->devices[device_id].display_name, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_STRING, "none", + G_TYPE_INVALID, + G_TYPE_STRING, &ret_error, + G_TYPE_INVALID); + + dbus_g_proxy_call (proxy, "PrinterSetEnabled", &error, + G_TYPE_STRING, pp->devices[device_id].display_name, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID, + G_TYPE_STRING, &ret_error, + G_TYPE_INVALID); + + 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); + + g_clear_error (&error); + } + else + { + 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) + /* Translators: An error has occured during execution of AutoConfigure CUPS maintenance command */ + g_warning ("An error has occured during automatic configuration of new printer."); + ippDelete (response); + } + } + g_free (commands); + g_free (commands_lowercase); + } + } + } + g_object_unref (proxy); + } + } + } + } + + 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; + + /* 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); + + 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; + + g_free (pp); +} + +static void +pp_new_printer_dialog_hide (PpNewPrinterDialog *pp) +{ + gtk_widget_hide (GTK_WIDGET (pp->dialog)); +} diff --git a/panels/printers/pp-new-printer-dialog.h b/panels/printers/pp-new-printer-dialog.h new file mode 100644 index 000000000..618c0d716 --- /dev/null +++ b/panels/printers/pp-new-printer-dialog.h @@ -0,0 +1,39 @@ +/* -*- 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. + * + */ + +#ifndef __PP_NEW_PRINTER_DIALOG_H__ +#define __PP_NEW_PRINTER_DIALOG_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _PpNewPrinterDialog PpNewPrinterDialog; + +typedef void (*UserResponseCallback) (GtkDialog *dialog, gint response_id, gpointer user_data); + +PpNewPrinterDialog *pp_new_printer_dialog_new (GtkWindow *parent, + UserResponseCallback user_callback, + gpointer user_data); +void pp_new_printer_dialog_free (PpNewPrinterDialog *dialog); + +G_END_DECLS + +#endif diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c new file mode 100644 index 000000000..39a1416f9 --- /dev/null +++ b/panels/printers/pp-utils.c @@ -0,0 +1,385 @@ +/* -*- 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 <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> +#include <cups/cups.h> +#include <dbus/dbus-glib.h> + +#include "pp-utils.h" + +DBusGProxy * +get_dbus_proxy (const gchar *name, + const gchar *path, + const gchar *iface, + const gboolean system_bus) +{ + DBusGConnection *bus; + DBusGProxy *proxy; + GError *error; + + error = NULL; + if (system_bus) + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + else + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + + if (bus == NULL) + { + if (system_bus) + /* Translators: Program cannot connect to DBus' system bus */ + g_warning ("Could not connect to system bus: %s", error->message); + else + /* Translators: Program cannot connect to DBus' session bus */ + g_warning ("Could not connect to session bus: %s", error->message); + g_error_free (error); + return NULL; + } + + error = NULL; + + proxy = dbus_g_proxy_new_for_name (bus, name, path, iface); + + return proxy; +} + +gchar *get_tag_value (const gchar *tag_string, const gchar *tag_name) +{ + gchar **tag_string_splitted = NULL; + gchar *tag_value = NULL; + gint tag_name_length = strlen (tag_name); + gint i; + + tag_string_splitted = g_strsplit (tag_string, ";", 0); + for (i = 0; i < g_strv_length (tag_string_splitted); i++) + if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0) + if (strlen (tag_string_splitted[i]) > tag_name_length + 1) + tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1); + g_strfreev (tag_string_splitted); + + return tag_value; +} + +gchar * +get_ppd_name (gchar *device_class, + gchar *device_id, + gchar *device_info, + gchar *device_make_and_model, + gchar *device_uri, + gchar *device_location) +{ + http_t *http = NULL; + ipp_t *request = NULL; + ipp_t *response = NULL; + gchar *mfg = NULL; + gchar *mdl = NULL; + gchar *result = NULL; + + mfg = get_tag_value (device_id, "mfg"); + mdl = get_tag_value (device_id, "mdl"); + + http = httpConnectEncrypt (cupsServer (), + ippPort (), + cupsEncryption ()); + + if (http) + { + request = ippNewRequest (CUPS_GET_PPDS); + if (device_id) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id", + NULL, device_id); + else if (mfg) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make", + NULL, mfg); + response = cupsDoRequest (http, request, "/"); + + if (response) + { + ipp_attribute_t *attr = NULL; + const char *ppd_device_id; + const char *ppd_make_model; + const char *ppd_make; + const char *ppd_name; + gchar *ppd_mfg; + gchar *ppd_mdl; + + for (attr = response->attrs; attr != NULL; attr = attr->next) + { + while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER) + attr = attr->next; + + if (attr == NULL) + break; + + ppd_device_id = "NONE"; + ppd_make_model = NULL; + ppd_make = NULL; + ppd_name = NULL; + ppd_mfg = NULL; + ppd_mdl = NULL; + + while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) + { + if (!strcmp(attr->name, "ppd-device-id") && + attr->value_tag == IPP_TAG_TEXT) + ppd_device_id = attr->values[0].string.text; + else if (!strcmp(attr->name, "ppd-name") && + attr->value_tag == IPP_TAG_NAME) + ppd_name = attr->values[0].string.text; + else if (!strcmp(attr->name, "ppd-make") && + attr->value_tag == IPP_TAG_TEXT) + ppd_make = attr->values[0].string.text; + else if (!strcmp(attr->name, "ppd-make-and-model") && + attr->value_tag == IPP_TAG_TEXT) + ppd_make_model = attr->values[0].string.text; + + attr = attr->next; + } + + if (mfg && mdl && !result) + { + if (ppd_device_id) + { + ppd_mfg = get_tag_value (ppd_device_id, "mfg"); + + if (ppd_mfg && g_ascii_strcasecmp (ppd_mfg, mfg) == 0) + { + ppd_mdl = get_tag_value (ppd_device_id, "mdl"); + + if (ppd_mdl && g_ascii_strcasecmp (ppd_mdl, mdl) == 0) + { + result = g_strdup (ppd_name); + g_free (ppd_mdl); + } + g_free (ppd_mfg); + } + } + } + + if (attr == NULL) + break; + } + ippDelete(response); + } + httpClose (http); + } + + g_free (mfg); + g_free (mdl); + + return result; +} + +char * +get_dest_attr (const char *dest_name, + const char *attr) +{ + cups_dest_t *dests; + int num_dests; + cups_dest_t *dest; + const char *value; + char *ret; + + if (dest_name == NULL) + return NULL; + + ret = NULL; + + num_dests = cupsGetDests (&dests); + if (num_dests < 1) { + g_debug ("Unable to get printer destinations"); + return NULL; + } + + dest = cupsGetDest (dest_name, NULL, num_dests, dests); + if (dest == NULL) { + g_debug ("Unable to find a printer named '%s'", dest_name); + goto out; + } + + value = cupsGetOption (attr, dest->num_options, dest->options); + if (value == NULL) { + g_debug ("Unable to get %s for '%s'", attr, dest_name); + goto out; + } + ret = g_strdup (value); +out: + cupsFreeDests (num_dests, dests); + + return ret; +} + +ipp_t * +execute_maintenance_command (const char *printer_name, + const char *command, + const char *title) +{ + http_t *http; + GError *error = NULL; + ipp_t *request = NULL; + ipp_t *response = NULL; + char uri[HTTP_MAX_URI + 1]; + int fd = -1; + + http = httpConnectEncrypt (cupsServer (), + ippPort (), + cupsEncryption ()); + + if (http) + { + request = ippNewRequest (IPP_PRINT_JOB); + + g_snprintf (uri, + sizeof (uri), + "ipp://localhost/printers/%s", + printer_name); + + ippAddString (request, + IPP_TAG_OPERATION, + IPP_TAG_URI, + "printer-uri", + NULL, + uri); + + ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", + NULL, title); + + ippAddString (request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format", + NULL, "application/vnd.cups-command"); + + gchar *file_name = NULL; + fd = g_file_open_tmp ("ccXXXXXX", &file_name, &error); + + if (fd != -1 && !error) + { + FILE *file; + + file = fdopen (fd, "w"); + fprintf (file, "#CUPS-COMMAND\n"); + fprintf (file, "%s\n", command); + fclose (file); + + response = cupsDoFileRequest (http, request, "/", file_name); + g_unlink (file_name); + } + + g_free (file_name); + httpClose (http); + } + + return response; +} + +int +ccGetAllowedUsers (gchar ***allowed_users, const char *printer_name) +{ + const char * const attrs[1] = { "requesting-user-name-allowed" }; + http_t *http; + ipp_t *request = NULL; + gchar **users = NULL; + ipp_t *response; + char uri[HTTP_MAX_URI + 1]; + int num_allowed_users = 0; + + http = httpConnectEncrypt (cupsServer (), + ippPort (), + cupsEncryption ()); + + if (http || !allowed_users) + { + request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES); + + g_snprintf (uri, sizeof (uri), "ipp://localhost/printers/%s", printer_name); + ippAddString (request, + IPP_TAG_OPERATION, + IPP_TAG_URI, + "printer-uri", + NULL, + uri); + ippAddStrings (request, + IPP_TAG_OPERATION, + IPP_TAG_KEYWORD, + "requested-attributes", + 1, + NULL, + attrs); + + response = cupsDoRequest (http, request, "/"); + if (response) + { + ipp_attribute_t *attr = NULL; + ipp_attribute_t *allowed = NULL; + + for (attr = response->attrs; attr != NULL; attr = attr->next) + { + if (attr->group_tag == IPP_TAG_PRINTER && + attr->value_tag == IPP_TAG_NAME && + !g_strcmp0 (attr->name, "requesting-user-name-allowed")) + allowed = attr; + } + + if (allowed && allowed->num_values > 0) + { + int i; + + num_allowed_users = allowed->num_values; + users = g_new (gchar*, num_allowed_users); + + for (i = 0; i < num_allowed_users; i ++) + users[i] = g_strdup (allowed->values[i].string.text); + } + ippDelete(response); + } + httpClose (http); + } + + *allowed_users = users; + return num_allowed_users; +} + +gchar * +get_ppd_attribute (const gchar *printer_name, const gchar *attribute_name) +{ + const char *file_name = NULL; + ppd_file_t *ppd_file = NULL; + ppd_attr_t *ppd_attr = NULL; + gchar *result = NULL; + + file_name = cupsGetPPD (printer_name); + + if (file_name) + { + ppd_file = ppdOpenFile (file_name); + if (ppd_file) + { + ppd_attr = ppdFindAttr (ppd_file, attribute_name, NULL); + if (ppd_attr != NULL) + result = g_strdup (ppd_attr->value); + ppdClose (ppd_file); + } + g_unlink (file_name); + } + + return result; +} diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h new file mode 100644 index 000000000..e314f5bbd --- /dev/null +++ b/panels/printers/pp-utils.h @@ -0,0 +1,59 @@ +/* -*- 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. + * + */ + +#ifndef __PP_UTILS_H__ +#define __PP_UTILS_H__ + +#include <gtk/gtk.h> +#include <dbus/dbus-glib.h> + +G_BEGIN_DECLS + +DBusGProxy *get_dbus_proxy (const gchar *name, + const gchar *path, + const gchar *iface, + const gboolean system_bus); + +gchar *get_tag_value (const gchar *tag_string, + const gchar *tag_name); + +gchar *get_ppd_name (gchar *device_class, + gchar *device_id, + gchar *device_info, + gchar *device_make_and_model, + gchar *device_uri, + gchar *device_location); + +char *get_dest_attr (const char *dest_name, + const char *attr); + +ipp_t *execute_maintenance_command (const char *printer_name, + const char *command, + const char *title); + +int ccGetAllowedUsers (gchar ***allowed_users, + const char *printer_name); + +gchar *get_ppd_attribute (const gchar *printer_name, + const gchar *attribute_name); + +G_END_DECLS + +#endif /* __PP_UTILS_H */ |