diff options
Diffstat (limited to 'plugins/print-notifications/gsd-printer.c')
-rw-r--r-- | plugins/print-notifications/gsd-printer.c | 1403 |
1 files changed, 1403 insertions, 0 deletions
diff --git a/plugins/print-notifications/gsd-printer.c b/plugins/print-notifications/gsd-printer.c new file mode 100644 index 0000000..573129b --- /dev/null +++ b/plugins/print-notifications/gsd-printer.c @@ -0,0 +1,1403 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 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 2, 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, see <http://www.gnu.org/licenses/>. + * + */ + +#include "config.h" +#include <gio/gio.h> +#include <stdlib.h> +#include <libnotify/notify.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> +#include <cups/cups.h> +#include <cups/ppd.h> + +static GDBusNodeInfo *npn_introspection_data = NULL; +static GDBusNodeInfo *pdi_introspection_data = NULL; + +#define SCP_DBUS_NPN_NAME "com.redhat.NewPrinterNotification" +#define SCP_DBUS_NPN_PATH "/com/redhat/NewPrinterNotification" +#define SCP_DBUS_NPN_INTERFACE "com.redhat.NewPrinterNotification" + +#define SCP_DBUS_PDI_NAME "com.redhat.PrinterDriversInstaller" +#define SCP_DBUS_PDI_PATH "/com/redhat/PrinterDriversInstaller" +#define SCP_DBUS_PDI_INTERFACE "com.redhat.PrinterDriversInstaller" + +#define PACKAGE_KIT_BUS "org.freedesktop.PackageKit" +#define PACKAGE_KIT_PATH "/org/freedesktop/PackageKit" +#define PACKAGE_KIT_MODIFY_IFACE "org.freedesktop.PackageKit.Modify" +#define PACKAGE_KIT_QUERY_IFACE "org.freedesktop.PackageKit.Query" + +#define SCP_BUS "org.fedoraproject.Config.Printing" +#define SCP_PATH "/org/fedoraproject/Config/Printing" +#define SCP_IFACE "org.fedoraproject.Config.Printing" + +#define MECHANISM_BUS "org.opensuse.CupsPkHelper.Mechanism" + +#define ALLOWED_CHARACTERS "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" + +#define DBUS_TIMEOUT 60000 +#define DBUS_INSTALL_TIMEOUT 3600000 + +#define GNOME_SESSION_DBUS_NAME "org.gnome.SessionManager" +#define GNOME_SESSION_DBUS_PATH "/org/gnome/SessionManager" +#define GNOME_SESSION_DBUS_IFACE "org.gnome.SessionManager" +#define GNOME_SESSION_CLIENT_PRIVATE_DBUS_IFACE "org.gnome.SessionManager.ClientPrivate" + +#define GNOME_SESSION_PRESENCE_DBUS_PATH "/org/gnome/SessionManager/Presence" +#define GNOME_SESSION_PRESENCE_DBUS_IFACE "org.gnome.SessionManager.Presence" + +#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) +#define HAVE_CUPS_1_6 1 +#endif + +#ifndef HAVE_CUPS_1_6 +#define ippGetState(ipp) ipp->state +#endif + +enum { + PRESENCE_STATUS_AVAILABLE = 0, + PRESENCE_STATUS_INVISIBLE, + PRESENCE_STATUS_BUSY, + PRESENCE_STATUS_IDLE, + PRESENCE_STATUS_UNKNOWN +}; + +static const gchar npn_introspection_xml[] = + "<node name='/com/redhat/NewPrinterNotification'>" + " <interface name='com.redhat.NewPrinterNotification'>" + " <method name='GetReady'>" + " </method>" + " <method name='NewPrinter'>" + " <arg type='i' name='status' direction='in'/>" + " <arg type='s' name='name' direction='in'/>" + " <arg type='s' name='mfg' direction='in'/>" + " <arg type='s' name='mdl' direction='in'/>" + " <arg type='s' name='des' direction='in'/>" + " <arg type='s' name='cmd' direction='in'/>" + " </method>" + " </interface>" + "</node>"; + +static const gchar pdi_introspection_xml[] = + "<node name='/com/redhat/PrinterDriversInstaller'>" + " <interface name='com.redhat.PrinterDriversInstaller'>" + " <method name='InstallDrivers'>" + " <arg type='s' name='mfg' direction='in'/>" + " <arg type='s' name='mdl' direction='in'/>" + " <arg type='s' name='cmd' direction='in'/>" + " </method>" + " </interface>" + "</node>"; + +static GMainLoop *main_loop; +static guint npn_registration_id; +static guint pdi_registration_id; +static guint npn_owner_id; +static guint pdi_owner_id; + +static GHashTable * +get_missing_executables (const gchar *ppd_file_name) +{ + GHashTable *executables = NULL; + GDBusProxy *proxy; + GVariant *output; + GVariant *array; + GError *error = NULL; + gint i; + + if (!ppd_file_name) + return NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + SCP_BUS, + SCP_PATH, + SCP_IFACE, + NULL, + &error); + + if (!proxy) { + g_warning ("%s", error->message); + g_error_free (error); + return NULL; + } + + output = g_dbus_proxy_call_sync (proxy, + "MissingExecutables", + g_variant_new ("(s)", + ppd_file_name), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output && g_variant_n_children (output) == 1) { + array = g_variant_get_child_value (output, 0); + if (array) { + executables = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + for (i = 0; i < g_variant_n_children (array); i++) { + g_hash_table_insert (executables, + g_strdup (g_variant_get_string ( + g_variant_get_child_value (array, i), + NULL)), + NULL); + } + } + } + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); + + return executables; +} + +static GHashTable * +find_packages_for_executables (GHashTable *executables) +{ + GHashTableIter exec_iter; + GHashTable *packages = NULL; + GDBusProxy *proxy; + GVariant *output; + gpointer key, value; + GError *error = NULL; + + if (!executables || g_hash_table_size (executables) <= 0) + return NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + PACKAGE_KIT_BUS, + PACKAGE_KIT_PATH, + PACKAGE_KIT_QUERY_IFACE, + NULL, + &error); + + if (!proxy) { + g_warning ("%s", error->message); + g_error_free (error); + return NULL; + } + + packages = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + g_hash_table_iter_init (&exec_iter, executables); + while (g_hash_table_iter_next (&exec_iter, &key, &value)) { + output = g_dbus_proxy_call_sync (proxy, + "SearchFile", + g_variant_new ("(ss)", + (gchar *) key, + ""), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output) { + gboolean installed; + gchar *package; + + g_variant_get (output, + "(bs)", + &installed, + &package); + if (!installed) + g_hash_table_insert (packages, g_strdup (package), NULL); + + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + } + + g_object_unref (proxy); + + return packages; +} + +static void +install_packages (GHashTable *packages) +{ + GVariantBuilder array_builder; + GHashTableIter pkg_iter; + GDBusProxy *proxy; + GVariant *output; + gpointer key, value; + GError *error = NULL; + + if (!packages || g_hash_table_size (packages) <= 0) + return; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + PACKAGE_KIT_BUS, + PACKAGE_KIT_PATH, + PACKAGE_KIT_MODIFY_IFACE, + NULL, + &error); + + if (!proxy) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as")); + + g_hash_table_iter_init (&pkg_iter, packages); + while (g_hash_table_iter_next (&pkg_iter, &key, &value)) { + g_variant_builder_add (&array_builder, + "s", + (gchar *) key); + } + + output = g_dbus_proxy_call_sync (proxy, + "InstallPackageNames", + g_variant_new ("(uass)", + 0, + &array_builder, + "hide-finished"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_INSTALL_TIMEOUT, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); +} + +static gchar * +get_best_ppd (gchar *device_id, + gchar *device_make_and_model, + gchar *device_uri) +{ + GDBusProxy *proxy; + GVariant *output; + GVariant *array; + GVariant *tuple; + GError *error = NULL; + gchar *ppd_name = NULL; + gint i, j; + static const char * const match_levels[] = { + "exact-cmd", + "exact", + "close", + "generic", + "none"}; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + SCP_BUS, + SCP_PATH, + SCP_IFACE, + NULL, + &error); + + if (!proxy) { + g_warning ("%s", error->message); + g_error_free (error); + return NULL; + } + + output = g_dbus_proxy_call_sync (proxy, + "GetBestDrivers", + g_variant_new ("(sss)", + device_id ? device_id : "", + device_make_and_model ? device_make_and_model : "", + device_uri ? device_uri : ""), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output && g_variant_n_children (output) >= 1) { + array = g_variant_get_child_value (output, 0); + if (array) + for (j = 0; j < G_N_ELEMENTS (match_levels) && ppd_name == NULL; j++) + for (i = 0; i < g_variant_n_children (array) && ppd_name == NULL; i++) { + tuple = g_variant_get_child_value (array, i); + if (tuple && g_variant_n_children (tuple) == 2) { + if (g_strcmp0 (g_variant_get_string ( + g_variant_get_child_value (tuple, 1), + NULL), match_levels[j]) == 0) + ppd_name = g_strdup (g_variant_get_string ( + g_variant_get_child_value (tuple, 0), + NULL)); + } + } + } + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); + + return ppd_name; +} + +static gchar * +get_tag_value (const gchar *tag_string, + const gchar *tag_name) +{ + gchar **tag_string_splitted; + gchar *tag_value = NULL; + gint tag_name_length; + gint i; + + if (!tag_string || + !tag_name) + return NULL; + + tag_name_length = strlen (tag_name); + tag_string_splitted = g_strsplit (tag_string, ";", 0); + if (tag_string_splitted) { + 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; +} + +static gchar * +create_name (gchar *device_id) +{ + cups_dest_t *dests; + gboolean already_present = FALSE; + gchar *name = NULL; + gchar *new_name = NULL; + gint num_dests; + gint name_index = 2; + gint j; + + g_return_val_if_fail (device_id != NULL, NULL); + + name = get_tag_value (device_id, "mdl"); + if (!name) + name = get_tag_value (device_id, "model"); + + if (name) + name = g_strcanon (name, ALLOWED_CHARACTERS, '-'); + + 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); + + return name; +} + +static gboolean +add_printer (gchar *printer_name, + gchar *device_uri, + gchar *ppd_name, + gchar *info, + gchar *location) +{ + cups_dest_t *dests; + GDBusProxy *proxy; + gboolean success = FALSE; + GVariant *output; + GError *error = NULL; + gint num_dests; + gint i; + + if (!printer_name || !device_uri || !ppd_name) + return FALSE; + + 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) { + g_warning ("%s", error->message); + g_error_free (error); + return FALSE; + } + + output = g_dbus_proxy_call_sync (proxy, + "PrinterAdd", + g_variant_new ("(sssss)", + printer_name, + device_uri, + ppd_name, + info ? info : "", + location ? location : ""), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); + + num_dests = cupsGetDests (&dests); + for (i = 0; i < num_dests; i++) + if (g_strcmp0 (dests[i].name, printer_name) == 0) + success = TRUE; + cupsFreeDests (num_dests, dests); + + return success; +} + +static gboolean +printer_set_enabled (const gchar *printer_name, + gboolean enabled) +{ + GDBusProxy *proxy; + gboolean result = TRUE; + GVariant *output; + GError *error = NULL; + + if (!printer_name) + return FALSE; + + 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) { + g_warning ("%s", error->message); + g_error_free (error); + return FALSE; + } + + output = g_dbus_proxy_call_sync (proxy, + "PrinterSetEnabled", + g_variant_new ("(sb)", + printer_name, + enabled), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + result = FALSE; + } + + g_object_unref (proxy); + + return result; +} + +static gboolean +printer_set_accepting_jobs (const gchar *printer_name, + gboolean accepting_jobs, + const gchar *reason) +{ + GDBusProxy *proxy; + gboolean result = TRUE; + GVariant *output; + GError *error = NULL; + + if (!printer_name) + return FALSE; + + 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) { + g_warning ("%s", error->message); + g_error_free (error); + return FALSE; + } + + output = g_dbus_proxy_call_sync (proxy, + "PrinterSetAcceptJobs", + g_variant_new ("(sbs)", + printer_name, + accepting_jobs, + reason ? reason : ""), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + result = FALSE; + } + + g_object_unref (proxy); + + return result; +} + +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; + gchar *file_name = NULL; + char *uri; + int fd = -1; + + http = httpConnectEncrypt (cupsServer (), + ippPort (), + cupsEncryption ()); + + if (!http) + return NULL; + + request = ippNewRequest (IPP_PRINT_JOB); + + uri = g_strdup_printf ("ipp://localhost/printers/%s", + printer_name); + + ippAddString (request, + IPP_TAG_OPERATION, + IPP_TAG_URI, + "printer-uri", + NULL, + uri); + + g_free (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"); + + fd = g_file_open_tmp ("ccXXXXXX", &file_name, &error); + + if (fd != -1) { + 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); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_free (file_name); + httpClose (http); + + return response; +} + +static 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; +} + +static void +printer_autoconfigure (gchar *printer_name) +{ + gchar *commands; + gchar *commands_lowercase; + ipp_t *response = NULL; + + if (!printer_name) + return; + + commands = get_dest_attr (printer_name, "printer-commands"); + commands_lowercase = g_ascii_strdown (commands, -1); + + if (g_strrstr (commands_lowercase, "autoconfigure")) { + response = execute_maintenance_command (printer_name, + "AutoConfigure", + ("Automatic configuration")); + if (response) { + if (ippGetState (response) == IPP_ERROR) + g_warning ("An error has occured during automatic configuration of new printer."); + ippDelete (response); + } + } + g_free (commands); + g_free (commands_lowercase); +} + +/* Returns default page size for current locale */ +static const gchar * +get_page_size_from_locale (void) +{ + if (g_str_equal (gtk_paper_size_get_default (), GTK_PAPER_NAME_LETTER)) + return "Letter"; + else + return "A4"; +} + +static void +set_default_paper_size (const gchar *printer_name, + const gchar *ppd_file_name) +{ + GDBusProxy *proxy; + GVariant *output; + GError *error = NULL; + GVariantBuilder *builder; + + 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) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + /* Set default media size according to the locale + * FIXME: Handle more than A4 and Letter: + * https://bugzilla.gnome.org/show_bug.cgi?id=660769 */ + builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); + g_variant_builder_add (builder, "s", get_page_size_from_locale ()); + + output = g_dbus_proxy_call_sync (proxy, + "PrinterAddOption", + g_variant_new ("(ssas)", + printer_name ? printer_name : "", + "PageSize", + builder), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } else { + if (!(error->domain == G_DBUS_ERROR && + (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN || + error->code == G_DBUS_ERROR_UNKNOWN_METHOD))) + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); +} + +/* + * Setup new printer and returns TRUE if successful. + */ +static gboolean +setup_printer (gchar *device_id, + gchar *device_make_and_model, + gchar *device_uri) +{ + gboolean success = FALSE; + gchar *ppd_name; + gchar *printer_name; + + ppd_name = get_best_ppd (device_id, device_make_and_model, device_uri); + printer_name = create_name (device_id); + + if (!ppd_name || !printer_name || !device_uri) { + g_free (ppd_name); + g_free (printer_name); + return FALSE; + } + + success = add_printer (printer_name, device_uri, + ppd_name, NULL, NULL); + + /* Set some options of the new printer */ + if (success) { + const char *ppd_file_name; + + printer_set_accepting_jobs (printer_name, TRUE, NULL); + printer_set_enabled (printer_name, TRUE); + printer_autoconfigure (printer_name); + + ppd_file_name = cupsGetPPD (printer_name); + + if (ppd_file_name) { + GHashTable *executables; + GHashTable *packages; + + set_default_paper_size (printer_name, ppd_file_name); + + executables = get_missing_executables (ppd_file_name); + packages = find_packages_for_executables (executables); + install_packages (packages); + + if (executables) + g_hash_table_destroy (executables); + if (packages) + g_hash_table_destroy (packages); + g_unlink (ppd_file_name); + } + } + + g_free (printer_name); + g_free (ppd_name); + + return success; +} + +static void +handle_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + gchar *primary_text = NULL; + gchar *secondary_text = NULL; + gchar *name = NULL; + gchar *mfg = NULL; + gchar *mdl = NULL; + gchar *des = NULL; + gchar *cmd = NULL; + gchar *device = NULL; + gchar *device_id; + gchar *make_and_model; + gint status = 0; + + if (g_strcmp0 (method_name, "GetReady") == 0) { + /* Translators: We are configuring new printer */ + primary_text = g_strdup (_("Configuring new printer")); + /* Translators: Just wait */ + secondary_text = g_strdup (_("Please wait…")); + + g_dbus_method_invocation_return_value (invocation, + NULL); + } + else if (g_strcmp0 (method_name, "NewPrinter") == 0) { + if (g_variant_n_children (parameters) == 6) { + g_variant_get (parameters, "(i&s&s&s&s&s)", + &status, + &name, + &mfg, + &mdl, + &des, + &cmd); + } + + if (g_strrstr (name, "/")) { + /* name is a URI, no queue was generated, because no suitable + * driver was found + */ + + device_id = g_strdup_printf ("MFG:%s;MDL:%s;DES:%s;CMD:%s;", mfg, mdl, des, cmd); + make_and_model = g_strdup_printf ("%s %s", mfg, mdl); + + if (!setup_printer (device_id, make_and_model, name)) { + + /* Translators: We have no driver installed for this printer */ + primary_text = g_strdup (_("Missing printer driver")); + + if ((mfg && mdl) || des) { + if (mfg && mdl) + device = g_strdup_printf ("%s %s", mfg, mdl); + else + device = g_strdup (des); + + /* Translators: We have no driver installed for the device */ + secondary_text = g_strdup_printf (_("No printer driver for %s."), device); + g_free (device); + } + else + /* Translators: We have no driver installed for this printer */ + secondary_text = g_strdup (_("No driver for this printer.")); + } + + g_free (make_and_model); + g_free (device_id); + } + else { + /* name is the name of the queue which hal_lpadmin has set up + * automatically. + */ + + const char *ppd_file_name; + + ppd_file_name = cupsGetPPD (name); + if (ppd_file_name) { + GHashTable *executables; + GHashTable *packages; + + executables = get_missing_executables (ppd_file_name); + packages = find_packages_for_executables (executables); + install_packages (packages); + + if (executables) + g_hash_table_destroy (executables); + if (packages) + g_hash_table_destroy (packages); + g_unlink (ppd_file_name); + } + } + + g_dbus_method_invocation_return_value (invocation, + NULL); + } + else if (g_strcmp0 (method_name, "InstallDrivers") == 0) { + GDBusProxy *proxy; + GError *error = NULL; + + if (g_variant_n_children (parameters) == 3) { + g_variant_get (parameters, "(&s&s&s)", + &mfg, + &mdl, + &cmd); + } + + if (mfg && mdl) + device = g_strdup_printf ("MFG:%s;MDL:%s;", mfg, mdl); + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + PACKAGE_KIT_BUS, + PACKAGE_KIT_PATH, + PACKAGE_KIT_MODIFY_IFACE, + NULL, + &error); + + if (!proxy) { + g_warning ("%s", error->message); + g_error_free (error); + } + + if (proxy && device) { + GVariantBuilder *builder; + GVariant *output; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); + g_variant_builder_add (builder, "s", device); + + output = g_dbus_proxy_call_sync (proxy, + "InstallPrinterDrivers", + g_variant_new ("(uass)", + 0, + builder, + "hide-finished"), + G_DBUS_CALL_FLAGS_NONE, + DBUS_INSTALL_TIMEOUT, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); + } + + g_dbus_method_invocation_return_value (invocation, + NULL); + } + + if (primary_text) { + NotifyNotification *notification; + notification = notify_notification_new (primary_text, + secondary_text, + "printer-symbolic"); + notify_notification_set_app_name (notification, _("Printers")); + notify_notification_set_hint_string (notification, "desktop-entry", "gnome-printers-panel"); + notify_notification_set_hint (notification, "transient", g_variant_new_boolean (TRUE)); + + notify_notification_show (notification, NULL); + g_object_unref (notification); + g_free (primary_text); + g_free (secondary_text); + } +} + +static const GDBusInterfaceVTable interface_vtable = +{ + handle_method_call, + NULL, + NULL +}; + +static void +unregister_objects () +{ + GDBusConnection *system_connection; + GError *error = NULL; + + system_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + + if (npn_registration_id > 0) { + g_dbus_connection_unregister_object (system_connection, npn_registration_id); + npn_registration_id = 0; + } + + if (pdi_registration_id > 0) { + g_dbus_connection_unregister_object (system_connection, pdi_registration_id); + pdi_registration_id = 0; + } +} + +static void +unown_names () +{ + if (npn_owner_id > 0) { + g_bus_unown_name (npn_owner_id); + npn_owner_id = 0; + } + + if (pdi_owner_id > 0) { + g_bus_unown_name (pdi_owner_id); + pdi_owner_id = 0; + } +} + +static void +on_npn_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GError *error = NULL; + + npn_registration_id = g_dbus_connection_register_object (connection, + SCP_DBUS_NPN_PATH, + npn_introspection_data->interfaces[0], + &interface_vtable, + NULL, + NULL, + &error); + + if (npn_registration_id == 0) { + g_warning ("Failed to register object: %s\n", error->message); + g_error_free (error); + } +} + +static void +on_pdi_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GError *error = NULL; + + pdi_registration_id = g_dbus_connection_register_object (connection, + SCP_DBUS_PDI_PATH, + pdi_introspection_data->interfaces[0], + &interface_vtable, + NULL, + NULL, + &error); + + if (pdi_registration_id == 0) { + g_warning ("Failed to register object: %s\n", error->message); + g_error_free (error); + } +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + unregister_objects (); +} + +static void +session_signal_handler (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + guint new_status; + + g_variant_get (parameters, "(u)", &new_status); + + if (new_status == PRESENCE_STATUS_IDLE || + new_status == PRESENCE_STATUS_AVAILABLE) { + unregister_objects (); + unown_names (); + + if (new_status == PRESENCE_STATUS_AVAILABLE) { + npn_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + SCP_DBUS_NPN_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE, + on_npn_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + pdi_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + SCP_DBUS_PDI_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE, + on_pdi_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + } + } +} + +static void +client_signal_handler (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GDBusProxy *proxy; + GError *error = NULL; + GVariant *output; + + if (g_strcmp0 (signal_name, "QueryEndSession") == 0 || + g_strcmp0 (signal_name, "EndSession") == 0) { + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + sender_name, + object_path, + interface_name, + NULL, + &error); + + if (proxy) { + output = g_dbus_proxy_call_sync (proxy, + "EndSessionResponse", + g_variant_new ("(bs)", TRUE, ""), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if (output) { + g_variant_unref (output); + } + else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); + } + else { + g_warning ("%s", error->message); + g_error_free (error); + } + + if (g_strcmp0 (signal_name, "EndSession") == 0) { + g_main_loop_quit (main_loop); + g_debug ("Exiting gsd-printer"); + } + } +} + +static gchar * +register_gnome_session_client (const gchar *app_id, + const gchar *client_startup_id) +{ + GDBusProxy *proxy; + GVariant *output = NULL; + GError *error = NULL; + const gchar *client_id = NULL; + gchar *result = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + GNOME_SESSION_DBUS_NAME, + GNOME_SESSION_DBUS_PATH, + GNOME_SESSION_DBUS_IFACE, + NULL, + &error); + + if (proxy) { + output = g_dbus_proxy_call_sync (proxy, + "RegisterClient", + g_variant_new ("(ss)", app_id, client_startup_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if (output) { + g_variant_get (output, "(o)", &client_id); + if (client_id) + result = g_strdup (client_id); + g_variant_unref (output); + } + else { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (proxy); + } + else { + g_warning ("%s", error->message); + g_error_free (error); + } + + return result; +} + +int +main (int argc, char *argv[]) +{ + GDBusConnection *connection; + gboolean client_signal_subscription_set = FALSE; + GError *error = NULL; + guint client_signal_subscription_id; + guint session_signal_subscription_id; + gchar *object_path; + + bindtextdomain (GETTEXT_PACKAGE, GNOME_SETTINGS_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + npn_registration_id = 0; + pdi_registration_id = 0; + npn_owner_id = 0; + pdi_owner_id = 0; + + notify_init ("gnome-settings-daemon-printer"); + + npn_introspection_data = + g_dbus_node_info_new_for_xml (npn_introspection_xml, &error); + + if (npn_introspection_data == NULL) { + g_warning ("Error parsing introspection XML: %s\n", error->message); + g_error_free (error); + goto error; + } + + pdi_introspection_data = + g_dbus_node_info_new_for_xml (pdi_introspection_xml, &error); + + if (pdi_introspection_data == NULL) { + g_warning ("Error parsing introspection XML: %s\n", error->message); + g_error_free (error); + goto error; + } + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + session_signal_subscription_id = + g_dbus_connection_signal_subscribe (connection, + NULL, + GNOME_SESSION_PRESENCE_DBUS_IFACE, + "StatusChanged", + GNOME_SESSION_PRESENCE_DBUS_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + session_signal_handler, + NULL, + NULL); + + object_path = register_gnome_session_client ("gsd-printer", ""); + if (object_path) { + client_signal_subscription_id = + g_dbus_connection_signal_subscribe (connection, + NULL, + GNOME_SESSION_CLIENT_PRIVATE_DBUS_IFACE, + NULL, + object_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + client_signal_handler, + NULL, + NULL); + client_signal_subscription_set = TRUE; + } + + if (npn_owner_id == 0) + npn_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + SCP_DBUS_NPN_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE, + on_npn_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + if (pdi_owner_id == 0) + pdi_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + SCP_DBUS_PDI_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE, + on_pdi_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + main_loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (main_loop); + + unregister_objects (); + unown_names (); + + if (client_signal_subscription_set) + g_dbus_connection_signal_unsubscribe (connection, client_signal_subscription_id); + g_dbus_connection_signal_unsubscribe (connection, session_signal_subscription_id); + + g_free (object_path); + + g_dbus_node_info_unref (npn_introspection_data); + g_dbus_node_info_unref (pdi_introspection_data); + + return 0; + +error: + + if (npn_introspection_data) + g_dbus_node_info_unref (npn_introspection_data); + + if (pdi_introspection_data) + g_dbus_node_info_unref (pdi_introspection_data); + + return 1; +} |