summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Bradford <rob@linux.intel.com>2011-06-20 12:05:56 +0100
committerRoss Burton <ross@linux.intel.com>2011-06-28 15:25:45 +0100
commiteed7b085e64f425bac72ec5f9f0634fdb35176d3 (patch)
tree06fa05cb8e7dd5945a15c879654b5bf88d1936d6
parentbd491608a31dec52e98491d7a4c9aae4386a227e (diff)
downloadgconf-eed7b085e64f425bac72ec5f9f0634fdb35176d3.tar.gz
build: Conditionally compile in the DBUS implementation files
-rw-r--r--gconf/Makefile.am16
-rw-r--r--gconf/gconf-database-dbus.c978
-rw-r--r--gconf/gconf-database-dbus.h37
-rw-r--r--gconf/gconf-dbus-utils.c1133
-rw-r--r--gconf/gconf-dbus-utils.h99
-rw-r--r--gconf/gconf-dbus.c2468
-rw-r--r--gconf/gconf-dbus.h0
-rw-r--r--gconf/gconfd-dbus.c392
-rw-r--r--gconf/gconfd-dbus.h42
9 files changed, 5165 insertions, 0 deletions
diff --git a/gconf/Makefile.am b/gconf/Makefile.am
index 75586974..821e768a 100644
--- a/gconf/Makefile.am
+++ b/gconf/Makefile.am
@@ -36,7 +36,9 @@ lib_LTLIBRARIES = libgconf-2.la
bin_PROGRAMS = gconftool-2
libexec_PROGRAMS = gconfd-2 $(SANITY_CHECK)
+if HAVE_CORBA
CORBA_SOURCECODE = GConfX-common.c GConfX-skels.c GConfX-stubs.c GConfX.h
+endif
BUILT_SOURCES = \
$(CORBA_SOURCECODE) \
@@ -76,6 +78,13 @@ gconfd_2_SOURCES = \
gconfd.h \
gconfd.c
+if HAVE_DBUS
+gconfd_2_SOURCES += \
+ gconfd-dbus.c \
+ gconfd-dbus.h \
+ gconf-database-dbus.h \
+ gconf-database-dbus.c
+endif
if OS_WIN32
gconfd_2_LDFLAGS = -mwindows
endif
@@ -121,6 +130,13 @@ libgconf_2_la_SOURCES = \
$(CORBA_SOURCECODE) \
$(WIN32_SOURCECODE)
+if HAVE_DBUS
+libgconf_2_la_SOURCES += \
+ gconf-dbus.c \
+ gconf-dbus-utils.c \
+ gconf-dbus-utils.h
+endif
+
libgconf_2_la_LDFLAGS = -version-info $(GCONF_CURRENT):$(GCONF_REVISION):$(GCONF_AGE) -no-undefined
libgconf_2_la_LIBADD = $(INTLLIBS) $(DEPENDENT_LIBS) $(DEPENDENT_DBUS_LIBS) $(DEPENDENT_ORBIT_LIBS)
diff --git a/gconf/gconf-database-dbus.c b/gconf/gconf-database-dbus.c
new file mode 100644
index 00000000..ce42382f
--- /dev/null
+++ b/gconf/gconf-database-dbus.c
@@ -0,0 +1,978 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* GConf
+ * Copyright (C) 2003, 2004 Imendio HB
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include "gconfd.h"
+#include "gconf-dbus-utils.h"
+#include "gconfd-dbus.h"
+#include "gconf-database-dbus.h"
+
+#define DATABASE_OBJECT_PATH "/org/gnome/GConf/Database"
+
+static gint object_nr = 0;
+
+typedef struct {
+ char *namespace_section;
+ GList *clients;
+} NotificationData;
+
+typedef struct {
+ gchar *service;
+ gint nr_of_notifications;
+} ListeningClientData;
+
+static void database_unregistered_func (DBusConnection *connection,
+ GConfDatabase *db);
+static DBusHandlerResult database_message_func (DBusConnection *connection,
+ DBusMessage *message,
+ GConfDatabase *db);
+static DBusHandlerResult database_filter_func (DBusConnection *connection,
+ DBusMessage *message,
+ GConfDatabase *db);
+static DBusHandlerResult database_handle_name_owner_changed (DBusConnection *connection,
+ DBusMessage *message,
+ GConfDatabase *db);
+
+static void database_handle_lookup (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_lookup_ext (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_lookup_default (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_set (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_unset (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_recursive_unset (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_dir_exists (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_get_all_entries (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_get_all_dirs (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_set_schema (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_suggest_sync (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static void database_handle_add_notify (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+static gboolean database_remove_notification_data (GConfDatabase *db,
+ NotificationData *notification,
+ const char *client);
+static void database_handle_remove_notify (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db);
+
+static ListeningClientData *database_add_listening_client (GConfDatabase *db,
+ const gchar *service);
+static void database_remove_listening_client (GConfDatabase *db,
+ ListeningClientData *client);
+
+
+static DBusObjectPathVTable database_vtable = {
+ (DBusObjectPathUnregisterFunction) database_unregistered_func,
+ (DBusObjectPathMessageFunction) database_message_func,
+ NULL,
+};
+
+static void
+database_unregistered_func (DBusConnection *connection, GConfDatabase *db)
+{
+}
+
+static DBusHandlerResult
+database_message_func (DBusConnection *connection,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ if (gconfd_dbus_check_in_shutdown (connection, message))
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_LOOKUP)) {
+ database_handle_lookup (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_LOOKUP_EXTENDED)) {
+ database_handle_lookup_ext (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_LOOKUP_DEFAULT)) {
+ database_handle_lookup_default (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_SET)) {
+ database_handle_set (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_UNSET)) {
+ database_handle_unset (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_RECURSIVE_UNSET)) {
+ database_handle_recursive_unset (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_DIR_EXISTS)) {
+ database_handle_dir_exists (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_GET_ALL_ENTRIES)) {
+ database_handle_get_all_entries (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_GET_ALL_DIRS)) {
+ database_handle_get_all_dirs (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_SET_SCHEMA)) {
+ database_handle_set_schema (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_SUGGEST_SYNC)) {
+ database_handle_suggest_sync (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_ADD_NOTIFY)) {
+ database_handle_add_notify (connection, message, db);
+ }
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_REMOVE_NOTIFY)) {
+ database_handle_remove_notify (connection, message, db);
+ } else {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+get_all_notifications_func (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GList **list = user_data;
+
+ *list = g_list_prepend (*list, value);
+}
+
+static DBusHandlerResult
+database_filter_func (DBusConnection *connection,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+#if 0
+ /* Debug output. */
+ if (dbus_message_get_member (message)) {
+ g_print ("Message: %s\n", dbus_message_get_member (message));
+ }
+#endif
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ return database_handle_name_owner_changed (connection, message, db);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+database_handle_name_owner_changed (DBusConnection *connection,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *service;
+ gchar *old_owner;
+ gchar *new_owner;
+ GList *notifications = NULL, *l;
+ NotificationData *notification;
+ ListeningClientData *client;
+
+ dbus_message_get_args (message,
+ NULL,
+ DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID);
+
+ if (strcmp (new_owner, "") != 0)
+ {
+ /* Service still exist, don't remove notifications */
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ g_hash_table_foreach (db->notifications, get_all_notifications_func,
+ &notifications);
+
+ /* Note: This might be a bit too slow to do like this. We could add a hash
+ * table that maps client base service names to notification data, instead of
+ * going through the entire list of notifications and clients.
+ */
+ for (l = notifications; l; l = l->next)
+ {
+ notification = l->data;
+
+ database_remove_notification_data (db, notification, service);
+ }
+
+ client = g_hash_table_lookup (db->listening_clients, service);
+ if (client)
+ database_remove_listening_client (db, client);
+
+ g_list_free (notifications);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+database_handle_lookup (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ GConfValue *value;
+ DBusMessage *reply;
+ gchar *key;
+ gchar *locale;
+ GConfLocaleList *locales;
+ gboolean use_schema_default;
+ GError *gerror = NULL;
+ DBusMessageIter iter;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_BOOLEAN, &use_schema_default,
+ DBUS_TYPE_INVALID))
+ return;
+
+ locales = gconfd_locale_cache_lookup (locale);
+
+ value = gconf_database_query_value (db, key, locales->list,
+ use_schema_default,
+ NULL, NULL, NULL, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ goto fail;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+ gconf_dbus_utils_append_value (&iter, value);
+
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+
+ fail:
+ if (value)
+ gconf_value_free (value);
+}
+
+static void
+database_handle_lookup_ext (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ GConfValue *value;
+ gchar *schema_name = NULL;
+ gboolean value_is_default;
+ gboolean value_is_writable;
+ DBusMessage *reply;
+ gchar *key;
+ gchar *locale;
+ GConfLocaleList *locales;
+ gboolean use_schema_default;
+ GError *gerror = NULL;
+ DBusMessageIter iter;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_BOOLEAN, &use_schema_default,
+ DBUS_TYPE_INVALID))
+ return;
+
+ locales = gconfd_locale_cache_lookup (locale);
+
+ value = gconf_database_query_value (db, key, locales->list,
+ use_schema_default,
+ &schema_name, &value_is_default,
+ &value_is_writable, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ goto fail;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (value)
+ gconf_dbus_utils_append_entry_values (&iter,
+ key,
+ value,
+ value_is_default,
+ value_is_writable,
+ schema_name);
+
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+
+ fail:
+ g_free (schema_name);
+
+ if (value)
+ gconf_value_free (value);
+}
+
+static void
+database_handle_lookup_default (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ GConfValue *value;
+ DBusMessage *reply;
+ gchar *key;
+ gchar *locale;
+ GConfLocaleList *locales;
+ GError *gerror = NULL;
+ DBusMessageIter iter;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_INVALID))
+ return;
+
+ locales = gconfd_locale_cache_lookup (locale);
+
+ value = gconf_database_query_default_value (db, key, locales->list,
+ NULL,
+ &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ goto fail;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (value)
+ gconf_dbus_utils_append_value (&iter, value);
+
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+
+ fail:
+ if (value)
+ gconf_value_free (value);
+}
+
+static void
+database_handle_set (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *key;
+ GConfValue *value = NULL;
+ GError *gerror = NULL;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ dbus_message_iter_init (message, &iter);
+ dbus_message_iter_get_basic (&iter, &key);
+
+ dbus_message_iter_next (&iter);
+ value = gconf_dbus_utils_get_value (&iter);
+
+ gconf_database_set (db, key, value, &gerror);
+ gconf_value_free (value);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static void
+database_handle_unset (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *key;
+ gchar *locale;
+ GError *gerror = NULL;
+ DBusMessage *reply;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_INVALID))
+ return;
+
+ if (locale[0] == '\0')
+ {
+ locale = NULL;
+ }
+
+ gconf_database_unset (db, key, locale, &gerror);
+
+ gconf_database_sync (db, NULL);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+
+}
+
+static void
+database_handle_recursive_unset (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *key;
+ gchar *locale;
+ GError *gerror = NULL;
+ guint32 unset_flags;
+ DBusMessage *reply;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_UINT32, &unset_flags,
+ DBUS_TYPE_INVALID))
+ return;
+
+ if (locale[0] == '\0')
+ {
+ locale = NULL;
+ }
+
+ gconf_database_recursive_unset (db, key, locale, unset_flags, &gerror);
+
+ gconf_database_sync (db, NULL);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static void
+database_handle_dir_exists (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gboolean exists;
+ gchar *dir;
+ GError *gerror = NULL;
+ DBusMessage *reply;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_INVALID))
+ return;
+
+ exists = gconf_database_dir_exists (db, dir, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_message_append_args (reply,
+ DBUS_TYPE_BOOLEAN, &exists,
+ DBUS_TYPE_INVALID);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static void
+database_handle_get_all_entries (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ GSList *entries, *l;
+ gchar *dir;
+ gchar *locale;
+ GError *gerror = NULL;
+ GConfLocaleList* locales;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_INVALID))
+ return;
+
+ locales = gconfd_locale_cache_lookup (locale);
+
+ entries = gconf_database_all_entries (db, dir,
+ locales->list, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ gconf_dbus_utils_append_entries (&iter, entries);
+
+ for (l = entries; l; l = l->next)
+ {
+ GConfEntry *entry = l->data;
+
+ gconf_entry_free (entry);
+ }
+
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+
+ g_slist_free (entries);
+}
+
+static void
+database_handle_get_all_dirs (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ GSList *dirs, *l;
+ gchar *dir;
+ GError *gerror = NULL;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_INVALID))
+ return;
+
+ dirs = gconf_database_all_dirs (db, dir, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter);
+
+ for (l = dirs; l; l = l->next)
+ {
+ gchar *str = (gchar *) l->data;
+
+ dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_STRING,
+ &str);
+
+ g_free (l->data);
+ }
+
+ dbus_message_iter_close_container (&iter, &array_iter);
+
+ g_slist_free (dirs);
+
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static void
+database_handle_set_schema (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *key;
+ gchar *schema_key;
+ GError *gerror = NULL;
+ DBusMessage *reply;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &schema_key,
+ DBUS_TYPE_INVALID))
+ return;
+
+ /* Empty schema key name means unset. */
+ if (schema_key[0] == '\0')
+ schema_key = NULL;
+
+ gconf_database_set_schema (db, key, schema_key, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static void
+database_handle_suggest_sync (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ GError *gerror = NULL;
+ DBusMessage *reply;
+
+ gconf_database_sync (db, &gerror);
+
+ if (gconfd_dbus_set_exception (conn, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static void
+database_handle_add_notify (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *namespace_section;
+ DBusMessage *reply;
+ const char *sender;
+ NotificationData *notification;
+ ListeningClientData *client;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &namespace_section,
+ DBUS_TYPE_INVALID))
+ return;
+
+ sender = dbus_message_get_sender (message);
+
+ client = g_hash_table_lookup (db->listening_clients, sender);
+ if (!client)
+ {
+ client = database_add_listening_client (db, sender);
+ }
+ else
+ {
+ client->nr_of_notifications++;
+ }
+
+ notification = g_hash_table_lookup (db->notifications, namespace_section);
+
+ if (notification == NULL)
+ {
+ notification = g_new0 (NotificationData, 1);
+ notification->namespace_section = g_strdup (namespace_section);
+
+ g_hash_table_insert (db->notifications,
+ notification->namespace_section, notification);
+ }
+
+ notification->clients = g_list_prepend (notification->clients,
+ g_strdup (sender));
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static gboolean
+database_remove_notification_data (GConfDatabase *db,
+ NotificationData *notification,
+ const char *client)
+{
+ GList *element;
+
+ element = g_list_find_custom (notification->clients, client,
+ (GCompareFunc)strcmp);
+ if (element == NULL)
+ return FALSE;
+
+ notification->clients = g_list_remove_link (notification->clients, element);
+ if (notification->clients == NULL)
+ {
+ g_hash_table_remove (db->notifications,
+ notification->namespace_section);
+
+ g_free (notification->namespace_section);
+ g_free (notification);
+ }
+
+ g_free (element->data);
+ g_list_free_1 (element);
+
+ return TRUE;
+}
+
+static void
+database_handle_remove_notify (DBusConnection *conn,
+ DBusMessage *message,
+ GConfDatabase *db)
+{
+ gchar *namespace_section;
+ DBusMessage *reply;
+ const char *sender;
+ NotificationData *notification;
+ ListeningClientData *client;
+
+ if (!gconfd_dbus_get_message_args (conn, message,
+ DBUS_TYPE_STRING, &namespace_section,
+ DBUS_TYPE_INVALID))
+ return;
+
+ sender = dbus_message_get_sender (message);
+
+ notification = g_hash_table_lookup (db->notifications, namespace_section);
+
+ client = g_hash_table_lookup (db->listening_clients, sender);
+ if (client) {
+ client->nr_of_notifications--;
+
+ if (client->nr_of_notifications == 0) {
+ database_remove_listening_client (db, client);
+ }
+ }
+
+ /* Notification can be NULL if the client and server get out of sync. */
+ if (notification == NULL || !database_remove_notification_data (db, notification, sender))
+ {
+ gconf_log (GCL_DEBUG, _("Notification on %s doesn't exist"),
+ namespace_section);
+ }
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (conn, reply, NULL);
+ dbus_message_unref (reply);
+}
+
+static gchar *
+get_rule_for_service (const gchar *service)
+{
+ gchar *rule;
+
+ rule = g_strdup_printf ("type='signal',member='NameOwnerChanged',arg0='%s'", service);
+
+ return rule;
+}
+
+static ListeningClientData *
+database_add_listening_client (GConfDatabase *db,
+ const gchar *service)
+{
+ ListeningClientData *client;
+ gchar *rule;
+
+ client = g_new0 (ListeningClientData, 1);
+ client->service = g_strdup (service);
+ client->nr_of_notifications = 1;
+
+ g_hash_table_insert (db->listening_clients, client->service, client);
+
+ rule = get_rule_for_service (service);
+ dbus_bus_add_match (gconfd_dbus_get_connection (), rule, NULL);
+ g_free (rule);
+
+ return client;
+}
+
+static void
+database_remove_listening_client (GConfDatabase *db,
+ ListeningClientData *client)
+{
+ gchar *rule;
+
+ rule = get_rule_for_service (client->service);
+ dbus_bus_remove_match (gconfd_dbus_get_connection (), rule, NULL);
+ g_free (rule);
+
+ g_hash_table_remove (db->listening_clients, client->service);
+ g_free (client->service);
+ g_free (client);
+}
+
+void
+gconf_database_dbus_setup (GConfDatabase *db)
+{
+ DBusConnection *conn;
+
+ g_assert (db->object_path == NULL);
+
+ db->object_path = g_strdup_printf ("%s/%d",
+ DATABASE_OBJECT_PATH,
+ object_nr++);
+
+ conn = gconfd_dbus_get_connection ();
+
+ dbus_connection_register_object_path (conn,
+ db->object_path,
+ &database_vtable,
+ db);
+
+ db->notifications = g_hash_table_new (g_str_hash, g_str_equal);
+ db->listening_clients = g_hash_table_new (g_str_hash, g_str_equal);
+
+ dbus_connection_add_filter (conn,
+ (DBusHandleMessageFunction)database_filter_func,
+ db,
+ NULL);
+}
+
+void
+gconf_database_dbus_teardown (GConfDatabase *db)
+{
+ DBusConnection *conn;
+
+ conn = gconfd_dbus_get_connection ();
+
+ dbus_connection_unregister_object_path (conn, db->object_path);
+
+ dbus_connection_remove_filter (conn,
+ (DBusHandleMessageFunction)database_filter_func,
+ db);
+ g_free (db->object_path);
+ db->object_path = NULL;
+}
+
+const char *
+gconf_database_dbus_get_path (GConfDatabase *db)
+{
+ return db->object_path;
+}
+
+void
+gconf_database_dbus_notify_listeners (GConfDatabase *db,
+ GConfSources *modified_sources,
+ const gchar *key,
+ const GConfValue *value,
+ gboolean is_default,
+ gboolean is_writable,
+ gboolean notify_others)
+{
+ char *dir, *sep;
+ GList *l;
+ NotificationData *notification;
+ DBusMessage *message;
+ gboolean last;
+
+ dir = g_strdup (key);
+
+ /* Lookup the key in the namespace hierarchy, start with the full key and then
+ * remove the leaf, lookup again, remove the leaf, and so on until a match is
+ * found. Notify the clients (identified by their base service) that
+ * correspond to the found namespace.
+ */
+ last = FALSE;
+ while (1)
+ {
+ notification = g_hash_table_lookup (db->notifications, dir);
+
+ if (notification)
+ {
+ for (l = notification->clients; l; l = l->next)
+ {
+ const char *base_service = l->data;
+ DBusMessageIter iter;
+
+ message = dbus_message_new_method_call (base_service,
+ GCONF_DBUS_CLIENT_OBJECT,
+ GCONF_DBUS_CLIENT_INTERFACE,
+ "Notify");
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &db->object_path,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_iter_init_append (message, &iter);
+
+ gconf_dbus_utils_append_entry_values (&iter,
+ key,
+ value,
+ is_default,
+ is_writable,
+ NULL);
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ dbus_connection_send (gconfd_dbus_get_connection (), message, NULL);
+ dbus_message_unref (message);
+ }
+ }
+
+ if (last)
+ break;
+
+ sep = strrchr (dir, '/');
+
+ /* Special case to catch notifications on the root. */
+ if (sep == dir)
+ {
+ last = TRUE;
+ sep[1] = '\0';
+ }
+ else
+ *sep = '\0';
+ }
+
+
+ g_free (dir);
+
+ if (modified_sources)
+ {
+ if (notify_others)
+ gconfd_notify_other_listeners (db, modified_sources, key);
+
+ g_list_free (modified_sources->sources);
+ g_free (modified_sources);
+ }
+}
diff --git a/gconf/gconf-database-dbus.h b/gconf/gconf-database-dbus.h
new file mode 100644
index 00000000..8794b1fa
--- /dev/null
+++ b/gconf/gconf-database-dbus.h
@@ -0,0 +1,37 @@
+/* GConf
+ * Copyright (C) 2003 Imendio HB
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GCONF_GCONF_DATABASE_DBUS_H
+#define GCONF_GCONF_DATABASE_DBUS_H
+
+#include <dbus/dbus.h>
+#include "gconf-database.h"
+
+void gconf_database_dbus_setup (GConfDatabase *db);
+void gconf_database_dbus_teardown (GConfDatabase *db);
+const gchar *gconf_database_dbus_get_path (GConfDatabase *db);
+void gconf_database_dbus_notify_listeners (GConfDatabase *db,
+ GConfSources *modified_sources,
+ const gchar *key,
+ const GConfValue *value,
+ gboolean is_default,
+ gboolean is_writable,
+ gboolean notify_others);
+
+#endif
diff --git a/gconf/gconf-dbus-utils.c b/gconf/gconf-dbus-utils.c
new file mode 100644
index 00000000..6fd5bfa2
--- /dev/null
+++ b/gconf/gconf-dbus-utils.c
@@ -0,0 +1,1133 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+
+#include <config.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "gconf/gconf.h"
+#include "gconf-internals.h"
+#include "gconf-dbus-utils.h"
+
+#define d(x)
+
+/* Entry:
+ *
+ * struct {
+ * string key;
+ * struct value;
+ *
+ * boolean schema_name_set;
+ * string schema_name;
+ *
+ * boolean is_default;
+ * boolean is_writable;
+ * };
+ *
+ */
+
+/* Pair:
+ *
+ * struct {
+ * int32 car_type;
+ * <fundamental> car_value;
+
+ * int32 cdr_type;
+ * <fundamental> cdr_value;
+ * };
+ *
+ */
+
+/* List:
+ *
+ * struct {
+ * int32 list_type;
+ * array<fundamental> values;
+ * };
+ *
+ */
+
+/* Value:
+ *
+ * struct {
+ * int32 type;
+ * <int, string, float, bool, list, pair, schema> value;
+ * };
+ *
+ */
+
+/* Schema:
+ *
+ * struct {
+ * int32 type;
+ * int32 list_type;
+ * int32 car_type;
+ * int32 cdr_type;
+
+ * boolean locale_set;
+ * string locale;
+
+ * boolean short_desc_set;
+ * string short_desc;
+
+ * boolean long_desc_set;
+ * string long_desc;
+
+ * boolean owner_set;
+ * string owner;
+
+ * boolean gettext_domain_set;
+ * string gettext_domain;
+
+ * string default_value;
+ * };
+ *
+ */
+
+static void utils_append_value_helper_fundamental (DBusMessageIter *iter,
+ const GConfValue *value);
+static void utils_append_value_helper_pair (DBusMessageIter *main_iter,
+ const GConfValue *value);
+static void utils_append_value_helper_list (DBusMessageIter *main_iter,
+ const GConfValue *value);
+static void utils_append_schema (DBusMessageIter *main_iter,
+ const GConfSchema *schema);
+static void utils_append_value (DBusMessageIter *main_iter,
+ const GConfValue *value);
+
+static GConfValue * utils_get_value_helper_fundamental (DBusMessageIter *iter,
+ GConfValueType value_type);
+static GConfValue * utils_get_value_helper_pair (DBusMessageIter *iter);
+static GConfValue * utils_get_value_helper_list (DBusMessageIter *iter);
+static GConfValue * utils_get_value (DBusMessageIter *main_iter);
+static GConfSchema *utils_get_schema (DBusMessageIter *main_iter);
+static GConfValue * utils_get_schema_value (DBusMessageIter *iter);
+
+
+/*
+ * Utilities
+ */
+
+/* A boolean followed by a string. */
+static void
+utils_append_optional_string (DBusMessageIter *iter,
+ const gchar *str)
+{
+ gboolean is_set;
+
+ if (str)
+ is_set = TRUE;
+ else
+ {
+ is_set = FALSE;
+ str = "";
+ }
+
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &is_set);
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &str);
+}
+
+/* A boolean followed by a string. */
+static const gchar *
+utils_get_optional_string (DBusMessageIter *iter)
+{
+ gboolean is_set;
+ const gchar *str;
+
+ dbus_message_iter_get_basic (iter, &is_set);
+ dbus_message_iter_next (iter);
+ dbus_message_iter_get_basic (iter, &str);
+
+ if (is_set)
+ return str;
+ else
+ return NULL;
+}
+
+
+/*
+ * Setters
+ */
+
+
+/* Helper for utils_append_value, writes a int/string/float/bool/schema.
+ */
+static void
+utils_append_value_helper_fundamental (DBusMessageIter *iter,
+ const GConfValue *value)
+{
+ gint32 i;
+ gboolean b;
+ const gchar *s;
+ gdouble d;
+
+ d(g_print ("Append value (fundamental)\n"));
+
+ switch (value->type)
+ {
+ case GCONF_VALUE_INT:
+ i = gconf_value_get_int (value);
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &i);
+ break;
+
+ case GCONF_VALUE_STRING:
+ s = gconf_value_get_string (value);
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &s);
+ break;
+
+ case GCONF_VALUE_FLOAT:
+ d = gconf_value_get_float (value);
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &d);
+ break;
+
+ case GCONF_VALUE_BOOL:
+ b = gconf_value_get_bool (value);
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &b);
+ break;
+
+ case GCONF_VALUE_SCHEMA:
+ utils_append_schema (iter, gconf_value_get_schema (value));
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/* Helper for utils_append_value, writes a pair. The pair is a struct with the
+ * two values and two fundamental values (type, value, type value).
+ */
+static void
+utils_append_value_helper_pair (DBusMessageIter *main_iter,
+ const GConfValue *value)
+{
+ DBusMessageIter struct_iter;
+ GConfValue *car, *cdr;
+ gint32 type;
+
+ d(g_print ("Append value (pair)\n"));
+
+ g_assert (value->type == GCONF_VALUE_PAIR);
+
+ dbus_message_iter_open_container (main_iter,
+ DBUS_TYPE_STRUCT,
+ NULL, /* for struct */
+ &struct_iter);
+
+ car = gconf_value_get_car (value);
+ cdr = gconf_value_get_cdr (value);
+
+ /* The pair types. */
+ if (car)
+ type = car->type;
+ else
+ type = GCONF_VALUE_INVALID;
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &type);
+
+ if (cdr)
+ type = cdr->type;
+ else
+ type = GCONF_VALUE_INVALID;
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &type);
+
+ /* The values. */
+ if (car)
+ utils_append_value_helper_fundamental (&struct_iter, car);
+
+ if (cdr)
+ utils_append_value_helper_fundamental (&struct_iter, cdr);
+
+ dbus_message_iter_close_container (main_iter, &struct_iter);
+}
+
+/* Helper for utils_append_value, writes a list. The "list" is a struct with the
+ * list type and an array with the values directly in it.
+ */
+static void
+utils_append_value_helper_list (DBusMessageIter *main_iter,
+ const GConfValue *value)
+{
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ GConfValueType list_type;
+ const gchar *array_type;
+ GSList *list;
+
+ d(g_print ("Append value (list)\n"));
+
+ g_assert (value->type == GCONF_VALUE_LIST);
+
+ dbus_message_iter_open_container (main_iter,
+ DBUS_TYPE_STRUCT,
+ NULL, /* for struct */
+ &struct_iter);
+
+ /* Write the list type. */
+ list_type = gconf_value_get_list_type (value);
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &list_type);
+
+ /* And the value. */
+ switch (list_type)
+ {
+ case GCONF_VALUE_INT:
+ array_type = DBUS_TYPE_INT32_AS_STRING;
+ break;
+
+ case GCONF_VALUE_STRING:
+ array_type = DBUS_TYPE_STRING_AS_STRING;
+ break;
+
+ case GCONF_VALUE_FLOAT:
+ array_type = DBUS_TYPE_DOUBLE_AS_STRING;
+ break;
+
+ case GCONF_VALUE_BOOL:
+ array_type = DBUS_TYPE_BOOLEAN_AS_STRING;
+ break;
+
+ case GCONF_VALUE_SCHEMA:
+ array_type = DBUS_TYPE_STRUCT_AS_STRING;
+ break;
+
+ default:
+ array_type = NULL;
+ g_assert_not_reached ();
+ }
+
+ dbus_message_iter_open_container (&struct_iter,
+ DBUS_TYPE_ARRAY,
+ array_type,
+ &array_iter);
+
+ list = gconf_value_get_list (value);
+
+ switch (list_type)
+ {
+ case GCONF_VALUE_STRING:
+ while (list)
+ {
+ const gchar *s;
+
+ s = gconf_value_get_string (list->data);
+ dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_STRING,
+ &s);
+
+ list = list->next;
+ }
+ break;
+
+ case GCONF_VALUE_INT:
+ while (list)
+ {
+ gint32 i;
+
+ i = gconf_value_get_int (list->data);
+ dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_INT32,
+ &i);
+
+ list = list->next;
+ }
+ break;
+
+ case GCONF_VALUE_FLOAT:
+ while (list)
+ {
+ gdouble d;
+
+ d = gconf_value_get_float (list->data);
+ dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_DOUBLE,
+ &d);
+
+ list = list->next;
+ }
+ break;
+
+ case GCONF_VALUE_BOOL:
+ while (list)
+ {
+ gboolean b;
+
+ b = gconf_value_get_bool (list->data);
+ dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_BOOLEAN,
+ &b);
+
+ list = list->next;
+ }
+ break;
+
+ case GCONF_VALUE_SCHEMA:
+ while (list)
+ {
+ GConfSchema *schema;
+
+ schema = gconf_value_get_schema (list->data);
+ utils_append_schema (&array_iter, schema);
+
+ list = list->next;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ dbus_message_iter_close_container (&struct_iter, &array_iter);
+ dbus_message_iter_close_container (main_iter, &struct_iter);
+}
+
+/* Writes a schema, which is a struct. */
+static void
+utils_append_schema (DBusMessageIter *main_iter,
+ const GConfSchema *schema)
+{
+ DBusMessageIter struct_iter;
+ gint32 i;
+ const gchar *s;
+ GConfValue *default_value;
+
+ dbus_message_iter_open_container (main_iter,
+ DBUS_TYPE_STRUCT,
+ NULL, /* for structs */
+ &struct_iter);
+
+ i = gconf_schema_get_type (schema);
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &i);
+
+ i = gconf_schema_get_list_type (schema);
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &i);
+
+ i = gconf_schema_get_car_type (schema);
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &i);
+
+ i = gconf_schema_get_cdr_type (schema);
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &i);
+
+ s = gconf_schema_get_locale (schema);
+ utils_append_optional_string (&struct_iter, s);
+
+ s = gconf_schema_get_short_desc (schema);
+ utils_append_optional_string (&struct_iter, s);
+
+ s = gconf_schema_get_long_desc (schema);
+ utils_append_optional_string (&struct_iter, s);
+
+ s = gconf_schema_get_owner (schema);
+ utils_append_optional_string (&struct_iter, s);
+
+ default_value = gconf_schema_get_default_value (schema);
+
+ /* We don't need to do this, but it's much simpler */
+ if (default_value)
+ {
+ gchar *encoded;
+
+ encoded = gconf_value_encode (default_value);
+ g_assert (encoded != NULL);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &encoded);
+ g_free (encoded);
+ }
+ else
+ {
+ s = "";
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &s);
+ }
+
+ if (!dbus_message_iter_close_container (main_iter, &struct_iter))
+ g_error ("Out of memory");
+}
+
+static void
+utils_append_value (DBusMessageIter *main_iter,
+ const GConfValue *value)
+{
+ DBusMessageIter struct_iter;
+ gint32 type;
+
+ /* A value is stored as a struct with the type and the actual value. */
+ dbus_message_iter_open_container (main_iter,
+ DBUS_TYPE_STRUCT,
+ NULL, /* for structs */
+ &struct_iter);
+
+ if (!value)
+ type = GCONF_VALUE_INVALID;
+ else
+ type = value->type;
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &type);
+
+ switch (type)
+ {
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_STRING:
+ case GCONF_VALUE_FLOAT:
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_SCHEMA:
+ utils_append_value_helper_fundamental (&struct_iter, value);
+ break;
+
+ case GCONF_VALUE_LIST:
+ utils_append_value_helper_list (&struct_iter, value);
+ break;
+
+ case GCONF_VALUE_PAIR:
+ utils_append_value_helper_pair (&struct_iter, value);
+ break;
+
+ case GCONF_VALUE_INVALID:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ dbus_message_iter_close_container (main_iter, &struct_iter);
+}
+
+/* Writes an entry, which is a struct. */
+static void
+utils_append_entry_values (DBusMessageIter *main_iter,
+ const gchar *key,
+ const GConfValue *value,
+ gboolean is_default,
+ gboolean is_writable,
+ const gchar *schema_name)
+{
+ DBusMessageIter struct_iter;
+
+ d(g_print ("Appending entry %s\n", key));
+
+ dbus_message_iter_open_container (main_iter,
+ DBUS_TYPE_STRUCT,
+ NULL, /* for structs */
+ &struct_iter);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &key);
+
+ utils_append_value (&struct_iter, value);
+
+ utils_append_optional_string (&struct_iter, schema_name);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BOOLEAN, &is_default);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BOOLEAN, &is_writable);
+
+ dbus_message_iter_close_container (main_iter, &struct_iter);
+}
+
+/* Writes an entry, which is a struct. */
+static void
+utils_append_entry_values_stringified (DBusMessageIter *main_iter,
+ const gchar *key,
+ const GConfValue *value,
+ gboolean is_default,
+ gboolean is_writable,
+ const gchar *schema_name)
+{
+ DBusMessageIter struct_iter;
+ gchar *value_str;
+
+ d(g_print ("Appending entry %s\n", key));
+
+ dbus_message_iter_open_container (main_iter,
+ DBUS_TYPE_STRUCT,
+ NULL, /* for structs */
+ &struct_iter);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &key);
+
+ value_str = NULL;
+ if (value)
+ value_str = gconf_value_encode ((GConfValue *) value);
+
+ if (!value_str)
+ value_str = g_strdup ("");
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &value_str);
+ g_free (value_str);
+
+ utils_append_optional_string (&struct_iter, schema_name);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BOOLEAN, &is_default);
+
+ dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BOOLEAN, &is_writable);
+
+ if (!dbus_message_iter_close_container (main_iter, &struct_iter))
+ g_error ("Out of memory");
+}
+
+gboolean
+gconf_dbus_utils_get_entry_values (DBusMessageIter *main_iter,
+ gchar **key_p,
+ GConfValue **value_p,
+ gboolean *is_default_p,
+ gboolean *is_writable_p,
+ gchar **schema_name_p)
+{
+ DBusMessageIter struct_iter;
+ gchar *key;
+ GConfValue *value;
+ gboolean is_default;
+ gboolean is_writable;
+ gchar *schema_name;
+
+ g_return_val_if_fail (dbus_message_iter_get_arg_type (main_iter) == DBUS_TYPE_STRUCT,
+ FALSE);
+
+ dbus_message_iter_recurse (main_iter, &struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &key);
+
+ d(g_print ("Getting entry %s\n", key));
+
+ dbus_message_iter_next (&struct_iter);
+ value = utils_get_value (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ schema_name = (gchar *) utils_get_optional_string (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &is_default);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &is_writable);
+
+ if (key_p)
+ *key_p = key;
+
+ if (value_p)
+ *value_p = value;
+ else if (value)
+ gconf_value_free (value);
+
+ if (schema_name_p)
+ *schema_name_p = schema_name;
+
+ if (is_default_p)
+ *is_default_p = is_default;
+
+ if (is_writable_p)
+ *is_writable_p = is_writable;
+
+ return TRUE;
+}
+
+static gboolean
+utils_get_entry_values_stringified (DBusMessageIter *main_iter,
+ gchar **key_p,
+ GConfValue **value_p,
+ gboolean *is_default_p,
+ gboolean *is_writable_p,
+ gchar **schema_name_p)
+{
+ DBusMessageIter struct_iter;
+ gchar *key;
+ gchar *value_str;
+ GConfValue *value;
+ gboolean is_default;
+ gboolean is_writable;
+ gchar *schema_name;
+
+ dbus_message_iter_recurse (main_iter, &struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &key);
+
+ d(g_print ("Getting entry %s\n", key));
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &value_str);
+ if (value_str[0] != '\0')
+ value = gconf_value_decode (value_str);
+ else
+ value = NULL;
+
+ dbus_message_iter_next (&struct_iter);
+ schema_name = (gchar *) utils_get_optional_string (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &is_default);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &is_writable);
+
+ if (key_p)
+ *key_p = key;
+
+ if (value_p)
+ *value_p = value;
+ else
+ gconf_value_free (value);
+
+ if (schema_name_p)
+ *schema_name_p = schema_name;
+
+ if (is_default_p)
+ *is_default_p = is_default;
+
+ if (is_writable_p)
+ *is_writable_p = is_writable;
+
+ return TRUE;
+}
+
+
+/*
+ * Getters
+ */
+
+/* Helper for utils_get_value, reads int/string/float/bool/schema. */
+static GConfValue *
+utils_get_value_helper_fundamental (DBusMessageIter *iter,
+ GConfValueType value_type)
+{
+ GConfValue *value;
+ GConfSchema *schema;
+ gint32 i;
+ const gchar *s;
+ gdouble d;
+ gboolean b;
+
+ d(g_print ("Get value (fundamental)\n"));
+
+ if (value_type == GCONF_VALUE_INVALID)
+ return NULL;
+
+ value = gconf_value_new (value_type);
+
+ switch (value_type)
+ {
+ case GCONF_VALUE_INT:
+ dbus_message_iter_get_basic (iter, &i);
+ gconf_value_set_int (value, i);
+ break;
+
+ case GCONF_VALUE_STRING:
+ dbus_message_iter_get_basic (iter, &s);
+ gconf_value_set_string (value, s);
+ break;
+
+ case GCONF_VALUE_FLOAT:
+ dbus_message_iter_get_basic (iter, &d);
+ gconf_value_set_float (value, d);
+ break;
+
+ case GCONF_VALUE_BOOL:
+ dbus_message_iter_get_basic (iter, &b);
+ gconf_value_set_bool (value, b);
+ break;
+
+ case GCONF_VALUE_SCHEMA:
+ schema = utils_get_schema (iter);
+ gconf_value_set_schema_nocopy (value, schema);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return value;
+}
+
+/* Helper for utils_get_value, reads a pair. The pair is a struct with the two
+ * values and two fundamental values (type, value, type, value).
+ */
+static GConfValue *
+utils_get_value_helper_pair (DBusMessageIter *iter)
+{
+ GConfValue *value;
+ DBusMessageIter struct_iter;
+ gint32 car_type, cdr_type;
+ GConfValue *car_value = NULL, *cdr_value = NULL;
+
+ d(g_print ("Get value (pair)\n"));
+
+ value = gconf_value_new (GCONF_VALUE_PAIR);
+
+ /* Get the pair types. */
+ dbus_message_iter_recurse (iter, &struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &car_type);
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &cdr_type);
+
+ /* Get the values. */
+ dbus_message_iter_next (&struct_iter);
+ if (car_type == GCONF_VALUE_SCHEMA)
+ car_value = utils_get_schema_value (&struct_iter);
+ else if (car_type != GCONF_VALUE_INVALID)
+ car_value = utils_get_value_helper_fundamental (&struct_iter, car_type);
+
+ dbus_message_iter_next (&struct_iter);
+ if (cdr_type == GCONF_VALUE_SCHEMA)
+ cdr_value = utils_get_schema_value (&struct_iter);
+ else if (cdr_type != GCONF_VALUE_INVALID)
+ cdr_value = utils_get_value_helper_fundamental (&struct_iter, cdr_type);
+
+ if (car_value)
+ gconf_value_set_car_nocopy (value, car_value);
+
+ if (cdr_value)
+ gconf_value_set_cdr_nocopy (value, cdr_value);
+
+ return value;
+}
+
+/* Helper for utils_get_value, reads a list. The "list" is a struct with the
+ * list type and an array with the values directly in it.
+ */
+static GConfValue *
+utils_get_value_helper_list (DBusMessageIter *iter)
+{
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ GConfValue *value;
+ gint32 list_type;
+ GSList *list;
+ GConfValue *child_value;
+
+ d(g_print ("Get value (list)\n"));
+
+ value = gconf_value_new (GCONF_VALUE_LIST);
+
+ dbus_message_iter_recurse (iter, &struct_iter);
+
+ /* Get the list type. */
+ dbus_message_iter_get_basic (&struct_iter, &list_type);
+ gconf_value_set_list_type (value, list_type);
+
+ /* Get the array. */
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_recurse (&struct_iter, &array_iter);
+
+ /* And the values from the array. */
+ list = NULL;
+ switch (list_type)
+ {
+ case GCONF_VALUE_STRING:
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRING)
+ {
+ const gchar *str;
+
+ dbus_message_iter_get_basic (&array_iter, &str);
+
+ child_value = gconf_value_new (GCONF_VALUE_STRING);
+ gconf_value_set_string (child_value, str);
+ list = g_slist_prepend (list, child_value);
+
+ dbus_message_iter_next (&array_iter);
+ }
+ break;
+
+ case GCONF_VALUE_INT:
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_INT32)
+ {
+ gint32 i;
+
+ dbus_message_iter_get_basic (&array_iter, &i);
+
+ child_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (child_value, i);
+ list = g_slist_prepend (list, child_value);
+
+ dbus_message_iter_next (&array_iter);
+ }
+ break;
+
+ case GCONF_VALUE_FLOAT:
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_DOUBLE)
+ {
+ gdouble d;
+
+ dbus_message_iter_get_basic (&array_iter, &d);
+
+ child_value = gconf_value_new (GCONF_VALUE_FLOAT);
+ gconf_value_set_float (child_value, d);
+ list = g_slist_prepend (list, child_value);
+
+ dbus_message_iter_next (&array_iter);
+ }
+ break;
+
+ case GCONF_VALUE_BOOL:
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_BOOLEAN)
+ {
+ gboolean b;
+
+ dbus_message_iter_get_basic (&array_iter, &b);
+
+ child_value = gconf_value_new (GCONF_VALUE_BOOL);
+ gconf_value_set_bool (child_value, b);
+ list = g_slist_prepend (list, child_value);
+
+ dbus_message_iter_next (&array_iter);
+ }
+ break;
+
+ case GCONF_VALUE_SCHEMA:
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRUCT)
+ {
+ child_value = utils_get_schema_value (&array_iter);
+ list = g_slist_prepend (list, child_value);
+
+ dbus_message_iter_next (&array_iter);
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ list = g_slist_reverse (list);
+ gconf_value_set_list_nocopy (value, list);
+
+ return value;
+}
+
+static GConfValue *
+utils_get_value (DBusMessageIter *main_iter)
+{
+ DBusMessageIter struct_iter;
+ gint32 type;
+ GConfValue *value;
+
+ g_assert (dbus_message_iter_get_arg_type (main_iter) == DBUS_TYPE_STRUCT);
+
+ /* A value is stored as a struct with the type and a variant with the actual
+ * value.
+ */
+
+ dbus_message_iter_recurse (main_iter, &struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &type);
+
+ dbus_message_iter_next (&struct_iter);
+
+ switch (type)
+ {
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_STRING:
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_FLOAT:
+ value = utils_get_value_helper_fundamental (&struct_iter, type);
+ break;
+
+ case GCONF_VALUE_PAIR:
+ value = utils_get_value_helper_pair (&struct_iter);
+ break;
+
+ case GCONF_VALUE_LIST:
+ value = utils_get_value_helper_list (&struct_iter);
+ break;
+
+ case GCONF_VALUE_SCHEMA:
+ value = utils_get_schema_value (&struct_iter);
+ break;
+
+ case GCONF_VALUE_INVALID:
+ value = NULL;
+ break;
+
+ default:
+ value = NULL;
+ g_assert_not_reached ();
+ }
+
+ return value;
+}
+
+static GConfSchema *
+utils_get_schema (DBusMessageIter *main_iter)
+{
+ DBusMessageIter struct_iter;
+ gint32 type, list_type, car_type, cdr_type;
+ const gchar *locale, *short_desc, *long_desc, *owner;
+ const gchar *encoded;
+ GConfSchema *schema;
+ GConfValue *default_value;
+
+ g_assert (dbus_message_iter_get_arg_type (main_iter) == DBUS_TYPE_STRUCT);
+
+ dbus_message_iter_recurse (main_iter, &struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &type);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &list_type);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &car_type);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &cdr_type);
+
+ dbus_message_iter_next (&struct_iter);
+ locale = utils_get_optional_string (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ short_desc = utils_get_optional_string (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ long_desc = utils_get_optional_string (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ owner = utils_get_optional_string (&struct_iter);
+
+ dbus_message_iter_next (&struct_iter);
+ dbus_message_iter_get_basic (&struct_iter, &encoded);
+
+ schema = gconf_schema_new ();
+
+ gconf_schema_set_type (schema, type);
+ gconf_schema_set_list_type (schema, list_type);
+ gconf_schema_set_car_type (schema, car_type);
+ gconf_schema_set_cdr_type (schema, cdr_type);
+
+ if (locale)
+ gconf_schema_set_locale (schema, locale);
+
+ if (short_desc)
+ gconf_schema_set_short_desc (schema, short_desc);
+
+ if (long_desc)
+ gconf_schema_set_long_desc (schema, long_desc);
+
+ if (owner)
+ gconf_schema_set_owner (schema, owner);
+
+ if (*encoded != '\0')
+ {
+ default_value = gconf_value_decode (encoded);
+ if (default_value)
+ gconf_schema_set_default_value_nocopy (schema, default_value);
+ }
+
+ return schema;
+}
+
+static GConfValue *
+utils_get_schema_value (DBusMessageIter *iter)
+{
+ GConfSchema *schema;
+ GConfValue *value;
+
+ schema = utils_get_schema (iter);
+
+ value = gconf_value_new (GCONF_VALUE_SCHEMA);
+ gconf_value_set_schema_nocopy (value, schema);
+
+ return value;
+}
+
+
+/*
+ * Public API
+ */
+
+void
+gconf_dbus_utils_append_value (DBusMessageIter *iter,
+ const GConfValue *value)
+{
+ utils_append_value (iter, value);
+}
+
+GConfValue *
+gconf_dbus_utils_get_value (DBusMessageIter *iter)
+{
+ return utils_get_value (iter);
+}
+
+void
+gconf_dbus_utils_append_entry_values (DBusMessageIter *iter,
+ const gchar *key,
+ const GConfValue *value,
+ gboolean is_default,
+ gboolean is_writable,
+ const gchar *schema_name)
+{
+ utils_append_entry_values (iter,
+ key,
+ value,
+ is_default,
+ is_writable,
+ schema_name);
+}
+
+/* Append the list of entries as an array. */
+void
+gconf_dbus_utils_append_entries (DBusMessageIter *iter,
+ GSList *entries)
+{
+ DBusMessageIter array_iter;
+ GSList *l;
+
+ dbus_message_iter_open_container (iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_BOOLEAN_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_BOOLEAN_AS_STRING
+ DBUS_TYPE_BOOLEAN_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &array_iter);
+
+ for (l = entries; l; l = l->next)
+ {
+ GConfEntry *entry = l->data;
+
+ utils_append_entry_values_stringified (&array_iter,
+ entry->key,
+ gconf_entry_get_value (entry),
+ gconf_entry_get_is_default (entry),
+ gconf_entry_get_is_writable (entry),
+ gconf_entry_get_schema_name (entry));
+ }
+
+ dbus_message_iter_close_container (iter, &array_iter);
+}
+
+/* Get a list of entries from an array. */
+GSList *
+gconf_dbus_utils_get_entries (DBusMessageIter *iter, const gchar *dir)
+{
+ GSList *entries;
+ DBusMessageIter array_iter;
+
+ entries = NULL;
+
+ dbus_message_iter_recurse (iter, &array_iter);
+
+ /* Loop through while there are structs (entries). */
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRUCT)
+ {
+ gchar *key;
+ GConfValue *value;
+ gboolean is_default;
+ gboolean is_writable;
+ gchar *schema_name;
+ GConfEntry *entry;
+
+ if (!utils_get_entry_values_stringified (&array_iter,
+ &key,
+ &value,
+ &is_default,
+ &is_writable,
+ &schema_name))
+ break;
+
+ entry = gconf_entry_new_nocopy (gconf_concat_dir_and_key (dir, key), value);
+
+ gconf_entry_set_is_default (entry, is_default);
+ gconf_entry_set_is_writable (entry, is_writable);
+
+ if (schema_name)
+ gconf_entry_set_schema_name (entry, schema_name);
+
+ entries = g_slist_prepend (entries, entry);
+
+ dbus_message_iter_next (&array_iter);
+ }
+
+ return entries;
+}
+
diff --git a/gconf/gconf-dbus-utils.h b/gconf/gconf-dbus-utils.h
new file mode 100644
index 00000000..deeab870
--- /dev/null
+++ b/gconf/gconf-dbus-utils.h
@@ -0,0 +1,99 @@
+/* GConf
+ * Copyright (C) 2003 Imendio HB
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GCONF_DBUS_UTILS_H
+#define GCONF_DBUS_UTILS_H
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gconf/gconf.h>
+#include <gconf/gconf-value.h>
+
+#define GCONF_DBUS_SERVICE "org.gnome.GConf"
+
+#define GCONF_DBUS_SERVER_INTERFACE "org.gnome.GConf.Server"
+#define GCONF_DBUS_DATABASE_INTERFACE "org.gnome.GConf.Database"
+
+#define GCONF_DBUS_SERVER_OBJECT "/org/gnome/GConf/Server"
+
+#define GCONF_DBUS_SERVER_GET_DEFAULT_DB "GetDefaultDatabase"
+#define GCONF_DBUS_SERVER_GET_DB "GetDatabase"
+#define GCONF_DBUS_SERVER_SHUTDOWN "Shutdown"
+
+#define GCONF_DBUS_DATABASE_LOOKUP "Lookup"
+#define GCONF_DBUS_DATABASE_LOOKUP_EXTENDED "LookupExtended"
+#define GCONF_DBUS_DATABASE_LOOKUP_DEFAULT "LookupDefault"
+#define GCONF_DBUS_DATABASE_SET "Set"
+#define GCONF_DBUS_DATABASE_UNSET "UnSet"
+#define GCONF_DBUS_DATABASE_RECURSIVE_UNSET "RecursiveUnset"
+#define GCONF_DBUS_DATABASE_DIR_EXISTS "DirExists"
+#define GCONF_DBUS_DATABASE_GET_ALL_ENTRIES "AllEntries"
+#define GCONF_DBUS_DATABASE_GET_ALL_DIRS "AllDirs"
+#define GCONF_DBUS_DATABASE_SET_SCHEMA "SetSchema"
+#define GCONF_DBUS_DATABASE_SUGGEST_SYNC "SuggestSync"
+
+#define GCONF_DBUS_DATABASE_ADD_NOTIFY "AddNotify"
+#define GCONF_DBUS_DATABASE_REMOVE_NOTIFY "RemoveNotify"
+
+#define GCONF_DBUS_LISTENER_NOTIFY "Notify"
+
+#define GCONF_DBUS_CLIENT_SERVICE "org.gnome.GConf.ClientService"
+#define GCONF_DBUS_CLIENT_OBJECT "/org/gnome/GConf/Client"
+#define GCONF_DBUS_CLIENT_INTERFACE "org.gnome.GConf.Client"
+
+#define GCONF_DBUS_UNSET_INCLUDING_SCHEMA_NAMES 0x1
+
+#define GCONF_DBUS_ERROR_FAILED "org.gnome.GConf.Error.Failed"
+#define GCONF_DBUS_ERROR_NO_PERMISSION "org.gnome.GConf.Error.NoPermission"
+#define GCONF_DBUS_ERROR_BAD_ADDRESS "org.gnome.GConf.Error.BadAddress"
+#define GCONF_DBUS_ERROR_BAD_KEY "org.gnome.GConf.Error.BadKey"
+#define GCONF_DBUS_ERROR_PARSE_ERROR "org.gnome.GConf.Error.ParseError"
+#define GCONF_DBUS_ERROR_CORRUPT "org.gnome.GConf.Error.Corrupt"
+#define GCONF_DBUS_ERROR_TYPE_MISMATCH "org.gnome.GConf.Error.TypeMismatch"
+#define GCONF_DBUS_ERROR_IS_DIR "org.gnome.GConf.Error.IsDir"
+#define GCONF_DBUS_ERROR_IS_KEY "org.gnome.GConf.Error.IsKey"
+#define GCONF_DBUS_ERROR_NO_WRITABLE_DATABASE "org.gnome.GConf.Error.NoWritableDatabase"
+#define GCONF_DBUS_ERROR_IN_SHUTDOWN "org.gnome.GConf.Error.InShutdown"
+#define GCONF_DBUS_ERROR_OVERRIDDEN "org.gnome.GConf.Error.Overriden"
+#define GCONF_DBUS_ERROR_LOCK_FAILED "org.gnome.GConf.Error.LockFailed"
+
+void gconf_dbus_utils_append_value (DBusMessageIter *iter,
+ const GConfValue *value);
+GConfValue *gconf_dbus_utils_get_value (DBusMessageIter *iter);
+
+void gconf_dbus_utils_append_entry_values (DBusMessageIter *iter,
+ const gchar *key,
+ const GConfValue *value,
+ gboolean is_default,
+ gboolean is_writable,
+ const gchar *schema_name);
+gboolean gconf_dbus_utils_get_entry_values (DBusMessageIter *iter,
+ gchar **key,
+ GConfValue **value,
+ gboolean *is_default,
+ gboolean *is_writable,
+ gchar **schema_name);
+
+void gconf_dbus_utils_append_entries (DBusMessageIter *iter,
+ GSList *entries);
+
+GSList *gconf_dbus_utils_get_entries (DBusMessageIter *iter, const gchar *dir);
+
+
+#endif/* GCONF_DBUS_UTILS_H */
diff --git a/gconf/gconf-dbus.c b/gconf/gconf-dbus.c
new file mode 100644
index 00000000..8071e94b
--- /dev/null
+++ b/gconf/gconf-dbus.c
@@ -0,0 +1,2468 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+
+/* GConf
+ * Copyright (C) 1999, 2000 Red Hat Inc.
+ * Copyright (C) 2003 Imendio AB
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "gconf.h"
+#include "gconf-dbus-utils.h"
+#include "gconf-internals.h"
+#include "gconf-sources.h"
+#include "gconf-locale.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define d(x)
+
+#define DAEMON_NAME_OWNER_CHANGED_RULE \
+ "type='signal',member='NameOwnerChanged',arg0='org.gnome.GConf'"
+#define NOTIFY_RULE \
+ "type='method_call',interface='org.gnome.GConf.Database',member='Notify'"
+#define DAEMON_DISCONNECTED_RULE \
+ "type='signal',member='Disconnected'"
+
+struct _GConfEngine {
+ guint refcount;
+
+ gchar *database;
+
+ GHashTable *notify_dirs;
+ GHashTable *notify_ids;
+
+ /* If non-NULL, this is a local engine;
+ local engines don't do notification! */
+ GConfSources* local_sources;
+
+ /* A list of addresses that make up this db
+ * if this is not the default engine;
+ * NULL if it's the default
+ */
+ GSList *addresses;
+
+ /* A concatentation of the addresses above.
+ */
+ char *persistent_address;
+
+ gpointer user_data;
+ GDestroyNotify dnotify;
+
+ gpointer owner;
+ int owner_use_count;
+
+ guint is_default : 1;
+
+ /* If TRUE, this is a local engine (and therefore
+ * has no ctable and no notifications)
+ */
+ guint is_local : 1;
+};
+
+typedef struct {
+ gchar* namespace_section;
+ guint client_id;
+
+ GConfEngine* conf; /* Engine we're associated with */
+ GConfNotifyFunc func;
+ gpointer user_data;
+} GConfCnxn;
+
+typedef struct {
+ GList *cnxns; /* List of connections to be notified below the dir */
+} CnxnsData;
+
+
+static DBusConnection *global_conn = NULL;
+static gboolean service_running = FALSE;
+static gboolean needs_reconnect = FALSE;
+static GConfEngine *default_engine = NULL;
+static GHashTable *engines_by_db = NULL;
+static GHashTable *engines_by_address = NULL;
+static gboolean dbus_disconnected = FALSE;
+
+static gboolean ensure_dbus_connection (void);
+static gboolean ensure_service (gboolean start_if_not_found,
+ GError **err);
+static gboolean ensure_database (GConfEngine *conf,
+ gboolean start_if_not_found,
+ GError **err);
+
+static void gconf_engine_detach (GConfEngine *conf);
+static void gconf_engine_set_database (GConfEngine *conf,
+ const gchar *db);
+static const gchar *gconf_engine_get_database (GConfEngine *conf,
+ gboolean start_if_not_found,
+ GError **err);
+
+static void register_engine (GConfEngine *conf);
+static void unregister_engine (GConfEngine *conf);
+static GConfCnxn * gconf_cnxn_new (GConfEngine *conf,
+ const gchar *namespace_section,
+ GConfNotifyFunc func,
+ gpointer user_data);
+static void gconf_cnxn_destroy (GConfCnxn *cnxn);
+static void gconf_cnxn_notify (GConfCnxn *cnxn,
+ GConfEntry *entry);
+static GConfCnxn * gconf_cnxn_lookup_id (GConfEngine *conf,
+ guint client_id);
+static GList * gconf_cnxn_lookup_dir (GConfEngine *conf,
+ const gchar *dir);
+static void gconf_cnxn_insert (GConfEngine *conf,
+ const gchar *dir,
+ guint client_id,
+ GConfCnxn *cnxn);
+static void gconf_cnxn_remove (GConfEngine *conf,
+ GConfCnxn *cnxn);
+static gboolean send_notify_add (GConfEngine *conf,
+ GConfCnxn *cnxn,
+ GError **err);
+static void reinitialize_databases (void);
+static DBusHandlerResult
+ gconf_dbus_message_filter (DBusConnection *dbus_conn,
+ DBusMessage *message,
+ gpointer user_data);
+static GConfEngine *lookup_engine_by_addresses (GSList *addresses);
+static GConfEngine *lookup_engine_by_database (const gchar *db);
+static gboolean gconf_handle_dbus_exception (DBusMessage *message,
+ DBusError *derr,
+ GError **gerr);
+static void gconf_detach_config_server (void);
+static DBusHandlerResult
+ handle_notify (DBusConnection *connection,
+ DBusMessage *message,
+ GConfEngine *conf);
+
+
+#define CHECK_OWNER_USE(engine) \
+ do { if ((engine)->owner && (engine)->owner_use_count == 0) \
+ g_warning ("%s: You can't use a GConfEngine that has an active " \
+ "GConfClient wrapper object. Use GConfClient API instead.", \
+ G_GNUC_FUNCTION); \
+ } while (0)
+
+
+static GConfError
+dbus_error_name_to_gconf_errno (const char *name)
+{
+ int i;
+ struct
+ {
+ const char *name;
+ GConfError error;
+ } errors [] = {
+ { GCONF_DBUS_ERROR_FAILED, GCONF_ERROR_FAILED },
+ { GCONF_DBUS_ERROR_NO_PERMISSION, GCONF_ERROR_NO_PERMISSION },
+ { GCONF_DBUS_ERROR_BAD_ADDRESS, GCONF_ERROR_BAD_ADDRESS },
+ { GCONF_DBUS_ERROR_BAD_KEY, GCONF_ERROR_BAD_KEY },
+ { GCONF_DBUS_ERROR_PARSE_ERROR, GCONF_ERROR_PARSE_ERROR },
+ { GCONF_DBUS_ERROR_CORRUPT, GCONF_ERROR_CORRUPT },
+ { GCONF_DBUS_ERROR_TYPE_MISMATCH, GCONF_ERROR_TYPE_MISMATCH },
+ { GCONF_DBUS_ERROR_IS_DIR, GCONF_ERROR_IS_DIR },
+ { GCONF_DBUS_ERROR_IS_KEY, GCONF_ERROR_IS_KEY },
+ { GCONF_DBUS_ERROR_OVERRIDDEN, GCONF_ERROR_OVERRIDDEN },
+ { GCONF_DBUS_ERROR_LOCK_FAILED, GCONF_ERROR_LOCK_FAILED },
+ { GCONF_DBUS_ERROR_NO_WRITABLE_DATABASE, GCONF_ERROR_NO_WRITABLE_DATABASE },
+ { GCONF_DBUS_ERROR_IN_SHUTDOWN, GCONF_ERROR_IN_SHUTDOWN },
+ };
+
+ for (i = 0; i < G_N_ELEMENTS (errors); i++)
+ {
+ if (strcmp (name, errors[i].name) == 0)
+ return errors[i].error;
+ }
+
+ g_assert_not_reached ();
+
+ return GCONF_ERROR_SUCCESS;
+}
+
+/* If no error is detected, return FALSE with no side-effects. If an error is
+ * detected, return TRUE, set gerr, unref message and free derr.
+ */
+static gboolean
+gconf_handle_dbus_exception (DBusMessage *message, DBusError *derr, GError **gerr)
+{
+ char *error_string;
+ const char *name;
+
+ if (message == NULL)
+ {
+ if (derr && dbus_error_is_set (derr))
+ {
+ if (gerr)
+ {
+ *gerr = gconf_error_new (GCONF_ERROR_NO_SERVER, _("D-BUS error: %s"),
+ derr->message);
+ }
+ }
+ else
+ {
+ if (gerr)
+ *gerr = gconf_error_new (GCONF_ERROR_FAILED, _("Unknown error"));
+ }
+
+ if (derr)
+ dbus_error_free (derr);
+
+ return TRUE;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+ return FALSE;
+
+ if (derr)
+ dbus_error_free (derr);
+
+ name = dbus_message_get_member (message);
+
+ dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &error_string,
+ DBUS_TYPE_INVALID);
+
+ if (g_str_has_prefix (name, "org.freedesktop.DBus.Error"))
+ {
+ if (gerr)
+ *gerr = gconf_error_new (GCONF_ERROR_NO_SERVER, _("D-BUS error: %s"),
+ error_string);
+ }
+ else if (g_str_has_prefix (name, "org.gnome.GConf.Error"))
+ {
+ if (gerr)
+ {
+ GConfError en;
+
+ en = dbus_error_name_to_gconf_errno (name);
+ *gerr = gconf_error_new (en, error_string);
+ }
+ }
+ else
+ {
+ if (gerr)
+ *gerr = gconf_error_new (GCONF_ERROR_FAILED, _("Unknown error %s: %s"),
+ name, error_string);
+ }
+
+ dbus_message_unref (message);
+
+ return TRUE;
+}
+
+static GConfEngine*
+gconf_engine_blank (gboolean remote)
+{
+ GConfEngine* conf;
+
+ _gconf_init_i18n ();
+
+ conf = g_new0 (GConfEngine, 1);
+
+ conf->refcount = 1;
+
+ conf->owner = NULL;
+ conf->owner_use_count = 0;
+
+ if (remote)
+ {
+ conf->database = NULL;
+
+ conf->notify_dirs = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ conf->notify_ids = g_hash_table_new (NULL, NULL);
+
+ conf->local_sources = NULL;
+ conf->is_local = FALSE;
+ conf->is_default = TRUE;
+ }
+ else
+ {
+ conf->database = NULL;
+ conf->notify_ids = NULL;
+ conf->notify_dirs = NULL;
+ conf->local_sources = NULL;
+ conf->is_local = TRUE;
+ conf->is_default = FALSE;
+ }
+
+ return conf;
+}
+
+void
+gconf_engine_set_owner (GConfEngine *engine,
+ gpointer client)
+{
+ g_return_if_fail (engine->owner_use_count == 0);
+
+ engine->owner = client;
+}
+
+void
+gconf_engine_push_owner_usage (GConfEngine *engine,
+ gpointer client)
+{
+ g_return_if_fail (engine->owner == client);
+
+ engine->owner_use_count += 1;
+}
+
+void
+gconf_engine_pop_owner_usage (GConfEngine *engine,
+ gpointer client)
+{
+ g_return_if_fail (engine->owner == client);
+ g_return_if_fail (engine->owner_use_count > 0);
+
+ engine->owner_use_count -= 1;
+}
+
+static GConfEngine *
+lookup_engine_by_database (const gchar *db)
+{
+ if (engines_by_db)
+ return g_hash_table_lookup (engines_by_db, db);
+ else
+ return NULL;
+}
+
+static void
+database_hash_value_destroy (gpointer value)
+{
+ GConfEngine *conf = value;
+
+ g_free (conf->database);
+ conf->database = NULL;
+}
+
+static void
+gconf_engine_set_database (GConfEngine *conf,
+ const gchar *db)
+{
+ gconf_engine_detach (conf);
+
+ conf->database = g_strdup (db);
+
+ if (engines_by_db == NULL)
+ engines_by_db = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ database_hash_value_destroy);
+
+ g_hash_table_insert (engines_by_db, conf->database, conf);
+}
+
+static void
+gconf_engine_detach (GConfEngine *conf)
+{
+ if (conf->database != NULL)
+ {
+ g_hash_table_remove (engines_by_db, conf->database);
+ }
+}
+
+static gboolean
+ensure_dbus_connection (void)
+{
+ DBusError error;
+
+ if (global_conn != NULL)
+ return TRUE;
+
+ if (dbus_disconnected)
+ {
+ g_warning ("The connection to DBus was broken. Can't reinitialize it.");
+ return FALSE;
+ }
+
+ dbus_error_init (&error);
+
+ global_conn = dbus_bus_get_private (DBUS_BUS_SESSION, &error);
+
+ if (!global_conn)
+ {
+ g_warning ("Client failed to connect to the D-BUS daemon:\n%s", error.message);
+
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ dbus_connection_setup_with_g_main (global_conn, NULL);
+
+ dbus_connection_set_exit_on_disconnect (global_conn, FALSE);
+
+ dbus_bus_add_match (global_conn, DAEMON_NAME_OWNER_CHANGED_RULE, NULL);
+ dbus_bus_add_match (global_conn, NOTIFY_RULE, NULL);
+ dbus_bus_add_match (global_conn, DAEMON_DISCONNECTED_RULE, NULL);
+
+ dbus_connection_add_filter (global_conn, gconf_dbus_message_filter,
+ NULL, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+ensure_service (gboolean start_if_not_found,
+ GError **err)
+{
+ DBusError error;
+
+ if (global_conn == NULL)
+ {
+ if (!ensure_dbus_connection ())
+ {
+ g_set_error (err, GCONF_ERROR,
+ GCONF_ERROR_NO_SERVER,
+ _("No D-BUS daemon running\n"));
+ return FALSE;
+ }
+
+ g_assert (global_conn != NULL);
+ }
+
+ if (service_running)
+ return TRUE;
+
+ if (start_if_not_found)
+ {
+ d(g_print ("* activate_service, activating\n"));
+
+ dbus_error_init (&error);
+
+ if (!dbus_bus_start_service_by_name (global_conn,
+ GCONF_DBUS_SERVICE,
+ 0,
+ NULL,
+ &error))
+ {
+ const gchar *msg;
+
+ if (dbus_error_is_set (&error))
+ msg = error.message;
+ else
+ msg = _("Unknown error");
+
+ g_set_error (err, GCONF_ERROR,
+ GCONF_ERROR_NO_SERVER,
+ _("Failed to activate configuration server: %s\n"),
+ msg);
+
+ if (dbus_error_is_set (&error))
+ dbus_error_free (&error);
+
+ return FALSE;
+ }
+
+ service_running = TRUE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ensure_database (GConfEngine *conf,
+ gboolean start_if_not_found,
+ GError **err)
+{
+ DBusMessage *message, *reply;
+ DBusError error;
+ gchar *db;
+
+ g_return_val_if_fail (!conf->is_local, TRUE);
+
+ if (!ensure_service (start_if_not_found, err))
+ return FALSE;
+
+ if (needs_reconnect)
+ {
+ /* Re-connect notifications and re-get database names from the previous
+ * (if any) instance of the GConf service.
+ */
+ needs_reconnect = FALSE;
+ reinitialize_databases ();
+ }
+
+ if (conf->database != NULL)
+ return TRUE;
+
+ if (conf->is_default)
+ {
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ GCONF_DBUS_SERVER_OBJECT,
+ GCONF_DBUS_SERVER_INTERFACE,
+ GCONF_DBUS_SERVER_GET_DEFAULT_DB);
+ }
+ else
+ {
+ gchar *addresses;
+
+ addresses = gconf_address_list_get_persistent_name (conf->addresses);
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ GCONF_DBUS_SERVER_OBJECT,
+ GCONF_DBUS_SERVER_INTERFACE,
+ GCONF_DBUS_SERVER_GET_DB);
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &addresses,
+ DBUS_TYPE_INVALID);
+
+ g_free (addresses);
+ }
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn,
+ message, -1, &error);
+
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ dbus_message_get_args (reply,
+ NULL,
+ DBUS_TYPE_STRING, &db,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_unref (reply);
+
+ if (db == NULL)
+ {
+ if (err)
+ *err = gconf_error_new (GCONF_ERROR_BAD_ADDRESS,
+ _("Server couldn't resolve the address `%s'"),
+ conf->persistent_address);
+
+ return FALSE;
+ }
+
+ gconf_engine_set_database (conf, db);
+
+ return TRUE;
+}
+
+static const gchar *
+gconf_engine_get_database (GConfEngine *conf,
+ gboolean start_if_not_found,
+ GError **err)
+{
+ if (!ensure_database (conf, start_if_not_found, err))
+ return NULL;
+ else
+ return conf->database;
+}
+
+static gboolean
+gconf_engine_is_local (GConfEngine* conf)
+{
+ return conf->is_local;
+}
+
+static void
+register_engine (GConfEngine *conf)
+{
+ g_return_if_fail (conf->addresses != NULL);
+
+ g_assert (conf->persistent_address == NULL);
+
+ conf->persistent_address =
+ gconf_address_list_get_persistent_name (conf->addresses);
+
+ if (engines_by_address == NULL)
+ engines_by_address = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (engines_by_address, conf->persistent_address, conf);
+}
+
+static void
+unregister_engine (GConfEngine *conf)
+{
+ g_return_if_fail (engines_by_address != NULL);
+
+ g_assert (conf->persistent_address != NULL);
+
+ g_hash_table_remove (engines_by_address, conf->persistent_address);
+ g_free (conf->persistent_address);
+ conf->persistent_address = NULL;
+
+ if (g_hash_table_size (engines_by_address) == 0)
+ {
+ g_hash_table_destroy (engines_by_address);
+
+ engines_by_address = NULL;
+ }
+}
+
+static GConfEngine *
+lookup_engine_by_addresses (GSList *addresses)
+{
+ if (engines_by_address != NULL)
+ {
+ GConfEngine *retval;
+ char *key;
+
+ key = gconf_address_list_get_persistent_name (addresses);
+
+ retval = g_hash_table_lookup (engines_by_address, key);
+
+ g_free (key);
+
+ return retval;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Connection maintenance
+ */
+
+static GConfCnxn *
+gconf_cnxn_new (GConfEngine *conf,
+ const gchar *namespace_section,
+ GConfNotifyFunc func,
+ gpointer user_data)
+{
+ GConfCnxn *cnxn;
+ static guint next_id = 1;
+
+ cnxn = g_new0 (GConfCnxn, 1);
+
+ cnxn->namespace_section = g_strdup (namespace_section);
+ cnxn->conf = conf;
+ cnxn->client_id = next_id;
+ cnxn->func = func;
+ cnxn->user_data = user_data;
+
+ ++next_id;
+
+ return cnxn;
+}
+
+static void
+gconf_cnxn_destroy (GConfCnxn* cnxn)
+{
+ g_free (cnxn->namespace_section);
+ g_free (cnxn);
+}
+
+static void
+gconf_cnxn_notify (GConfCnxn* cnxn,
+ GConfEntry *entry)
+{
+ (*cnxn->func) (cnxn->conf, cnxn->client_id,
+ entry,
+ cnxn->user_data);
+}
+
+static GList *
+gconf_cnxn_lookup_dir (GConfEngine *conf, const gchar *dir)
+{
+ CnxnsData *data;
+
+ data = g_hash_table_lookup (conf->notify_dirs, dir);
+
+ if (data == NULL)
+ return NULL;
+
+ return data->cnxns;
+}
+
+static GConfCnxn *
+gconf_cnxn_lookup_id (GConfEngine *conf, guint client_id)
+{
+ gint id = client_id;
+
+ return g_hash_table_lookup (conf->notify_ids, GINT_TO_POINTER (id));
+}
+
+static void
+gconf_cnxn_insert (GConfEngine *conf, const gchar *dir, guint client_id, GConfCnxn *cnxn)
+{
+ CnxnsData *data;
+ gint id = client_id;
+
+ data = g_hash_table_lookup (conf->notify_dirs, dir);
+
+ if (data == NULL)
+ {
+ data = g_new (CnxnsData, 1);
+ data->cnxns = NULL;
+ g_hash_table_insert (conf->notify_dirs, g_strdup (dir), data);
+ }
+
+ data->cnxns = g_list_prepend (data->cnxns, cnxn);
+
+ g_hash_table_insert (conf->notify_ids, GINT_TO_POINTER (id), cnxn);
+}
+
+static void
+gconf_cnxn_remove (GConfEngine *conf, GConfCnxn *cnxn)
+{
+ CnxnsData *data;
+ gint id = cnxn->client_id;
+
+ g_hash_table_remove (conf->notify_ids, GINT_TO_POINTER (id));
+
+ data = g_hash_table_lookup (conf->notify_dirs, cnxn->namespace_section);
+ if (data)
+ {
+ data->cnxns = g_list_remove (data->cnxns, cnxn);
+
+ if (data->cnxns == NULL)
+ {
+ g_hash_table_remove (conf->notify_dirs, cnxn->namespace_section);
+ g_free (data);
+
+ gconf_cnxn_destroy (cnxn);
+ }
+ }
+}
+
+
+/*
+ * Public Interface
+ */
+
+GConfEngine*
+gconf_engine_get_local (const gchar* address,
+ GError** err)
+{
+ GConfEngine *conf;
+ GConfSource *source;
+
+ g_return_val_if_fail (address != NULL, NULL);
+ g_return_val_if_fail (err == NULL || *err == NULL, NULL);
+
+ source = gconf_resolve_address (address, err);
+
+ if (source == NULL)
+ return NULL;
+
+ conf = gconf_engine_blank (FALSE);
+
+ conf->local_sources = gconf_sources_new_from_source (source);
+
+ g_assert (gconf_engine_is_local (conf));
+
+ return conf;
+}
+
+GConfEngine *
+gconf_engine_get_local_for_addresses (GSList *addresses,
+ GError **err)
+{
+ GConfEngine *conf;
+
+ g_return_val_if_fail (addresses != NULL, NULL);
+ g_return_val_if_fail (err == NULL || *err == NULL, NULL);
+
+ conf = gconf_engine_blank (FALSE);
+
+ conf->local_sources = gconf_sources_new_from_addresses (addresses, err);
+
+ g_assert (gconf_engine_is_local (conf));
+
+ return conf;
+}
+
+GConfEngine*
+gconf_engine_get_default (void)
+{
+ GConfEngine* conf = NULL;
+
+ if (default_engine)
+ conf = default_engine;
+
+ if (conf == NULL)
+ {
+ conf = gconf_engine_blank (TRUE);
+
+ conf->is_default = TRUE;
+
+ default_engine = conf;
+ }
+ else
+ conf->refcount += 1;
+
+ return conf;
+}
+
+GConfEngine*
+gconf_engine_get_for_address (const gchar* address, GError** err)
+{
+ GConfEngine *conf;
+ GSList *addresses;
+
+ addresses = g_slist_append (NULL, g_strdup (address));
+
+ conf = lookup_engine_by_addresses (addresses);
+
+ if (conf == NULL)
+ {
+ conf = gconf_engine_blank (TRUE);
+
+ conf->is_default = FALSE;
+ conf->addresses = addresses;
+
+ if (!ensure_database (conf, TRUE, err))
+ {
+ gconf_engine_unref (conf);
+ return NULL;
+ }
+
+ register_engine (conf);
+ }
+ else
+ {
+ g_free (addresses->data);
+ g_slist_free (addresses);
+ conf->refcount += 1;
+ }
+
+ return conf;
+}
+
+GConfEngine*
+gconf_engine_get_for_addresses (GSList *addresses, GError** err)
+{
+ GConfEngine* conf;
+
+ conf = lookup_engine_by_addresses (addresses);
+
+ if (conf == NULL)
+ {
+ GSList *tmp;
+
+ conf = gconf_engine_blank (TRUE);
+
+ conf->is_default = FALSE;
+ conf->addresses = NULL;
+
+ tmp = addresses;
+ while (tmp != NULL)
+ {
+ conf->addresses = g_slist_append (conf->addresses,
+ g_strdup (tmp->data));
+ tmp = tmp->next;
+ }
+
+ if (!ensure_database (conf, TRUE, err))
+ {
+ gconf_engine_unref (conf);
+ return NULL;
+ }
+
+ register_engine (conf);
+ }
+ else
+ conf->refcount += 1;
+
+ return conf;
+}
+
+void
+gconf_engine_ref (GConfEngine* conf)
+{
+ g_return_if_fail (conf != NULL);
+ g_return_if_fail (conf->refcount > 0);
+
+ conf->refcount += 1;
+}
+
+void
+gconf_engine_unref (GConfEngine* conf)
+{
+ g_return_if_fail (conf != NULL);
+ g_return_if_fail (conf->refcount > 0);
+
+ conf->refcount -= 1;
+
+ if (conf->refcount == 0)
+ {
+ if (gconf_engine_is_local (conf))
+ {
+ if (conf->local_sources != NULL)
+ gconf_sources_free (conf->local_sources);
+ }
+ else
+ {
+ /* Remove all connections associated with this GConf */
+
+ /* FIXME: remove notify_ids from hash when we have
+ add/remove_notify. */
+
+ if (conf->dnotify)
+ {
+ (* conf->dnotify) (conf->user_data);
+ }
+
+ /* do this after removing the notifications,
+ to avoid funky race conditions */
+ if (conf->addresses)
+ {
+ gconf_address_list_free (conf->addresses);
+ conf->addresses = NULL;
+ }
+
+ if (conf->persistent_address)
+ {
+ unregister_engine (conf);
+ }
+
+ /* Release the ConfigDatabase */
+ gconf_engine_detach (conf);
+
+ if (conf->notify_ids)
+ g_hash_table_destroy (conf->notify_ids);
+ if (conf->notify_dirs)
+ g_hash_table_destroy (conf->notify_dirs);
+ }
+
+ if (conf == default_engine)
+ default_engine = NULL;
+
+ g_free(conf);
+ }
+}
+
+void
+gconf_engine_set_user_data (GConfEngine *engine,
+ gpointer data,
+ GDestroyNotify dnotify)
+{
+ if (engine->dnotify)
+ {
+ (* engine->dnotify) (engine->user_data);
+ }
+
+ engine->dnotify = dnotify;
+ engine->user_data = data;
+}
+
+gpointer
+gconf_engine_get_user_data (GConfEngine *engine)
+{
+ return engine->user_data;
+}
+
+static gboolean
+send_notify_add (GConfEngine *conf,
+ GConfCnxn *cnxn,
+ GError **err)
+{
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, 0);
+
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_ADD_NOTIFY);
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &cnxn->namespace_section,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn,
+ message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+guint
+gconf_engine_notify_add (GConfEngine* conf,
+ const gchar* namespace_section,
+ GConfNotifyFunc func,
+ gpointer user_data,
+ GError** err)
+{
+ GConfCnxn *cnxn;
+
+ g_return_val_if_fail (!gconf_engine_is_local (conf), 0);
+
+ CHECK_OWNER_USE (conf);
+
+ if (gconf_engine_is_local (conf))
+ {
+ if (err)
+ *err = gconf_error_new (GCONF_ERROR_LOCAL_ENGINE,
+ _("Can't add notifications to a local configuration source"));
+
+ return 0;
+ }
+
+ cnxn = gconf_cnxn_new (conf, namespace_section, func, user_data);
+ gconf_cnxn_insert (conf, namespace_section, cnxn->client_id, cnxn);
+
+ if (!send_notify_add (conf, cnxn, err))
+ {
+ gconf_cnxn_remove (conf, cnxn);
+ return 0;
+ }
+
+ return cnxn->client_id;
+}
+
+void
+gconf_engine_notify_remove (GConfEngine* conf,
+ guint client_id)
+{
+ GConfCnxn *cnxn;
+ const gchar *db;
+ gchar *namespace_section = NULL;
+ DBusMessage *message, *reply;
+ DBusError error;
+
+ CHECK_OWNER_USE (conf);
+
+ if (gconf_engine_is_local(conf))
+ return;
+
+ cnxn = gconf_cnxn_lookup_id (conf, client_id);
+ if (cnxn != NULL)
+ {
+ namespace_section = g_strdup (cnxn->namespace_section);
+ gconf_cnxn_remove (conf, cnxn);
+ }
+
+ g_return_if_fail (cnxn != NULL);
+
+ db = gconf_engine_get_database (conf, TRUE, NULL);
+
+ if (db == NULL)
+ return;
+
+ d(g_print ("notify_remove, id = %d\n", client_id));
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_REMOVE_NOTIFY);
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &namespace_section,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ dbus_error_free (&error);
+
+ g_free (namespace_section);
+
+ dbus_message_unref (reply);
+}
+
+GConfValue *
+gconf_engine_get_fuller (GConfEngine *conf,
+ const gchar *key,
+ const gchar *locale,
+ gboolean use_schema_default,
+ gboolean *is_default_p,
+ gboolean *is_writable_p,
+ gchar **schema_name_p,
+ GError **err)
+{
+ GConfValue* val;
+ const gchar *db;
+ gboolean is_default = FALSE;
+ gboolean is_writable = TRUE;
+ gchar *schema_name = NULL;
+ DBusMessage *message, *reply;
+ DBusError error;
+ DBusMessageIter iter;
+ gboolean success;
+
+ g_return_val_if_fail(conf != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(err == NULL || *err == NULL, NULL);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check (key, err))
+ return NULL;
+
+ if (gconf_engine_is_local (conf))
+ {
+ gchar **locale_list;
+
+ locale_list = gconf_split_locale (locale);
+
+ val = gconf_sources_query_value (conf->local_sources,
+ key,
+ (const gchar**)locale_list,
+ use_schema_default,
+ &is_default,
+ &is_writable,
+ schema_name_p ? &schema_name : NULL,
+ err);
+
+ if (locale_list != NULL)
+ g_strfreev(locale_list);
+
+ if (is_default_p)
+ *is_default_p = is_default;
+
+ if (is_writable_p)
+ *is_writable_p = is_writable;
+
+ if (schema_name_p)
+ *schema_name_p = schema_name;
+ else
+ g_free (schema_name);
+
+ return val;
+ }
+
+ g_assert (!gconf_engine_is_local (conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, NULL);
+ return NULL;
+ }
+
+ if (schema_name_p)
+ *schema_name_p = NULL;
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_LOOKUP_EXTENDED);
+
+ locale = locale ? locale : gconf_current_locale();
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_BOOLEAN, &use_schema_default,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return NULL;
+
+ dbus_message_iter_init (reply, &iter);
+
+ /* If there is no struct (entry) here, there is no value. */
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRUCT)
+ {
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ success = gconf_dbus_utils_get_entry_values (&iter,
+ NULL,
+ &val,
+ &is_default,
+ &is_writable,
+ &schema_name);
+
+ dbus_message_unref (reply);
+
+ if (!success)
+ {
+ if (err)
+ g_set_error (err, GCONF_ERROR,
+ GCONF_ERROR_FAILED,
+ _("Couldn't get value"));
+
+ return NULL;
+ }
+
+ if (is_default_p)
+ *is_default_p = !!is_default;
+
+ if (is_writable_p)
+ *is_writable_p = !!is_writable;
+
+ if (schema_name && schema_name[0] != '/')
+ {
+ schema_name = NULL;
+ }
+
+ if (schema_name_p)
+ *schema_name_p = g_strdup (schema_name);
+
+ return val;
+}
+
+GConfValue *
+gconf_engine_get_full (GConfEngine *conf,
+ const gchar *key,
+ const gchar *locale,
+ gboolean use_schema_default,
+ gboolean *is_default_p,
+ gboolean *is_writable_p,
+ GError **err)
+{
+ return gconf_engine_get_fuller (conf, key, locale, use_schema_default,
+ is_default_p, is_writable_p,
+ NULL, err);
+}
+
+GConfEntry*
+gconf_engine_get_entry (GConfEngine* conf,
+ const gchar* key,
+ const gchar* locale,
+ gboolean use_schema_default,
+ GError** err)
+{
+ gboolean is_writable = TRUE;
+ gboolean is_default = FALSE;
+ GConfValue *val;
+ GError *error;
+ GConfEntry *entry;
+ gchar *schema_name;
+
+ CHECK_OWNER_USE (conf);
+
+ schema_name = NULL;
+ error = NULL;
+ val = gconf_engine_get_fuller (conf, key, locale, use_schema_default,
+ &is_default, &is_writable,
+ &schema_name, &error);
+ if (error != NULL)
+ {
+ g_propagate_error (err, error);
+ return NULL;
+ }
+
+ entry = gconf_entry_new_nocopy (g_strdup (key), val);
+
+ gconf_entry_set_is_default (entry, is_default);
+ gconf_entry_set_is_writable (entry, is_writable);
+ gconf_entry_set_schema_name (entry, schema_name);
+
+ g_free (schema_name);
+
+ return entry;
+}
+
+GConfValue*
+gconf_engine_get (GConfEngine* conf, const gchar* key, GError** err)
+{
+ return gconf_engine_get_with_locale (conf, key, NULL, err);
+}
+
+GConfValue*
+gconf_engine_get_with_locale (GConfEngine* conf, const gchar* key,
+ const gchar* locale,
+ GError** err)
+{
+ return gconf_engine_get_full (conf, key, locale, TRUE,
+ NULL, NULL, err);
+}
+
+GConfValue*
+gconf_engine_get_without_default (GConfEngine* conf, const gchar* key,
+ GError** err)
+{
+ return gconf_engine_get_full (conf, key, NULL, FALSE, NULL, NULL, err);
+}
+
+GConfValue*
+gconf_engine_get_default_from_schema (GConfEngine* conf,
+ const gchar* key,
+ GError** err)
+{
+ GConfValue* val;
+ const gchar *db;
+ const gchar *locale;
+ DBusMessage *message, *reply;
+ DBusError error;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail(conf != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(err == NULL || *err == NULL, NULL);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check (key, err))
+ return NULL;
+
+ if (gconf_engine_is_local(conf))
+ {
+ gchar** locale_list;
+
+ locale_list = gconf_split_locale(gconf_current_locale());
+
+ val = gconf_sources_query_default_value(conf->local_sources,
+ key,
+ (const gchar**)locale_list,
+ NULL,
+ err);
+
+ if (locale_list != NULL)
+ g_strfreev(locale_list);
+
+ return val;
+ }
+
+ g_assert (!gconf_engine_is_local (conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, NULL);
+ return NULL;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_LOOKUP_DEFAULT);
+
+ locale = gconf_current_locale();
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return NULL;
+
+ dbus_message_iter_init (reply, &iter);
+
+ /* If there is no struct (entry) here, there is no value. */
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRUCT)
+ {
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ val = gconf_dbus_utils_get_value (&iter);
+
+ dbus_message_unref (reply);
+
+ if (!val)
+ {
+ if (err)
+ g_set_error (err, GCONF_ERROR,
+ GCONF_ERROR_FAILED,
+ _("Couldn't get value"));
+
+ return NULL;
+ }
+
+ return val;
+}
+
+gboolean
+gconf_engine_set (GConfEngine* conf, const gchar* key,
+ const GConfValue* value, GError** err)
+{
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail(conf != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+ g_return_val_if_fail(value != NULL, FALSE);
+ g_return_val_if_fail(value->type != GCONF_VALUE_INVALID, FALSE);
+ g_return_val_if_fail( (value->type != GCONF_VALUE_STRING) ||
+ (gconf_value_get_string(value) != NULL) , FALSE );
+ g_return_val_if_fail( (value->type != GCONF_VALUE_LIST) ||
+ (gconf_value_get_list_type(value) != GCONF_VALUE_INVALID), FALSE);
+ g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check (key, err))
+ return FALSE;
+
+ if (!gconf_value_validate (value, err))
+ return FALSE;
+
+ if (gconf_engine_is_local (conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_set_value (conf->local_sources, key, value, NULL, &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ g_assert (!gconf_engine_is_local (conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, FALSE);
+
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_SET);
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_iter_init_append (message, &iter);
+ gconf_dbus_utils_append_value (&iter, value);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+gboolean
+gconf_engine_unset (GConfEngine* conf, const gchar* key, GError** err)
+{
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+ const gchar *empty;
+
+ g_return_val_if_fail (conf != NULL, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check (key, err))
+ return FALSE;
+
+ if (gconf_engine_is_local (conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_unset_value (conf->local_sources, key, NULL, NULL, &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ g_assert(!gconf_engine_is_local(conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, FALSE);
+
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_UNSET);
+
+ empty = "";
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &empty,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+/**
+ * gconf_engine_recursive_unset:
+ * @engine: a #GConfEngine
+ * @key: a key or directory name
+ * @flags: change how the unset is done
+ * @err: return location for a #GError, or %NULL to ignore errors
+ *
+ * Unsets all keys below @key, including @key itself. If any unset
+ * fails, continues on to unset as much as it can. The first
+ * failure is returned in @err.
+ *
+ * Returns: %FALSE if error is set
+ **/
+gboolean
+gconf_engine_recursive_unset (GConfEngine *conf,
+ const char *key,
+ GConfUnsetFlags flags,
+ GError **err)
+{
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+ guint dbus_flags;
+ const gchar *empty;
+
+ g_return_val_if_fail (conf != NULL, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check (key, err))
+ return FALSE;
+
+ if (gconf_engine_is_local (conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_recursive_unset (conf->local_sources, key, NULL,
+ flags, NULL, &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ g_assert (!gconf_engine_is_local (conf));
+
+ dbus_flags = 0;
+ if (flags & GCONF_UNSET_INCLUDING_SCHEMA_NAMES)
+ dbus_flags |= GCONF_DBUS_UNSET_INCLUDING_SCHEMA_NAMES;
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail (err == NULL || *err != NULL, FALSE);
+
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_RECURSIVE_UNSET);
+
+ empty = "";
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &empty,
+ DBUS_TYPE_UINT32, &dbus_flags,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+gboolean
+gconf_engine_associate_schema (GConfEngine* conf, const gchar* key,
+ const gchar* schema_key, GError** err)
+{
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+
+ g_return_val_if_fail (conf != NULL, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ if (!gconf_key_check (key, err))
+ return FALSE;
+
+ if (schema_key && !gconf_key_check (schema_key, err))
+ return FALSE;
+
+ if (gconf_engine_is_local(conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_set_schema (conf->local_sources, key, schema_key, &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free(error);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ g_assert (!gconf_engine_is_local (conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail (err == NULL || *err != NULL, FALSE);
+
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_SET_SCHEMA);
+
+ /* Empty schema string means unset. */
+ schema_key = schema_key ? schema_key : "";
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_STRING, &schema_key,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+static void
+qualify_entries (GSList *entries, const char *dir)
+{
+ GSList *tmp = entries;
+
+ while (tmp != NULL)
+ {
+ GConfEntry *entry = tmp->data;
+ gchar *full;
+
+ full = gconf_concat_dir_and_key (dir, entry->key);
+
+ g_free (entry->key);
+ entry->key = full;
+
+ tmp = g_slist_next (tmp);
+ }
+}
+
+GSList*
+gconf_engine_all_entries (GConfEngine* conf, const gchar* dir, GError** err)
+{
+ GSList* entries = NULL;
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+ DBusMessageIter iter;
+ const gchar *locale;
+
+ g_return_val_if_fail(conf != NULL, NULL);
+ g_return_val_if_fail(dir != NULL, NULL);
+ g_return_val_if_fail(err == NULL || *err == NULL, NULL);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check(dir, err))
+ return NULL;
+
+ if (gconf_engine_is_local(conf))
+ {
+ GError* error = NULL;
+ gchar** locale_list;
+ GSList* retval;
+
+ locale_list = gconf_split_locale(gconf_current_locale());
+
+ retval = gconf_sources_all_entries(conf->local_sources,
+ dir,
+ (const gchar**)locale_list,
+ &error);
+
+ if (locale_list)
+ g_strfreev(locale_list);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free(error);
+ }
+
+ g_assert(retval == NULL);
+
+ return NULL;
+ }
+
+ qualify_entries (retval, dir);
+
+ return retval;
+ }
+
+ g_assert(!gconf_engine_is_local(conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, NULL);
+
+ return NULL;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_GET_ALL_ENTRIES);
+
+ locale = gconf_current_locale ();
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_STRING, &locale,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return NULL;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, NULL);
+
+ dbus_message_iter_init (reply, &iter);
+
+ entries = gconf_dbus_utils_get_entries (&iter, dir);
+
+ dbus_message_unref (reply);
+
+ return entries;
+}
+
+static void
+qualify_keys (GSList *keys, const char *dir)
+{
+ GSList *tmp = keys;
+ while (tmp != NULL)
+ {
+ char *key = tmp->data;
+ gchar *full;
+
+ full = gconf_concat_dir_and_key (dir, key);
+
+ g_free (tmp->data);
+ tmp->data = full;
+
+ tmp = g_slist_next (tmp);
+ }
+}
+
+GSList*
+gconf_engine_all_dirs(GConfEngine* conf, const gchar* dir, GError** err)
+{
+ GSList* subdirs = NULL;
+ const gchar *db;
+ DBusMessage *message, *reply;
+ DBusError error;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+
+ g_return_val_if_fail(conf != NULL, NULL);
+ g_return_val_if_fail(dir != NULL, NULL);
+ g_return_val_if_fail(err == NULL || *err == NULL, NULL);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check(dir, err))
+ return NULL;
+
+ if (gconf_engine_is_local(conf))
+ {
+ GError* error = NULL;
+ GSList* retval;
+
+ retval = gconf_sources_all_dirs(conf->local_sources,
+ dir,
+ &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free(error);
+ }
+
+ g_assert(retval == NULL);
+
+ return NULL;
+ }
+
+ qualify_keys (retval, dir);
+
+ return retval;
+ }
+
+ g_assert(!gconf_engine_is_local(conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail (err == NULL || *err != NULL, NULL);
+
+ return NULL;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_GET_ALL_DIRS);
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return NULL;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, NULL);
+
+ dbus_message_iter_init (reply, &iter);
+
+ dbus_message_iter_recurse (&iter, &array_iter);
+ while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRING)
+ {
+ const gchar *key;
+ gchar *s;
+
+ dbus_message_iter_get_basic (&array_iter, &key);
+
+ s = gconf_concat_dir_and_key (dir, key);
+ subdirs = g_slist_prepend (subdirs, s);
+
+ if (!dbus_message_iter_next (&array_iter))
+ break;
+ }
+
+ dbus_message_unref (reply);
+
+ return subdirs;
+}
+
+/* annoyingly, this is REQUIRED for local sources */
+void
+gconf_engine_suggest_sync(GConfEngine* conf, GError** err)
+{
+ const gchar *db;
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+
+ g_return_if_fail(conf != NULL);
+ g_return_if_fail(err == NULL || *err == NULL);
+
+ CHECK_OWNER_USE (conf);
+
+ if (gconf_engine_is_local(conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_sync_all(conf->local_sources,
+ &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free(error);
+ }
+ return;
+ }
+
+ return;
+ }
+
+ g_assert(!gconf_engine_is_local(conf));
+
+ db = gconf_engine_get_database (conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_if_fail (err == NULL || *err != NULL);
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_SUGGEST_SYNC);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (!gconf_handle_dbus_exception (reply, &error, err))
+ dbus_message_unref (reply);
+}
+
+void
+gconf_clear_cache(GConfEngine* conf, GError** err)
+{
+ g_return_if_fail(conf != NULL);
+ g_return_if_fail(err == NULL || *err == NULL);
+
+ /* don't disallow non-owner use here since you can't do this
+ * via GConfClient API and calling this function won't break
+ * GConfClient anyway
+ */
+
+ if (gconf_engine_is_local(conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_clear_cache(conf->local_sources);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free(error);
+ }
+ return;
+ }
+
+ return;
+ }
+
+ /* Do nothing for non-local case. */
+}
+
+void
+gconf_synchronous_sync(GConfEngine* conf, GError** err)
+{
+ g_return_if_fail(conf != NULL);
+ g_return_if_fail(err == NULL || *err == NULL);
+
+ if (gconf_engine_is_local(conf))
+ {
+ GError* error = NULL;
+
+ gconf_sources_sync_all(conf->local_sources, &error);
+
+ if (error != NULL)
+ {
+ if (err)
+ *err = error;
+ else
+ {
+ g_error_free(error);
+ }
+ return;
+ }
+
+ return;
+ }
+
+ /* Do nothing for non-local case. */
+}
+
+gboolean
+gconf_engine_dir_exists (GConfEngine *conf, const gchar *dir, GError** err)
+{
+ const gchar *db;
+ dbus_bool_t exists;
+ DBusMessage *message, *reply;
+ DBusError error;
+
+ g_return_val_if_fail(conf != NULL, FALSE);
+ g_return_val_if_fail(dir != NULL, FALSE);
+ g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
+
+ CHECK_OWNER_USE (conf);
+
+ if (!gconf_key_check(dir, err))
+ return FALSE;
+
+ if (gconf_engine_is_local(conf))
+ {
+ return gconf_sources_dir_exists(conf->local_sources,
+ dir,
+ err);
+ }
+
+ g_assert(!gconf_engine_is_local(conf));
+
+ db = gconf_engine_get_database(conf, TRUE, err);
+
+ if (db == NULL)
+ {
+ g_return_val_if_fail(err == NULL || *err != NULL, FALSE);
+
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ db,
+ GCONF_DBUS_DATABASE_INTERFACE,
+ GCONF_DBUS_DATABASE_DIR_EXISTS);
+
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &dir,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init (&error);
+ reply = dbus_connection_send_with_reply_and_block (global_conn, message, -1, &error);
+ dbus_message_unref (message);
+
+ if (gconf_handle_dbus_exception (reply, &error, err))
+ return FALSE;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ exists = FALSE;
+ dbus_message_get_args (reply,
+ NULL,
+ DBUS_TYPE_BOOLEAN, &exists,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_unref (reply);
+
+ return !!exists;
+}
+
+void
+gconf_engine_remove_dir (GConfEngine* conf,
+ const gchar* dir,
+ GError** err)
+{
+ g_return_if_fail(conf != NULL);
+ g_return_if_fail(dir != NULL);
+ g_return_if_fail(err == NULL || *err == NULL);
+
+ /* FIXME we have no GConfClient method for doing this */
+ /* CHECK_OWNER_USE (conf); */
+
+ if (!gconf_key_check(dir, err))
+ return;
+
+ if (gconf_engine_is_local(conf))
+ {
+ gconf_sources_remove_dir(conf->local_sources, dir, err);
+ return;
+ }
+}
+
+static void
+cnxn_get_all_func (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GList **list = user_data;
+
+ *list = g_list_prepend (*list, value);
+}
+
+static void
+engines_by_db_get_all_func (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GList **list = user_data;
+
+ *list = g_list_prepend (*list, value);
+}
+
+static void
+reinitialize_databases (void)
+{
+ GList *engines = NULL, *engine;
+ GList *cnxns, *l;
+ GConfEngine *conf;
+
+ if (engines_by_db)
+ g_hash_table_foreach (engines_by_db,
+ engines_by_db_get_all_func,
+ &engines);
+
+ /* Reset databases. */
+ for (engine = engines; engine; engine = engine->next)
+ {
+ conf = engine->data;
+
+ g_hash_table_remove (engines_by_db, conf->database);
+ ensure_database (conf, FALSE, NULL);
+ }
+
+ /* Re-add notifications. */
+ for (engine = engines; engine; engine = engine->next)
+ {
+ conf = engine->data;
+
+ cnxns = NULL;
+ g_hash_table_foreach (conf->notify_ids,
+ cnxn_get_all_func,
+ &cnxns);
+
+ for (l = cnxns; l; l = l->next)
+ {
+ GConfCnxn *cnxn = l->data;
+
+ send_notify_add (conf, cnxn, NULL);
+ }
+
+ g_list_free (cnxns);
+ }
+
+ g_list_free (engines);
+}
+
+static DBusHandlerResult
+gconf_dbus_message_filter (DBusConnection *dbus_conn,
+ DBusMessage *message,
+ gpointer user_data)
+{
+ if (dbus_message_is_method_call (message,
+ GCONF_DBUS_CLIENT_INTERFACE,
+ "Notify"))
+ {
+ return handle_notify (dbus_conn, message, NULL);
+ }
+ else if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected"))
+ {
+ dbus_connection_unref (global_conn);
+ global_conn = NULL;
+ service_running = FALSE;
+ dbus_disconnected = TRUE;
+
+ g_warning ("Got Disconnected from DBus.\n");
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+ char *service;
+ char *old_owner;
+ char *new_owner;
+
+ dbus_message_get_args (message,
+ NULL,
+ DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID);
+
+ if (strcmp (service, GCONF_DBUS_SERVICE) != 0)
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (strcmp (old_owner, "") == 0)
+ {
+ /* GConfd is back. */
+ service_running = TRUE;
+
+ if (needs_reconnect)
+ {
+ needs_reconnect = FALSE;
+ reinitialize_databases ();
+ }
+
+ d(g_print ("*** Gconf Service created\n"));
+ }
+
+ if (strcmp (new_owner, "") == 0)
+ {
+ /* GConfd is gone, set the state so we can detect that we're down. */
+ service_running = FALSE;
+ needs_reconnect = TRUE;
+
+ d(g_print ("*** GConf Service deleted\n"));
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* FIXME: What should this do in the D-BUS case? */
+static void
+gconf_detach_config_server(void)
+{
+ if (engines_by_db != NULL)
+ {
+ g_hash_table_destroy (engines_by_db);
+ engines_by_db = NULL;
+ }
+}
+
+/**
+ * gconf_debug_shutdown:
+ * @void:
+ *
+ * Detach from the config server and release
+ * all related resources
+ **/
+int
+gconf_debug_shutdown (void)
+{
+ gconf_detach_config_server ();
+
+ return 0;
+}
+
+static DBusHandlerResult
+handle_notify (DBusConnection *connection,
+ DBusMessage *message,
+ GConfEngine *conf2)
+{
+ GConfEngine *conf;
+ gchar *key, *schema_name;
+ gboolean is_default, is_writable;
+ DBusMessageIter iter;
+ GConfValue *value;
+ GConfEntry* entry;
+ GList *list, *l;
+ gboolean match = FALSE;
+ gchar *namespace_section, *db;
+
+ dbus_message_iter_init (message, &iter);
+
+ dbus_message_iter_get_basic (&iter, &db);
+
+ if (!dbus_message_iter_next (&iter))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ dbus_message_iter_get_basic (&iter, &namespace_section);
+
+ if (!dbus_message_iter_next (&iter))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ conf = lookup_engine_by_database (db);
+
+ g_return_val_if_fail (conf != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ if (conf == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!gconf_dbus_utils_get_entry_values (&iter,
+ &key,
+ &value,
+ &is_default,
+ &is_writable,
+ &schema_name))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ d(g_print ("Got notify on %s (%s)\n", key, namespace_section));
+
+ list = gconf_cnxn_lookup_dir (conf, namespace_section);
+ for (l = list; l; l = l->next)
+ {
+ GConfCnxn *cnxn = l->data;
+
+ d(g_print ("match? %s\n", cnxn->namespace_section));
+
+ if (strcmp (cnxn->namespace_section, namespace_section) == 0)
+ {
+ d(g_print ("yes: %s\n", key));
+
+ entry = gconf_entry_new (key, value);
+ gconf_cnxn_notify (cnxn, entry);
+ gconf_entry_free (entry);
+
+ match = TRUE;
+ }
+ }
+
+ if (value)
+ gconf_value_free (value);
+
+ if (!match)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+
+/*
+ * Daemon control
+ */
+
+void
+gconf_shutdown_daemon (GError** err)
+{
+ DBusMessage *message;
+
+ /* Don't want to spawn it if it's already down */
+ if (global_conn == NULL || !service_running)
+ return;
+
+ message = dbus_message_new_method_call (GCONF_DBUS_SERVICE,
+ GCONF_DBUS_SERVER_OBJECT,
+ GCONF_DBUS_SERVER_INTERFACE,
+ GCONF_DBUS_SERVER_SHUTDOWN);
+
+ dbus_connection_send (global_conn, message, 0);
+ dbus_connection_flush (global_conn);
+
+ dbus_message_unref (message);
+}
+
+gboolean
+gconf_ping_daemon (void)
+{
+ if (global_conn == NULL)
+ {
+ if (!ensure_dbus_connection ())
+ {
+ return FALSE;
+ }
+ g_assert (global_conn != NULL);
+ }
+
+ if (!dbus_bus_name_has_owner(global_conn,
+ GCONF_DBUS_SERVICE,
+ NULL))
+ {
+ service_running = FALSE;
+ }
+ else
+ {
+ service_running = TRUE;
+ }
+
+ return service_running;
+}
+
+gboolean
+gconf_spawn_daemon (GError **err)
+{
+ return ensure_service (TRUE, err);
+}
+
+
diff --git a/gconf/gconf-dbus.h b/gconf/gconf-dbus.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/gconf/gconf-dbus.h
diff --git a/gconf/gconfd-dbus.c b/gconf/gconfd-dbus.c
new file mode 100644
index 00000000..cb0ecac1
--- /dev/null
+++ b/gconf/gconfd-dbus.c
@@ -0,0 +1,392 @@
+/* GConf
+ * Copyright (C) 2003 Imendio HB
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <string.h>
+#include "gconf-database-dbus.h"
+#include "gconf-dbus-utils.h"
+#include "gconfd.h"
+#include "gconfd-dbus.h"
+
+static DBusConnection *bus_conn;
+static const char *server_path = "/org/gnome/GConf/Server";
+static gint nr_of_connections = 0;
+
+static void server_unregistered_func (DBusConnection *connection,
+ void *user_data);
+static DBusHandlerResult server_message_func (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+static DBusHandlerResult server_filter_func (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+static void server_handle_get_db (DBusConnection *connection,
+ DBusMessage *message);
+static void server_handle_shutdown (DBusConnection *connection,
+ DBusMessage *message);
+static void server_handle_get_default_db (DBusConnection *connection,
+ DBusMessage *message);
+
+
+static DBusObjectPathVTable
+server_vtable = {
+ server_unregistered_func,
+ server_message_func,
+ NULL,
+};
+
+static void
+server_unregistered_func (DBusConnection *connection, void *user_data)
+{
+ g_print ("Server object unregistered\n");
+ nr_of_connections = 0;
+}
+
+static DBusHandlerResult
+server_message_func (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ if (gconfd_dbus_check_in_shutdown (connection, message))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ g_print ("Not a method call\n");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (strcmp (dbus_message_get_interface (message),
+ GCONF_DBUS_SERVER_INTERFACE) != 0)
+ {
+ g_print ("Not correct interface: \"%s\"\n",
+ dbus_message_get_interface (message));
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (dbus_message_is_method_call (message,
+ GCONF_DBUS_SERVER_INTERFACE,
+ GCONF_DBUS_SERVER_GET_DEFAULT_DB))
+ server_handle_get_default_db (connection, message);
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_SERVER_INTERFACE,
+ GCONF_DBUS_SERVER_GET_DB))
+ server_handle_get_db (connection, message);
+ else if (dbus_message_is_method_call (message,
+ GCONF_DBUS_SERVER_INTERFACE,
+ GCONF_DBUS_SERVER_SHUTDOWN))
+ server_handle_shutdown (connection, message);
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+server_filter_func (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected")) {
+ /* Exit cleanly. */
+ gconfd_main_quit ();
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+server_real_handle_get_db (DBusConnection *connection,
+ DBusMessage *message,
+ GSList *addresses)
+{
+ GConfDatabase *db;
+ DBusMessage *reply;
+ GError *gerror = NULL;
+ const gchar *str;
+
+ if (gconfd_dbus_check_in_shutdown (connection, message))
+ return;
+
+ db = gconfd_obtain_database (addresses, &gerror);
+
+ if (gconfd_dbus_set_exception (connection, message, &gerror))
+ return;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ g_error ("No memory");
+
+ str = gconf_database_dbus_get_path (db);
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send (connection, reply, NULL))
+ g_error ("No memory");
+
+ dbus_message_unref (reply);
+}
+
+static void
+server_handle_get_default_db (DBusConnection *connection,
+ DBusMessage *message)
+{
+ server_real_handle_get_db (connection, message, NULL);
+}
+
+static void
+server_handle_get_db (DBusConnection *connection, DBusMessage *message)
+{
+ char *addresses;
+ GSList *list;
+
+ if (!gconfd_dbus_get_message_args (connection, message,
+ DBUS_TYPE_STRING, &addresses,
+ DBUS_TYPE_INVALID))
+ return;
+
+ list = gconf_persistent_name_get_address_list (addresses);
+
+ server_real_handle_get_db (connection, message, list);
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+}
+
+static void
+server_handle_shutdown (DBusConnection *connection, DBusMessage *message)
+{
+ DBusMessage *reply;
+
+ if (gconfd_dbus_check_in_shutdown (connection, message))
+ return;
+
+ gconf_log(GCL_DEBUG, _("Shutdown request received"));
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+
+ dbus_connection_unregister_object_path (connection, server_path);
+
+ gconfd_main_quit();
+}
+
+gboolean
+gconfd_dbus_init (void)
+{
+ DBusError error;
+ gint ret;
+
+ dbus_error_init (&error);
+
+ bus_conn = dbus_bus_get (DBUS_BUS_SESSION, &error);
+
+ if (!bus_conn)
+ {
+ gconf_log (GCL_ERR, _("Daemon failed to connect to the D-BUS daemon:\n%s"),
+ error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ /* We handle exiting ourselves on disconnect. */
+ dbus_connection_set_exit_on_disconnect (bus_conn, FALSE);
+
+ /* Add message filter to handle Disconnected. */
+ dbus_connection_add_filter (bus_conn,
+ (DBusHandleMessageFunction) server_filter_func,
+ NULL, NULL);
+
+ ret = dbus_bus_request_name (bus_conn,
+ GCONF_DBUS_SERVICE,
+ 0,
+ &error);
+
+ if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ {
+ gconf_log (GCL_ERR, "Daemon could not become primary owner");
+ return FALSE;
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ gconf_log (GCL_ERR, _("Daemon failed to acquire gconf service:\n%s"),
+ error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ if (!dbus_connection_register_object_path (bus_conn,
+ server_path,
+ &server_vtable,
+ NULL))
+ {
+ gconf_log (GCL_ERR, _("Failed to register server object with the D-BUS bus daemon"));
+ return FALSE;
+ }
+
+
+ nr_of_connections = 1;
+ dbus_connection_setup_with_g_main (bus_conn, NULL);
+
+ return TRUE;
+}
+
+guint
+gconfd_dbus_client_count (void)
+{
+ return nr_of_connections;
+}
+
+gboolean
+gconfd_dbus_get_message_args (DBusConnection *connection,
+ DBusMessage *message,
+ int first_arg_type,
+ ...)
+{
+ gboolean retval;
+ va_list var_args;
+
+ va_start (var_args, first_arg_type);
+ retval = dbus_message_get_args_valist (message, NULL, first_arg_type, var_args);
+ va_end (var_args);
+
+ if (!retval)
+ {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_error (message, GCONF_DBUS_ERROR_FAILED,
+ _("Got a malformed message."));
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gconfd_dbus_set_exception (DBusConnection *connection,
+ DBusMessage *message,
+ GError **error)
+{
+ GConfError en;
+ const char *name = NULL;
+ DBusMessage *reply;
+
+ if (error == NULL || *error == NULL)
+ return FALSE;
+
+ en = (*error)->code;
+
+ /* success is not supposed to get set */
+ g_return_val_if_fail(en != GCONF_ERROR_SUCCESS, FALSE);
+
+ switch (en)
+ {
+ case GCONF_ERROR_FAILED:
+ name = GCONF_DBUS_ERROR_FAILED;
+ break;
+ case GCONF_ERROR_NO_PERMISSION:
+ name = GCONF_DBUS_ERROR_NO_PERMISSION;
+ break;
+ case GCONF_ERROR_BAD_ADDRESS:
+ name = GCONF_DBUS_ERROR_BAD_ADDRESS;
+ break;
+ case GCONF_ERROR_BAD_KEY:
+ name = GCONF_DBUS_ERROR_BAD_KEY;
+ break;
+ case GCONF_ERROR_PARSE_ERROR:
+ name = GCONF_DBUS_ERROR_PARSE_ERROR;
+ break;
+ case GCONF_ERROR_CORRUPT:
+ name = GCONF_DBUS_ERROR_CORRUPT;
+ break;
+ case GCONF_ERROR_TYPE_MISMATCH:
+ name = GCONF_DBUS_ERROR_TYPE_MISMATCH;
+ break;
+ case GCONF_ERROR_IS_DIR:
+ name = GCONF_DBUS_ERROR_IS_DIR;
+ break;
+ case GCONF_ERROR_IS_KEY:
+ name = GCONF_DBUS_ERROR_IS_KEY;
+ break;
+ case GCONF_ERROR_NO_WRITABLE_DATABASE:
+ name = GCONF_DBUS_ERROR_NO_WRITABLE_DATABASE;
+ break;
+ case GCONF_ERROR_IN_SHUTDOWN:
+ name = GCONF_DBUS_ERROR_IN_SHUTDOWN;
+ break;
+ case GCONF_ERROR_OVERRIDDEN:
+ name = GCONF_DBUS_ERROR_OVERRIDDEN;
+ break;
+ case GCONF_ERROR_LOCK_FAILED:
+ name = GCONF_DBUS_ERROR_LOCK_FAILED;
+ break;
+ case GCONF_ERROR_OAF_ERROR:
+ case GCONF_ERROR_LOCAL_ENGINE:
+ case GCONF_ERROR_NO_SERVER:
+ case GCONF_ERROR_SUCCESS:
+ default:
+ gconf_log (GCL_ERR, "Unhandled error code %d", en);
+ g_assert_not_reached();
+ break;
+ }
+
+ reply = dbus_message_new_error (message, name, (*error)->message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+gboolean
+gconfd_dbus_check_in_shutdown (DBusConnection *connection,
+ DBusMessage *message)
+{
+ if (gconfd_in_shutdown ())
+ {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_error (message,
+ GCONF_DBUS_ERROR_IN_SHUTDOWN,
+ _("The GConf daemon is currently shutting down."));
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+DBusConnection *
+gconfd_dbus_get_connection (void)
+{
+ return bus_conn;
+}
+
diff --git a/gconf/gconfd-dbus.h b/gconf/gconfd-dbus.h
new file mode 100644
index 00000000..40695abd
--- /dev/null
+++ b/gconf/gconfd-dbus.h
@@ -0,0 +1,42 @@
+/* GConf
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GCONF_GCONFD_DBUS_H
+#define GCONF_GCONFD_DBUS_H
+
+#include <dbus/dbus.h>
+
+gboolean gconfd_dbus_init (void);
+gboolean gconfd_dbus_check_in_shutdown (DBusConnection *connection,
+ DBusMessage *message);
+guint gconfd_dbus_client_count (void);
+
+/* Convenience function copied from andercas old code */
+gboolean gconfd_dbus_get_message_args (DBusConnection *connection,
+ DBusMessage *message,
+ int first_arg_type,
+ ...);
+gboolean gconfd_dbus_set_exception (DBusConnection *connection,
+ DBusMessage *message,
+ GError **error);
+gboolean gconfd_dbus_check_in_shutdown (DBusConnection *connection,
+ DBusMessage *message);
+DBusConnection *gconfd_dbus_get_connection (void);
+
+#endif