From eed7b085e64f425bac72ec5f9f0634fdb35176d3 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Jun 2011 12:05:56 +0100 Subject: build: Conditionally compile in the DBUS implementation files --- gconf/Makefile.am | 16 + gconf/gconf-database-dbus.c | 978 +++++++++++++++++ gconf/gconf-database-dbus.h | 37 + gconf/gconf-dbus-utils.c | 1133 ++++++++++++++++++++ gconf/gconf-dbus-utils.h | 99 ++ gconf/gconf-dbus.c | 2468 +++++++++++++++++++++++++++++++++++++++++++ gconf/gconf-dbus.h | 0 gconf/gconfd-dbus.c | 392 +++++++ gconf/gconfd-dbus.h | 42 + 9 files changed, 5165 insertions(+) create mode 100644 gconf/gconf-database-dbus.c create mode 100644 gconf/gconf-database-dbus.h create mode 100644 gconf/gconf-dbus-utils.c create mode 100644 gconf/gconf-dbus-utils.h create mode 100644 gconf/gconf-dbus.c create mode 100644 gconf/gconf-dbus.h create mode 100644 gconf/gconfd-dbus.c create mode 100644 gconf/gconfd-dbus.h 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 +#include +#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, + ¬ifications); + + /* 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 +#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 +#include +#include + +#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; + * car_value; + + * int32 cdr_type; + * cdr_value; + * }; + * + */ + +/* List: + * + * struct { + * int32 list_type; + * array values; + * }; + * + */ + +/* Value: + * + * struct { + * int32 type; + * 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 +#include +#include +#include + +#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 + +#include "gconf.h" +#include "gconf-dbus-utils.h" +#include "gconf-internals.h" +#include "gconf-sources.h" +#include "gconf-locale.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 + +#include +#include +#include +#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 + +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 -- cgit v1.2.1