diff options
Diffstat (limited to 'gconf/gconf-database-dbus.c')
-rw-r--r-- | gconf/gconf-database-dbus.c | 978 |
1 files changed, 978 insertions, 0 deletions
diff --git a/gconf/gconf-database-dbus.c b/gconf/gconf-database-dbus.c new file mode 100644 index 00000000..ce42382f --- /dev/null +++ b/gconf/gconf-database-dbus.c @@ -0,0 +1,978 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* GConf + * Copyright (C) 2003, 2004 Imendio HB + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include "gconfd.h" +#include "gconf-dbus-utils.h" +#include "gconfd-dbus.h" +#include "gconf-database-dbus.h" + +#define DATABASE_OBJECT_PATH "/org/gnome/GConf/Database" + +static gint object_nr = 0; + +typedef struct { + char *namespace_section; + GList *clients; +} NotificationData; + +typedef struct { + gchar *service; + gint nr_of_notifications; +} ListeningClientData; + +static void database_unregistered_func (DBusConnection *connection, + GConfDatabase *db); +static DBusHandlerResult database_message_func (DBusConnection *connection, + DBusMessage *message, + GConfDatabase *db); +static DBusHandlerResult database_filter_func (DBusConnection *connection, + DBusMessage *message, + GConfDatabase *db); +static DBusHandlerResult database_handle_name_owner_changed (DBusConnection *connection, + DBusMessage *message, + GConfDatabase *db); + +static void database_handle_lookup (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_lookup_ext (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_lookup_default (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_set (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_unset (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_recursive_unset (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_dir_exists (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_get_all_entries (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_get_all_dirs (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_set_schema (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_suggest_sync (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static void database_handle_add_notify (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); +static gboolean database_remove_notification_data (GConfDatabase *db, + NotificationData *notification, + const char *client); +static void database_handle_remove_notify (DBusConnection *conn, + DBusMessage *message, + GConfDatabase *db); + +static ListeningClientData *database_add_listening_client (GConfDatabase *db, + const gchar *service); +static void database_remove_listening_client (GConfDatabase *db, + ListeningClientData *client); + + +static DBusObjectPathVTable database_vtable = { + (DBusObjectPathUnregisterFunction) database_unregistered_func, + (DBusObjectPathMessageFunction) database_message_func, + NULL, +}; + +static void +database_unregistered_func (DBusConnection *connection, GConfDatabase *db) +{ +} + +static DBusHandlerResult +database_message_func (DBusConnection *connection, + DBusMessage *message, + GConfDatabase *db) +{ + if (gconfd_dbus_check_in_shutdown (connection, message)) + return DBUS_HANDLER_RESULT_HANDLED; + + if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_LOOKUP)) { + database_handle_lookup (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_LOOKUP_EXTENDED)) { + database_handle_lookup_ext (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_LOOKUP_DEFAULT)) { + database_handle_lookup_default (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_SET)) { + database_handle_set (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_UNSET)) { + database_handle_unset (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_RECURSIVE_UNSET)) { + database_handle_recursive_unset (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_DIR_EXISTS)) { + database_handle_dir_exists (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_GET_ALL_ENTRIES)) { + database_handle_get_all_entries (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_GET_ALL_DIRS)) { + database_handle_get_all_dirs (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_SET_SCHEMA)) { + database_handle_set_schema (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_SUGGEST_SYNC)) { + database_handle_suggest_sync (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_ADD_NOTIFY)) { + database_handle_add_notify (connection, message, db); + } + else if (dbus_message_is_method_call (message, + GCONF_DBUS_DATABASE_INTERFACE, + GCONF_DBUS_DATABASE_REMOVE_NOTIFY)) { + database_handle_remove_notify (connection, message, db); + } else { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +get_all_notifications_func (gpointer key, + gpointer value, + gpointer user_data) +{ + GList **list = user_data; + + *list = g_list_prepend (*list, value); +} + +static DBusHandlerResult +database_filter_func (DBusConnection *connection, + DBusMessage *message, + GConfDatabase *db) +{ +#if 0 + /* Debug output. */ + if (dbus_message_get_member (message)) { + g_print ("Message: %s\n", dbus_message_get_member (message)); + } +#endif + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) + return database_handle_name_owner_changed (connection, message, db); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult +database_handle_name_owner_changed (DBusConnection *connection, + DBusMessage *message, + GConfDatabase *db) +{ + gchar *service; + gchar *old_owner; + gchar *new_owner; + GList *notifications = NULL, *l; + NotificationData *notification; + ListeningClientData *client; + + dbus_message_get_args (message, + NULL, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID); + + if (strcmp (new_owner, "") != 0) + { + /* Service still exist, don't remove notifications */ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + g_hash_table_foreach (db->notifications, get_all_notifications_func, + ¬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); + } +} |