Commit c459da1c authored by Cosimo Cecchi's avatar Cosimo Cecchi Committed by Stef Walter

Port gnome-keyring-daemon to GDBus

This commit ports gnome-keyring-daemon from libdbus1 to GDBus.
Unfortunately it's not really possible to split this commit into
multiple while keeping the code building.

Noteworthy is the different approach that GDBus allows us to use;
instead of filtering every message and writing our own internal message
routing, we can more naturally use generated skeletons and
register/unregister them on the bus as needed.

https://bugzilla.gnome.org/show_bug.cgi?id=622905
parent 3c055532
......@@ -72,7 +72,7 @@ PKG_CHECK_MODULES(GOBJECT, glib-2.0 gobject-2.0)
AC_SUBST(GOBJECT_CFLAGS)
AC_SUBST(GOBJECT_LIBS)
PKG_CHECK_MODULES(GIO, glib-2.0 gio-2.0)
PKG_CHECK_MODULES(GIO, glib-2.0 gio-2.0 gio-unix-2.0)
AC_SUBST(GIO_CFLAGS)
AC_SUBST(GIO_LIBS)
......
noinst_LTLIBRARIES += libgkd-dbus.la
daemon/dbus/gkd-secrets-generated.h: daemon/dbus/org.freedesktop.Secrets.xml Makefile.am
$(AM_V_GEN) gdbus-codegen --interface-prefix org.freedesktop.Secrets. \
--generate-c-code $(srcdir)/daemon/dbus/gkd-secrets-generated \
--c-namespace Gkd \
$(srcdir)/daemon/dbus/org.freedesktop.Secrets.xml
daemon/dbus/gkd-secrets-generated.c: daemon/dbus/gkd-secrets-generated.h
@: # generated as a side-effect
daemon/dbus/gkd-daemon-generated.h: daemon/dbus/org.gnome.keyring.Daemon.xml Makefile.am
$(AM_V_GEN) gdbus-codegen --interface-prefix org.gnome.keyring.Daemon. \
--generate-c-code $(srcdir)/daemon/dbus/gkd-daemon-generated \
--c-namespace Gkd \
$(srcdir)/daemon/dbus/org.gnome.keyring.Daemon.xml
daemon/dbus/gkd-daemon-generated.c: daemon/dbus/gkd-daemon-generated.h
@: # generated as a side-effect
daemon/dbus/gkd-internal-generated.h: daemon/dbus/org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.xml Makefile.am
$(AM_V_GEN) gdbus-codegen --interface-prefix org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface. \
--generate-c-code $(srcdir)/daemon/dbus/gkd-internal-generated \
--c-namespace Gkd \
$(srcdir)/daemon/dbus/org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.xml
daemon/dbus/gkd-internal-generated.c: daemon/dbus/gkd-internal-generated.h
@: # generated as a side-effect
BUILT_SOURCES += \
daemon/dbus/gkd-daemon-generated.c \
daemon/dbus/gkd-daemon-generated.h \
daemon/dbus/gkd-internal-generated.c \
daemon/dbus/gkd-internal-generated.h \
daemon/dbus/gkd-secrets-generated.c \
daemon/dbus/gkd-secrets-generated.h
libgkd_dbus_la_SOURCES = \
$(BUILT_SOURCES) \
daemon/dbus/gkd-dbus.c \
daemon/dbus/gkd-dbus.h \
daemon/dbus/gkd-dbus-environment.c \
daemon/dbus/gkd-dbus-private.h \
daemon/dbus/gkd-dbus-secrets.c \
daemon/dbus/gkd-dbus-session.c \
daemon/dbus/gkd-dbus-util.c \
daemon/dbus/gkd-dbus-util.h \
daemon/dbus/gkd-secret-change.c \
daemon/dbus/gkd-secret-change.h \
daemon/dbus/gkd-secret-create.c \
......@@ -20,8 +51,6 @@ libgkd_dbus_la_SOURCES = \
daemon/dbus/gkd-secret-error.h \
daemon/dbus/gkd-secret-exchange.c \
daemon/dbus/gkd-secret-exchange.h \
daemon/dbus/gkd-secret-introspect.c \
daemon/dbus/gkd-secret-introspect.h \
daemon/dbus/gkd-secret-lock.c \
daemon/dbus/gkd-secret-lock.h \
daemon/dbus/gkd-secret-objects.c \
......@@ -44,12 +73,14 @@ libgkd_dbus_la_SOURCES = \
$(NULL)
libgkd_dbus_la_LIBADD = \
$(GIO_LIBS) \
$(GLIB_LIBS) \
$(GOBJECT_LIBS)
libgkd_dbus_la_CFLAGS = \
$(DAEMON_CFLAGS) \
$(GCR_BASE_CFLAGS) \
$(GIO_CFLAGS) \
$(GOBJECT_CFLAGS)
# -------------------------------------------------------------------
......
......@@ -26,8 +26,6 @@
#include "daemon/gkd-util.h"
#include <dbus/dbus.h>
#include <string.h>
#define SERVICE_SESSION_MANAGER "org.gnome.SessionManager"
......@@ -35,38 +33,36 @@
#define IFACE_SESSION_MANAGER "org.gnome.SessionManager"
void
gkd_dbus_environment_cleanup (DBusConnection *conn)
gkd_dbus_environment_cleanup (GDBusConnection *conn)
{
/* Nothing to do here */
}
static void
on_setenv_reply (DBusPendingCall *pending, void *user_data)
on_setenv_reply (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
DBusMessage *reply;
DBusError derr = DBUS_ERROR_INIT;
GError *error = NULL;
reply = dbus_pending_call_steal_reply (pending);
g_return_if_fail (reply);
g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
if (dbus_set_error_from_message (&derr, reply)) {
if (dbus_error_has_name (&derr, "org.gnome.SessionManager.NotInInitialization") ||
dbus_error_has_name (&derr, DBUS_ERROR_SERVICE_UNKNOWN))
g_debug ("couldn't set environment variable in session: %s", derr.message);
if (error != NULL) {
gchar *dbus_error;
dbus_error = g_dbus_error_get_remote_error (error);
if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) ||
g_strcmp0 (dbus_error, "org.gnome.SessionManager.NotInInitialization"))
g_debug ("couldn't set environment variable in session: %s", error->message);
else
g_message ("couldn't set environment variable in session: %s", derr.message);
dbus_error_free (&derr);
g_message ("couldn't set environment variable in session: %s", error->message);
g_error_free (error);
g_free (dbus_error);
}
dbus_message_unref (reply);
}
static void
setenv_request (DBusConnection *conn, const gchar *env)
setenv_request (GDBusConnection *conn, const gchar *env)
{
DBusPendingCall *pending = NULL;
DBusError derr = DBUS_ERROR_INIT;
DBusMessage *msg;
const gchar *value;
gchar *name;
......@@ -78,43 +74,31 @@ setenv_request (DBusConnection *conn, const gchar *env)
name = g_strndup (env, value - env);
++value;
msg = dbus_message_new_method_call (SERVICE_SESSION_MANAGER,
PATH_SESSION_MANAGER,
IFACE_SESSION_MANAGER,
"Setenv");
g_return_if_fail (msg);
if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &value,
DBUS_TYPE_INVALID))
g_return_if_reached ();
g_dbus_connection_call (conn,
SERVICE_SESSION_MANAGER,
PATH_SESSION_MANAGER,
IFACE_SESSION_MANAGER,
"Setenv",
g_variant_new ("(ss)",
name,
value),
NULL, G_DBUS_CALL_FLAGS_NONE,
-1, NULL,
on_setenv_reply, NULL);
g_free (name);
value = name = NULL;
/* Send message and get a handle for a reply */
dbus_connection_send_with_reply (conn, msg, &pending, -1);
dbus_message_unref (msg);
if (pending) {
dbus_pending_call_set_notify (pending, on_setenv_reply, NULL, NULL);
dbus_pending_call_unref (pending);
} else {
g_warning ("couldn't send dbus message: %s",
derr.message ? derr.message : "");
dbus_error_free (&derr);
}
}
static void
on_watch_environment (gpointer data, gpointer user_data)
{
DBusConnection *conn = user_data;
GDBusConnection *conn = user_data;
const gchar *env = data;
setenv_request (conn, env);
}
void
gkd_dbus_environment_init (DBusConnection *conn)
gkd_dbus_environment_init (GDBusConnection *conn)
{
const gchar **envp;
......@@ -127,6 +111,6 @@ gkd_dbus_environment_init (DBusConnection *conn)
for (; *envp; ++envp)
setenv_request (conn, *envp);
gkd_util_watch_environment (on_watch_environment, dbus_connection_ref (conn),
(GDestroyNotify)dbus_connection_unref);
gkd_util_watch_environment (on_watch_environment, g_object_ref (conn),
(GDestroyNotify) g_object_unref);
}
......@@ -24,22 +24,22 @@
#define GKD_DBUS_PRIVATE_H
#include <glib.h>
#include <dbus/dbus.h>
#include <gio/gio.h>
/* DBus environment variables sent to session */
void gkd_dbus_environment_init (DBusConnection *conn);
void gkd_dbus_environment_cleanup (DBusConnection *conn);
void gkd_dbus_environment_init (GDBusConnection *conn);
void gkd_dbus_environment_cleanup (GDBusConnection *conn);
/* The gnome-keyring Dbus service, very simple */
void gkd_dbus_service_init (DBusConnection *conn);
void gkd_dbus_service_cleanup (DBusConnection *conn);
void gkd_dbus_service_init (GDBusConnection *conn);
void gkd_dbus_service_cleanup (GDBusConnection *conn);
/* DBus desktop session interaction */
void gkd_dbus_session_init (DBusConnection *conn);
void gkd_dbus_session_cleanup (DBusConnection *conn);
void gkd_dbus_session_init (GDBusConnection *conn);
void gkd_dbus_session_cleanup (GDBusConnection *conn);
/* DBus secrets API */
void gkd_dbus_secrets_init (DBusConnection *conn);
void gkd_dbus_secrets_cleanup (DBusConnection *conn);
void gkd_dbus_secrets_init (GDBusConnection *conn);
void gkd_dbus_secrets_cleanup (GDBusConnection *conn);
#endif /* GKD_DBUS_PRIVATE_H */
......@@ -33,7 +33,7 @@
#include <gck/gck.h>
static DBusConnection *dbus_conn = NULL;
static GDBusConnection *dbus_conn = NULL;
static GkdSecretService *secrets_service = NULL;
static GckSlot*
......@@ -66,18 +66,19 @@ calculate_secrets_slot (void)
gboolean
gkd_dbus_secrets_startup (void)
{
DBusError error = DBUS_ERROR_INIT;
dbus_uint32_t result = 0;
const gchar *service = NULL;
unsigned int flags = 0;
GckSlot *slot;
GError *error = NULL;
GVariant *request_variant;
guint res;
g_return_val_if_fail (dbus_conn, FALSE);
#ifdef WITH_DEBUG
service = g_getenv ("GNOME_KEYRING_TEST_SERVICE");
if (service && service[0])
flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT | DBUS_NAME_FLAG_REPLACE_EXISTING;
flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE;
else
#endif
service = SECRET_SERVICE;
......@@ -87,29 +88,37 @@ gkd_dbus_secrets_startup (void)
g_return_val_if_fail (slot, FALSE);
/* Try and grab our name */
result = dbus_bus_request_name (dbus_conn, service, flags, &error);
if (dbus_error_is_set (&error)) {
request_variant = g_dbus_connection_call_sync (dbus_conn,
"org.freedesktop.DBus", /* bus name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"RequestName", /* method name */
g_variant_new ("(su)",
service,
flags),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &error);
if (error != NULL) {
g_message ("couldn't request name '%s' on session bus: %s",
service, error.message);
dbus_error_free (&error);
service, error->message);
g_error_free (error);
} else {
switch (result) {
g_variant_get (request_variant, "(u)", &res);
g_variant_unref (request_variant);
switch (res) {
/* We acquired the service name */
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
break;
case 1: /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */
/* We already acquired the service name. */
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
case 4: /* DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER */
break;
/* Another daemon is running */
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
case DBUS_REQUEST_NAME_REPLY_EXISTS:
case 2: /* DBUS_REQUEST_NAME_REPLY_IN_QUEUE */
case 3: /* DBUS_REQUEST_NAME_REPLY_EXISTS */
g_message ("another secret service is running");
break;
default:
g_return_val_if_reached (FALSE);
break;
......@@ -128,19 +137,18 @@ static void
cleanup_dbus_conn (gpointer unused)
{
g_assert (dbus_conn);
dbus_connection_unref (dbus_conn);
dbus_conn = NULL;
g_clear_object (&dbus_conn);
}
void
gkd_dbus_secrets_init (DBusConnection *conn)
gkd_dbus_secrets_init (GDBusConnection *conn)
{
dbus_conn = dbus_connection_ref (conn);
dbus_conn = g_object_ref (conn);
egg_cleanup_register (cleanup_dbus_conn, NULL);
}
void
gkd_dbus_secrets_cleanup (DBusConnection *conn)
gkd_dbus_secrets_cleanup (GDBusConnection *conn)
{
if (secrets_service) {
g_object_run_dispose (G_OBJECT (secrets_service));
......
......@@ -26,8 +26,6 @@
#include "daemon/gkd-main.h"
#include <dbus/dbus.h>
#include <string.h>
#define SERVICE_SESSION_MANAGER "org.gnome.SessionManager"
......@@ -37,108 +35,89 @@
#define IFACE_SESSION_PRIVATE "org.gnome.SessionManager.ClientPrivate"
static gchar *client_session_path = NULL;
static gchar *client_session_rule = NULL;
static guint client_session_signal_id = 0;
static void
send_end_session_response (DBusConnection *conn)
send_end_session_response (GDBusConnection *conn)
{
DBusMessageIter args;
DBusMessage *msg;
DBusMessage *reply;
DBusError derr = { 0 };
const gchar *reason = "";
dbus_bool_t is_ok = TRUE;
gboolean is_ok = TRUE;
GError *error = NULL;
g_return_if_fail (client_session_path);
msg = dbus_message_new_method_call (SERVICE_SESSION_MANAGER,
client_session_path,
IFACE_SESSION_PRIVATE,
"EndSessionResponse");
g_return_if_fail (msg);
dbus_message_iter_init_append (msg, &args);
if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_BOOLEAN, &is_ok) ||
!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &reason))
g_return_if_reached ();
reply = dbus_connection_send_with_reply_and_block (conn, msg, 1000, &derr);
dbus_message_unref (msg);
if (!reply) {
g_message ("dbus failure responding to ending session: %s", derr.message);
g_dbus_connection_call_sync (conn,
SERVICE_SESSION_MANAGER,
client_session_path,
IFACE_SESSION_PRIVATE,
"EndSessionResponse",
g_variant_new ("(bs)",
is_ok,
reason),
NULL,
G_DBUS_CALL_FLAGS_NONE, 1000,
NULL, &error);
if (error != NULL) {
g_message ("dbus failure responding to ending session: %s", error->message);
g_error_free (error);
return;
}
dbus_message_unref (reply);
}
static void
unregister_daemon_in_session (DBusConnection *conn)
unregister_daemon_in_session (GDBusConnection *conn)
{
DBusMessageIter args;
DBusMessage *msg;
if (client_session_rule) {
dbus_bus_remove_match (conn, client_session_rule, NULL);
g_free (client_session_rule);
client_session_rule = NULL;
if (client_session_signal_id) {
g_dbus_connection_signal_unsubscribe (conn, client_session_signal_id);
client_session_signal_id = 0;
}
if (!client_session_path)
return;
msg = dbus_message_new_method_call (SERVICE_SESSION_MANAGER,
PATH_SESSION_MANAGER,
IFACE_SESSION_MANAGER,
"UnregisterClient");
g_return_if_fail (msg);
dbus_message_iter_init_append (msg, &args);
if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_OBJECT_PATH, &client_session_path))
g_return_if_reached ();
dbus_message_set_no_reply (msg, TRUE);
dbus_connection_send (conn, msg, NULL);
dbus_connection_flush (conn);
dbus_message_unref (msg);
g_dbus_connection_call_sync (conn,
SERVICE_SESSION_MANAGER,
PATH_SESSION_MANAGER,
IFACE_SESSION_MANAGER,
"UnregisterClient",
g_variant_new ("(o)", client_session_path),
NULL, G_DBUS_CALL_FLAGS_NONE,
-1, NULL, NULL);
g_free (client_session_path);
client_session_path = NULL;
}
static DBusHandlerResult
signal_filter (DBusConnection *conn, DBusMessage *msg, void *user_data)
static void
signal_filter (GDBusConnection *conn,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
/* Quit the daemon when the session is over */
if (dbus_message_is_signal (msg, IFACE_SESSION_PRIVATE, "Stop")) {
if (g_strcmp0 (signal_name, "Stop") == 0) {
unregister_daemon_in_session (conn);
gkd_main_quit ();
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal (msg, IFACE_SESSION_PRIVATE, "QueryEndSession")) {
} else if (g_strcmp0 (signal_name, "QueryEndSession") == 0) {
send_end_session_response (conn);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal (msg, IFACE_SESSION_PRIVATE, "EndSession")) {
} else if (g_strcmp0 (signal_name, "EndSession") == 0) {
send_end_session_response (conn);
unregister_daemon_in_session (conn);
gkd_main_quit ();
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal (msg, DBUS_INTERFACE_LOCAL, "Disconnected")) {
} else if (g_strcmp0 (signal_name, "Disconnected") == 0) {
gkd_main_quit ();
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
void
gkd_dbus_session_cleanup (DBusConnection *conn)
gkd_dbus_session_cleanup (GDBusConnection *conn)
{
g_free (client_session_path);
client_session_path = NULL;
g_free (client_session_rule);
client_session_rule = NULL;
}
/*
......@@ -146,50 +125,37 @@ gkd_dbus_session_cleanup (DBusConnection *conn)
* session manager via DBus.
*/
void
gkd_dbus_session_init (DBusConnection *conn)
gkd_dbus_session_init (GDBusConnection *conn)
{
DBusMessageIter args;
DBusMessage *msg;
DBusMessage *reply;
DBusError derr = { 0 };
const gchar *app_id = "gnome-keyring-daemon";
const gchar *client_id;
GError *error = NULL;
GVariant *object_path_variant;
client_id = g_getenv ("DESKTOP_AUTOSTART_ID");
if (!client_id)
return;
msg = dbus_message_new_method_call (SERVICE_SESSION_MANAGER,
PATH_SESSION_MANAGER,
IFACE_SESSION_MANAGER,
"RegisterClient");
g_return_if_fail (msg);
dbus_message_iter_init_append (msg, &args);
if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &app_id) ||
!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &client_id))
g_return_if_reached ();
/* Send message and get a handle for a reply */
reply = dbus_connection_send_with_reply_and_block (conn, msg, 1000, &derr);
dbus_message_unref (msg);
if (!reply) {
g_message ("couldn't register in session: %s", derr.message);
dbus_error_free (&derr);
object_path_variant = g_dbus_connection_call_sync (conn,
SERVICE_SESSION_MANAGER,
PATH_SESSION_MANAGER,
IFACE_SESSION_MANAGER,
"RegisterClient",
g_variant_new ("(ss)",
app_id,
client_id),
G_VARIANT_TYPE ("(o)"),
G_DBUS_CALL_FLAGS_NONE, 1000,
NULL, &error);
if (error != NULL) {
g_message ("couldn't register in session: %s", error->message);
g_error_free (error);
return;
}
/* Get out our client path */
if (!dbus_message_iter_init (reply, &args) ||
dbus_message_iter_get_arg_type (&args) != DBUS_TYPE_OBJECT_PATH) {
g_message ("invalid register response from session");
} else {
dbus_message_iter_get_basic (&args, &client_session_path);
client_session_path = g_strdup (client_session_path);
}
dbus_message_unref (reply);
g_variant_get (object_path_variant, "(o)", &client_session_path);
g_variant_unref (object_path_variant);
/*
* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
......@@ -201,19 +167,10 @@ gkd_dbus_session_init (DBusConnection *conn)
* Now we register for DBus signals on that client session path
* These are fired specifically for us.
*/
client_session_rule = g_strdup_printf("type='signal',"
"interface='org.gnome.SessionManager.ClientPrivate',"
"path='%s'",
client_session_path);
dbus_bus_add_match (conn, client_session_rule, &derr);
if(dbus_error_is_set(&derr)) {
g_message ("couldn't listen for signals in session: %s", derr.message);
dbus_error_free (&derr);
g_free (client_session_rule);
client_session_rule = NULL;
return;
}
dbus_connection_add_filter (conn, signal_filter, NULL, NULL);
g_dbus_connection_signal_subscribe (conn,
NULL,
"org.gnome.SessionManager.ClientPrivate", NULL,
client_session_path, NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
signal_filter, NULL, NULL);
}
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gkd-dbus.c - hook into dbus, call other bits
Copyright (C) 2007, 2009, Stefan Walter
The Gnome Keyring Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Keyring Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
<http://www.gnu.org/licenses/>.
Author: Stef Walter <stef@memberwebs.com>
*/
#include "config.h"
#include "gkd-dbus-util.h"
#include "gkd-secret-types.h"
#include "egg/egg-error.h"
#include <string.h>
GType
gkd_dbus_connection_get_boxed_type (void)
{
static GType type = 0;
if (!type)
type = g_boxed_type_register_static ("GkdDBusConnection",
(GBoxedCopyFunc)dbus_connection_ref,
(GBoxedFreeFunc)dbus_connection_unref);
return type;
}
gboolean
gkd_dbus_interface_match (const gchar *interface, const gchar *match)
{
g_return_val_if_fail (interface, FALSE);
/* Null or zero length matches anything */
if (!match || !match[0])
return TRUE;
return strcmp (interface, match) == 0;
}
static gchar *
build_child_node_xml (const gchar *parent,
const gchar **children)
{
GString *result;
const gchar *child;
guint i;
result = g_string_new ("");
for (i = 0; children != NULL && children[i] != NULL; i++) {
if (children[i][0] == '/') {
if (!g_str_has_prefix (children[i], parent)) {
g_warning ("in introspection data child '%s' is not descendant of parent '%s'",
children[i], parent);
continue;
}
child = children[i] + strlen (parent);
while (child[0] == '/')
child++;
} else {
child = children[i];
}
g_string_append_printf (result, "\t<node name=\"%s\"/>\n", child);
}
return g_string_free (result, FALSE);
}
static gboolean
string_replace (GString *string,
const gchar *search,
const gchar *replace)
{
const gchar *pos;
pos = strstr (string->str, search);
if (pos == NULL)
return FALSE;
g_string_erase (string, pos - string->str, strlen (search));
g_string_insert (string, pos - string->str, replace);
return TRUE;
}
DBusMessage *
gkd_dbus_introspect_handle (DBusMessage *message,
const gchar *data,
const gchar **children)
{
DBusMessage *reply;
GString *output = NULL;
gchar *nodes;
g_return_val_if_fail (message, NULL);
g_return_val_if_fail (data, NULL);
if (dbus_message_is_method_call (message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") &&
dbus_message_get_args (message, NULL, DBUS_TYPE_INVALID)) {
if (children != NULL) {
output = g_string_new (data);
nodes = build_child_node_xml (dbus_message_get_path (message), children);
if (!string_replace (output, "<!--@children@-->", nodes))
g_warning ("introspection data contained no location for child nodes");
g_free (nodes);