/* -*- 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 .
*
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
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[] =
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"";
static const gchar pdi_introspection_xml[] =
""
" "
" "
" "
" "
" "
" "
" "
"";
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;
}