diff options
author | Matthias Clasen <mclasen@redhat.com> | 2008-07-11 16:34:29 +0000 |
---|---|---|
committer | Matthias Clasen <matthiasc@src.gnome.org> | 2008-07-11 16:34:29 +0000 |
commit | 7d4caf65c7292dcd3da8f97fb6c644561a8e6fb0 (patch) | |
tree | 88b67095994a64d102e19c71bb98d46e2b33f717 /defaults | |
parent | 492bedfb37600ae2296198c7a97759a9deae0ffb (diff) | |
download | gconf-7d4caf65c7292dcd3da8f97fb6c644561a8e6fb0.tar.gz |
Bug 531169 – A mechanism for setting defaults
2008-07-11 Matthias Clasen <mclasen@redhat.com>
Bug 531169 – A mechanism for setting defaults
The cache-clearing part of this patch is thanks
to Behdad Esfahbod.
* defaults/*: A DBus system bus service that can copy a
subtree of GConf values from the callers db to a system-wide
db, using PolicyKit to control access.
* configure.in: Add --enable-defaults-service to optionally
build the defaults service.
* gconf/gconf-database.[hc]: Add
gconf_database_clear_cache_for_sources.
* gconf/gconf-sources.[hc]: Add gconf_sources_clear_cache_for_sources.
* gconf/gconfd.c: Listen for changes in the system-wide
databases by the defaults service, and clear the cache.
* po/POTFILES.in: Glue
* Makefile.am: Glue
svn path=/trunk/; revision=2628
Diffstat (limited to 'defaults')
-rw-r--r-- | defaults/Makefile.am | 56 | ||||
-rw-r--r-- | defaults/gconf-defaults-main.c | 174 | ||||
-rw-r--r-- | defaults/gconf-defaults.c | 753 | ||||
-rw-r--r-- | defaults/gconf-defaults.h | 88 | ||||
-rw-r--r-- | defaults/gconf-defaults.xml | 64 | ||||
-rw-r--r-- | defaults/org.gnome.GConf.Defaults.conf | 23 | ||||
-rw-r--r-- | defaults/org.gnome.GConf.Defaults.service.in | 4 | ||||
-rw-r--r-- | defaults/org.gnome.gconf.defaults.policy.in | 28 |
8 files changed, 1190 insertions, 0 deletions
diff --git a/defaults/Makefile.am b/defaults/Makefile.am new file mode 100644 index 00000000..48aaec0c --- /dev/null +++ b/defaults/Makefile.am @@ -0,0 +1,56 @@ +libexec_PROGRAMS = gconf-defaults-mechanism + +gconf-defaults-glue.h: $(srcdir)/gconf-defaults.xml + dbus-binding-tool --prefix=gconf_defaults --mode=glib-server \ + --output=gconf-defaults-glue.h \ + $(srcdir)/gconf-defaults.xml + + +gconf_defaults_mechanism_SOURCES = \ + gconf-defaults.h \ + gconf-defaults.c \ + gconf-defaults-glue.h \ + gconf-defaults-main.c + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(DEFAULTS_CFLAGS) + +gconf_defaults_mechanism_LDADD = \ + $(top_builddir)/gconf/libgconf-2.la \ + $(DEFAULTS_LIBS) + +BUILT_SOURCES = gconf-defaults-glue.h + +dbus_servicesdir = $(datadir)/dbus-1/system-services +dbus_confdir = $(sysconfdir)/dbus-1/system.d +polkitdir = $(datadir)/PolicyKit/policy + +dbus_services_in_files = org.gnome.GConf.Defaults.service.in +polkit_in_files = org.gnome.gconf.defaults.policy.in + +dbus_services_DATA = $(dbus_services_in_files:.service.in=.service) + +$(dbus_services_DATA): $(dbus_services_in_files) + sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ + +dbus_conf_DATA = org.gnome.GConf.Defaults.conf + +@INTLTOOL_POLICY_RULE@ +polkit_DATA = $(polkit_in_files:.policy.in=.policy) + +check: + $(POLKIT_POLICY_FILE_VALIDATE) $(polkit_DATA) + +EXTRA_DIST = \ + $(dbus_services_in_files) \ + org.gnome.GConf.Defaults.conf \ + $(polkit_in_files) \ + gconf-defaults.xml + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(polkit_DATA) \ + $(dbus_services_DATA) + diff --git a/defaults/gconf-defaults-main.c b/defaults/gconf-defaults-main.c new file mode 100644 index 00000000..bd229029 --- /dev/null +++ b/defaults/gconf-defaults-main.c @@ -0,0 +1,174 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Matthias Clasen <mclasen@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <glib.h> +#include <glib-object.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + + +#include "gconf-defaults.h" + +static DBusGProxy * +get_bus_proxy (DBusGConnection *connection) +{ + DBusGProxy *bus_proxy; + + bus_proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + return bus_proxy; +} + +#define BUS_NAME "org.gnome.GConf.Defaults" + +static gboolean +acquire_name_on_proxy (DBusGProxy *bus_proxy) +{ + GError *error; + guint result; + gboolean res; + gboolean ret; + + ret = FALSE; + + if (bus_proxy == NULL) { + goto out; + } + + error = NULL; + res = dbus_g_proxy_call (bus_proxy, + "RequestName", + &error, + G_TYPE_STRING, BUS_NAME, + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", BUS_NAME, error->message); + g_error_free (error); + } else { + g_warning ("Failed to acquire %s", BUS_NAME); + } + goto out; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", BUS_NAME, error->message); + g_error_free (error); + } else { + g_warning ("Failed to acquire %s", BUS_NAME); + } + goto out; + } + + ret = TRUE; + + out: + return ret; +} + +static DBusGConnection * +get_system_bus (void) +{ + GError *error; + DBusGConnection *bus; + DBusConnection *connection; + + error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to system bus: %s", error->message); + g_error_free (error); + goto out; + } + + connection = dbus_g_connection_get_connection (bus); + out: + return bus; +} + +int +main (int argc, char **argv) +{ + GMainLoop *loop; + GConfDefaults *mechanism; + DBusGProxy *bus_proxy; + DBusGConnection *connection; + int ret; + + ret = 1; + + if (! g_thread_supported ()) { + g_thread_init (NULL); + } + dbus_g_thread_init (); + g_type_init (); + + connection = get_system_bus (); + if (connection == NULL) { + goto out; + } + + bus_proxy = get_bus_proxy (connection); + if (bus_proxy == NULL) { + g_warning ("Could not construct bus_proxy object; bailing out"); + goto out; + } + + mechanism = gconf_defaults_new (); + + if (mechanism == NULL) { + goto out; + } + + if (!acquire_name_on_proxy (bus_proxy)) { + g_warning ("Could not acquire name; bailing out"); + goto out; + } + + loop = g_main_loop_new (NULL, FALSE); + + g_main_loop_run (loop); + + g_object_unref (mechanism); + g_main_loop_unref (loop); + ret = 0; + +out: + return ret; +} diff --git a/defaults/gconf-defaults.c b/defaults/gconf-defaults.c new file mode 100644 index 00000000..295f5db0 --- /dev/null +++ b/defaults/gconf-defaults.c @@ -0,0 +1,753 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Matthias Clasen <mclasen@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/types.h> +#include <pwd.h> + +#include <glib.h> +#include <glib-object.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include <polkit-dbus/polkit-dbus.h> +#include <polkit/polkit.h> + +#define GCONF_ENABLE_INTERNALS +#include <gconf/gconf-client.h> +#include <gconf/gconf-engine.h> + +#include "gconf-defaults.h" +#include "gconf-defaults-glue.h" + +static gboolean +do_exit (gpointer user_data) +{ + g_debug ("Exiting due to inactivity"); + exit (1); + return FALSE; +} + +static guint timer_id = 0; + +static void +stop_killtimer (void) +{ + if (timer_id > 0) { + g_source_remove (timer_id); + timer_id = 0; + } +} + +static void +start_killtimer (void) +{ + g_debug ("Setting killtimer to 30 seconds..."); + timer_id = g_timeout_add_seconds (30, do_exit, NULL); +} + +struct GConfDefaultsPrivate +{ + DBusGConnection *system_bus_connection; + DBusGProxy *system_bus_proxy; + PolKitContext *pol_ctx; +}; + +static void gconf_defaults_finalize (GObject *object); + +G_DEFINE_TYPE (GConfDefaults, gconf_defaults, G_TYPE_OBJECT) + +#define GCONF_DEFAULTS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCONF_TYPE_DEFAULTS, GConfDefaultsPrivate)) + +GQuark +gconf_defaults_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) { + ret = g_quark_from_static_string ("gconf_defaults_error"); + } + + return ret; +} + + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gconf_defaults_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + ENUM_ENTRY (GCONF_DEFAULTS_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (GCONF_DEFAULTS_ERROR_NOT_PRIVILEGED, "NotPrivileged"), + { 0, 0, 0 } + }; + + g_assert (GCONF_DEFAULTS_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GConfDefaultsError", values); + } + + return etype; +} + + +static GObject * +gconf_defaults_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GConfDefaults *mechanism; + GConfDefaultsClass *klass; + + klass = GCONF_DEFAULTS_CLASS (g_type_class_peek (GCONF_TYPE_DEFAULTS)); + + mechanism = GCONF_DEFAULTS (G_OBJECT_CLASS (gconf_defaults_parent_class)->constructor ( + type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (mechanism); +} + +enum { + SYSTEM_SET, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +gconf_defaults_class_init (GConfDefaultsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = gconf_defaults_constructor; + object_class->finalize = gconf_defaults_finalize; + + signals[SYSTEM_SET] = g_signal_new ("system-set", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GConfDefaultsClass, system_set), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, G_TYPE_STRV); + + g_type_class_add_private (klass, sizeof (GConfDefaultsPrivate)); + + dbus_g_object_type_install_info (GCONF_TYPE_DEFAULTS, &dbus_glib_gconf_defaults_object_info); + + dbus_g_error_domain_register (GCONF_DEFAULTS_ERROR, NULL, GCONF_DEFAULTS_TYPE_ERROR); + +} + +static void +gconf_defaults_init (GConfDefaults *mechanism) +{ + mechanism->priv = GCONF_DEFAULTS_GET_PRIVATE (mechanism); +} + +static void +gconf_defaults_finalize (GObject *object) +{ + GConfDefaults *mechanism; + + g_return_if_fail (object != NULL); + g_return_if_fail (GCONF_IS_DEFAULTS (object)); + + mechanism = GCONF_DEFAULTS (object); + + g_return_if_fail (mechanism->priv != NULL); + + g_object_unref (mechanism->priv->system_bus_proxy); + + G_OBJECT_CLASS (gconf_defaults_parent_class)->finalize (object); +} + +static gboolean +pk_io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data) +{ + int fd; + PolKitContext *pk_context = user_data; + fd = g_io_channel_unix_get_fd (channel); + polkit_context_io_func (pk_context, fd); + return TRUE; +} + +static int +pk_io_add_watch (PolKitContext *pk_context, int fd) +{ + guint id = 0; + GIOChannel *channel; + channel = g_io_channel_unix_new (fd); + if (channel == NULL) + goto out; + id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context); + if (id == 0) { + g_io_channel_unref (channel); + goto out; + } + g_io_channel_unref (channel); +out: + return id; +} + +static void +pk_io_remove_watch (PolKitContext *pk_context, int watch_id) +{ + g_source_remove (watch_id); +} + +static gboolean +register_mechanism (GConfDefaults *mechanism) +{ + GError *error = NULL; + + mechanism->priv->pol_ctx = polkit_context_new (); + polkit_context_set_io_watch_functions (mechanism->priv->pol_ctx, pk_io_add_watch, pk_io_remove_watch); + if (!polkit_context_init (mechanism->priv->pol_ctx, NULL)) { + g_critical ("cannot initialize libpolkit"); + goto error; + } + + error = NULL; + mechanism->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (mechanism->priv->system_bus_connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + goto error; + } + + dbus_g_connection_register_g_object (mechanism->priv->system_bus_connection, "/", + G_OBJECT (mechanism)); + + mechanism->priv->system_bus_proxy = dbus_g_proxy_new_for_name (mechanism->priv->system_bus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + start_killtimer (); + + return TRUE; + +error: + return FALSE; +} + + +GConfDefaults * +gconf_defaults_new (void) +{ + GObject *object; + gboolean res; + + object = g_object_new (GCONF_TYPE_DEFAULTS, NULL); + + res = register_mechanism (GCONF_DEFAULTS (object)); + if (! res) { + g_object_unref (object); + return NULL; + } + + return GCONF_DEFAULTS (object); +} + +static const char * +polkit_action_for_gconf_path (GConfDefaults *mechanism, + const char *annotation_key, + const char *path) +{ + PolKitPolicyCache *cache; + PolKitPolicyFileEntry *entry; + char *prefix, *p; + const char *action; + + cache = polkit_context_get_policy_cache (mechanism->priv->pol_ctx); + prefix = g_strdup (path); + + while (1) { + entry = polkit_policy_cache_get_entry_by_annotation (cache, + annotation_key, + prefix); + if (entry) { + action = polkit_policy_file_entry_get_id (entry); + break; + } + + p = strrchr (prefix, '/'); + + if (p == NULL || p == prefix) { + action = NULL; + break; + } + + *p = 0; + } + + g_free (prefix); + + return action; +} + +static gboolean +check_polkit_for_action (GConfDefaults *mechanism, + DBusGMethodInvocation *context, + const char *action) +{ + const char *sender; + GError *error; + DBusError dbus_error; + PolKitCaller *pk_caller; + PolKitAction *pk_action; + PolKitResult pk_result; + + error = NULL; + + /* Check that caller is privileged */ + sender = dbus_g_method_get_sender (context); + dbus_error_init (&dbus_error); + pk_caller = polkit_caller_new_from_dbus_name ( + dbus_g_connection_get_connection (mechanism->priv->system_bus_connection), + sender, + &dbus_error); + if (pk_caller == NULL) { + error = g_error_new (GCONF_DEFAULTS_ERROR, + GCONF_DEFAULTS_ERROR_GENERAL, + "Error getting information about caller: %s: %s", + dbus_error.name, dbus_error.message); + dbus_error_free (&dbus_error); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + pk_action = polkit_action_new (); + polkit_action_set_action_id (pk_action, action); + pk_result = polkit_context_is_caller_authorized (mechanism->priv->pol_ctx, pk_action, pk_caller, TRUE, NULL); + polkit_caller_unref (pk_caller); + + if (pk_result != POLKIT_RESULT_YES) { + dbus_error_init (&dbus_error); + polkit_dbus_error_generate (pk_action, pk_result, &dbus_error); + dbus_set_g_error (&error, &dbus_error); + dbus_g_method_return_error (context, error); + dbus_error_free (&dbus_error); + g_error_free (error); + polkit_action_unref (pk_action); + return FALSE; + } + + polkit_action_unref (pk_action); + return TRUE; +} + +static char * +gconf_address_for_caller (GConfDefaults *mechanism, + DBusGMethodInvocation *context, + GError **gerror) +{ + char *sender; + DBusConnection *conn; + uid_t uid; + struct passwd *pwd; + char *result; + DBusError error; + + conn = dbus_g_connection_get_connection (mechanism->priv->system_bus_connection); + sender = dbus_g_method_get_sender (context); + + dbus_error_init (&error); + uid = dbus_bus_get_unix_user (conn, sender, &error); + g_free (sender); + if (uid == (unsigned)-1) { + dbus_set_g_error (gerror, &error); + dbus_error_free (&error); + return NULL; + } + + pwd = getpwuid (uid); + if (pwd == NULL) { + g_set_error (gerror, + 0, 0, + "Failed to get passwd information for uid %d", uid); + return NULL; + } + + result = g_strconcat ("xml:merged:", pwd->pw_dir, "/.gconf", NULL); + return result; +} + +static gboolean +path_is_excluded (const char *path, + const char **excludes) +{ + int i; + + for (i = 0; excludes && excludes[i]; i++) { + if (g_str_has_prefix (path, excludes[i])) + return TRUE; + } + + return FALSE; +} + +static void +copy_tree (GConfClient *src, + const char *path, + GConfChangeSet *changes, + const char **excludes) +{ + GSList *list, *l; + GConfEntry *entry; + + if (path_is_excluded (path, excludes)) + return; + + list = gconf_client_all_entries (src, path, NULL); + for (l = list; l; l = l->next) { + entry = l->data; + if (!path_is_excluded (entry->key, excludes)) + gconf_change_set_set (changes, entry->key, entry->value); + } + g_slist_foreach (list, (GFunc)gconf_entry_free, NULL); + g_slist_free (list); + + list = gconf_client_all_dirs (src, path, NULL); + for (l = list; l; l = l->next) + copy_tree (src, (const char *)l->data, changes, excludes); + g_slist_foreach (list, (GFunc)g_free, NULL); + g_slist_free (list); +} + +static void +copy_entry (GConfClient *src, + const char *path, + GConfChangeSet *changes, + const char **excludes) +{ + GConfValue *value; + + if (path_is_excluded (path, excludes)) + return; + + value = gconf_client_get (src, path, NULL); + if (value) { + gconf_change_set_set (changes, path, value); + gconf_value_free (value); + } +} + + +static void +do_copy (GConfDefaults *mechanism, + gboolean mandatory, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context, + GConfChangeSet **changeset_out) +{ + char *address = NULL; + GConfClient *source = NULL; + GConfClient *dest = NULL; + GConfChangeSet *changes = NULL; + GConfEngine *engine; + GError *error; + GError *error2; + const char *action; + const char *annotation_key; + const char *default_action; + const char *dest_address; + int i; + + if (changeset_out) + *changeset_out = NULL; + + stop_killtimer (); + + /* check privileges for each include */ + if (mandatory) { + annotation_key = "org.gnome.gconf.defaults.set-mandatory.prefix"; + default_action = "org.gnome.gconf.defaults.set-mandatory"; + dest_address = "xml:merged:/etc/gconf/gconf.xml.mandatory"; + } + else { + annotation_key = "org.gnome.gconf.defaults.set-system.prefix"; + default_action = "org.gnome.gconf.defaults.set-system"; + dest_address = "xml:merged:/etc/gconf/gconf.xml.system"; + } + + for (i = 0; includes[i]; i++) { + action = polkit_action_for_gconf_path (mechanism, annotation_key, includes[i]); + if (action == NULL) + action = default_action; + + if (!check_polkit_for_action (mechanism, context, action)) + goto out; + } + + error = NULL; + engine = gconf_engine_get_local (dest_address, &error); + if (error) + goto cleanup; + + dest = gconf_client_get_for_engine (engine); + gconf_engine_unref (engine); + + /* find the address to from the caller id */ + address = gconf_address_for_caller (mechanism, context, &error); + if (error) + goto cleanup; + + engine = gconf_engine_get_local (address, &error); + if (error) + goto cleanup; + + source = gconf_client_get_for_engine (engine); + gconf_engine_unref (engine); + + changes = gconf_change_set_new (); + + /* recursively copy each include, leaving out the excludes */ + for (i = 0; includes[i]; i++) { + if (gconf_client_dir_exists (source, includes[i], NULL)) + copy_tree (source, includes[i], changes, excludes); + else + copy_entry (source, includes[i], changes, excludes); + } + + gconf_client_commit_change_set (dest, changes, FALSE, &error); + gconf_client_suggest_sync (dest, NULL); + + if (changeset_out) { + *changeset_out = changes; + changes = NULL; + } + +cleanup: + g_free (address); + if (changes) + gconf_change_set_unref (changes); + if (dest) + g_object_unref (dest); + if (source) + g_object_unref (source); + + if (error) { + g_print ("failed to set GConf values: %s\n", error->message); + error2 = g_error_new (GCONF_DEFAULTS_ERROR, + GCONF_DEFAULTS_ERROR_GENERAL, + error->message); + g_error_free (error); + + dbus_g_method_return_error (context, error2); + g_error_free (error2); + } + else + dbus_g_method_return (context); + +out: + start_killtimer (); +} + +static void +append_key (GConfChangeSet *cs, + const gchar *key, + GConfValue *value, + gpointer user_data) +{ + GPtrArray *keys = (GPtrArray *) user_data; + + g_ptr_array_add (keys, (gpointer) key); +} + +void +gconf_defaults_set_system (GConfDefaults *mechanism, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context) +{ + GConfChangeSet *changes = NULL; + GPtrArray *keys; + + do_copy (mechanism, FALSE, includes, excludes, context, &changes); + + if (!changes) + return; + + keys = g_ptr_array_new (); + gconf_change_set_foreach (changes, append_key, keys); + g_ptr_array_add (keys, NULL); + + g_signal_emit (mechanism, signals[SYSTEM_SET], 0, keys->pdata); + + g_ptr_array_free (keys, TRUE); + gconf_change_set_unref (changes); +} + +void +gconf_defaults_set_mandatory (GConfDefaults *mechanism, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context) +{ + do_copy (mechanism, TRUE, includes, excludes, context, NULL); +} + +static void +unset_tree (GConfClient *dest, + const char *path, + GConfChangeSet *changes, + const char **excludes) +{ + GSList *list, *l; + GConfEntry *entry; + + if (path_is_excluded (path, excludes)) + return; + + list = gconf_client_all_entries (dest, path, NULL); + for (l = list; l; l = l->next) { + entry = l->data; + if (!path_is_excluded (entry->key, excludes)) + gconf_change_set_unset (changes, entry->key); + } + g_slist_foreach (list, (GFunc)gconf_entry_free, NULL); + g_slist_free (list); + + list = gconf_client_all_dirs (dest, path, NULL); + for (l = list; l; l = l->next) + unset_tree (dest, (const char *)l->data, changes, excludes); + g_slist_foreach (list, (GFunc)g_free, NULL); + g_slist_free (list); +} + +static void +unset_entry (GConfClient *dest, + const char *path, + GConfChangeSet *changes, + const char **excludes) +{ + if (path_is_excluded (path, excludes)) + return; + + gconf_change_set_unset (changes, path); +} + +static void +unset_in_db (GConfDefaults *mechanism, + const char *address, + const char **includes, + const char **excludes, + GError **error) +{ + GConfEngine *engine; + GConfClient *dest = NULL; + GConfChangeSet *changes = NULL; + int i; + + engine = gconf_engine_get_local (address, error); + if (*error) + goto out; + + dest = gconf_client_get_for_engine (engine); + gconf_engine_unref (engine); + + changes = gconf_change_set_new (); + + /* recursively copy each include, leaving out the excludes */ + for (i = 0; includes[i]; i++) { + if (gconf_client_dir_exists (dest, includes[i], NULL)) + unset_tree (dest, includes[i], changes, excludes); + else + unset_entry (dest, includes[i], changes, excludes); + } + + gconf_client_commit_change_set (dest, changes, TRUE, error); + gconf_client_suggest_sync (dest, NULL); + +out: + if (dest) + g_object_unref (dest); + if (changes) + gconf_change_set_unref (changes); +} + +void +gconf_defaults_unset_mandatory (GConfDefaults *mechanism, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context) +{ + const char *annotation_key; + const char *default_action; + int i; + const char *action; + GError *error; + GError *error2; + + stop_killtimer (); + + annotation_key = "org.gnome.gconf.defaults.set-mandatory.prefix"; + default_action = "org.gnome.gconf.defaults.set-mandatory"; + + for (i = 0; includes[i]; i++) { + action = polkit_action_for_gconf_path (mechanism, annotation_key, includes[i]); + if (action == NULL) + action = default_action; + + if (!check_polkit_for_action (mechanism, context, action)) + goto out; + } + + error = NULL; + unset_in_db (mechanism,"xml:merged:/etc/gconf/gconf.xml.mandatory", + includes, excludes, &error); + + if (error) { + error2 = g_error_new (GCONF_DEFAULTS_ERROR, + GCONF_DEFAULTS_ERROR_GENERAL, + error->message); + g_error_free (error); + + dbus_g_method_return_error (context, error2); + g_error_free (error2); + } + else + dbus_g_method_return (context); +out: + start_killtimer(); +} diff --git a/defaults/gconf-defaults.h b/defaults/gconf-defaults.h new file mode 100644 index 00000000..d7c4bcc1 --- /dev/null +++ b/defaults/gconf-defaults.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Matthias Clasen <mclasen@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef GCONF_DEFAULTS_H +#define GCONF_DEFAULTS_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +G_BEGIN_DECLS + +#define GCONF_TYPE_DEFAULTS (gconf_defaults_get_type ()) +#define GCONF_DEFAULTS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCONF_TYPE_DEFAULTS, GConfDefaults)) +#define GCONF_DEFAULTS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCONF_TYPE_DEFAULTS, GConfDefaultsClass)) +#define GCONF_IS_DEFAULTS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCONF_TYPE_DEFAULTS)) +#define GCONF_IS_DEFAULTS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCONF_TYPE_DEFAULTS)) +#define GCONF_DEFAULTS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCONF_TYPE_DEFAULTS, GConfDefaultsClass)) + +typedef struct GConfDefaultsPrivate GConfDefaultsPrivate; + +typedef struct +{ + GObject parent; + GConfDefaultsPrivate *priv; +} GConfDefaults; + +typedef struct +{ + GObjectClass parent_class; + + void (* system_set) (GConfDefaults *defaults, + const char **keys); + +} GConfDefaultsClass; + +typedef enum +{ + GCONF_DEFAULTS_ERROR_GENERAL, + GCONF_DEFAULTS_ERROR_NOT_PRIVILEGED, + GCONF_DEFAULTS_NUM_ERRORS +} GConfDefaultsError; + +#define GCONF_DEFAULTS_ERROR gconf_defaults_error_quark () + +GType gconf_defaults_error_get_type (void); +#define GCONF_DEFAULTS_TYPE_ERROR (gconf_defaults_error_get_type ()) + + +GQuark gconf_defaults_error_quark (void); +GType gconf_defaults_get_type (void); +GConfDefaults *gconf_defaults_new (void); + +/* exported methods */ +void gconf_defaults_set_system (GConfDefaults *mechanism, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context); + +void gconf_defaults_set_mandatory (GConfDefaults *mechanism, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context); + +void gconf_defaults_unset_mandatory (GConfDefaults *mechanism, + const char **includes, + const char **excludes, + DBusGMethodInvocation *context); + +G_END_DECLS + +#endif /* GCONF_DEFAULTS_H */ diff --git a/defaults/gconf-defaults.xml b/defaults/gconf-defaults.xml new file mode 100644 index 00000000..5dacc441 --- /dev/null +++ b/defaults/gconf-defaults.xml @@ -0,0 +1,64 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node name="/"> + <interface name="org.gnome.GConf.Defaults"> + <!-- + includes: an array of GConf paths to copy from the + callers GConf database to the system database + excludes: an array of GConf paths to omit + + Copies values from the callers GConf database to the system-wide + database. The subtree below each included path is copied recursively, + skipping the excluded subtrees. + To decide which PolicyKit privilege to require for the copying of + each path in includes, the mechanism looks for a privilege with an + annotation with key org.gnome.gconf.defaults.set-system.prefix whose + value is a prefix of the path. If no privilege is found this way, the + org.gnome.gconf.defaults.set-system privilege is required. + --> + <method name="SetSystem"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="includes" direction="in" type="as"/> + <arg name="excludes" direction="in" type="as"/> + </method> + + <signal name="SystemSet"> + <arg name="keys" type="as"/> + </signal> + <!-- + includes: an array of GConf paths to copy from the + callers GConf database to the mandatory database + excludes: an array of GConf paths to omit + + Copies values from the callers GConf database to the system-wide + mandatory database. The subtree below each included path is copied + recursively, skipping the excluded subtrees. + To decide which PolicyKit privilege to require for the copying of + each path in includes, the mechanism looks for a privilege with an + annotation with key org.gnome.gconf.defaults.set-mandatory.prefix whose + value is a prefix of the path. If no privilege is found this way, the + org.gnome.gconf.defaults.set-mandatory privilege is required. + --> + <method name="SetMandatory"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="includes" direction="in" type="as"/> + <arg name="excludes" direction="in" type="as"/> + </method> + + <!-- + Unsets keys in the system-wide mandatory GConf database, making the + keys writable again. The subtree below each included path is copied + recursively, skipping the excluded subtrees. + To decide which PolicyKit privilege to require for the copying of + each path in includes, the mechanism looks for a privilege with an + annotation with key org.gnome.gconf.defaults.set-mandatory.prefix whose + value is a prefix of the path. If no privilege is found this way, the + org.gnome.gconf.defaults.set-mandatory privilege is required. + --> + <method name="UnsetMandatory"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="includes" direction="in" type="as"/> + <arg name="excludes" direction="in" type="as"/> + </method> + + </interface> +</node> diff --git a/defaults/org.gnome.GConf.Defaults.conf b/defaults/org.gnome.GConf.Defaults.conf new file mode 100644 index 00000000..b3971ddf --- /dev/null +++ b/defaults/org.gnome.GConf.Defaults.conf @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- --> + +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- Only root can own the service --> + <policy user="root"> + <allow own="org.gnome.GConf.Defaults"/> + <allow send_interface="org.gnome.GConf.Defaults.SetSystem"/> + <allow send_interface="org.gnome.GConf.Defaults.SetMandatory"/> + <allow send_interface="org.gnome.GConf.Defaults.UnsetMandatory"/> + </policy> + + <!-- Allow anyone to invoke methods on the interfaces --> + <policy context="default"> + <allow send_interface="org.gnome.GConf.Defaults.SetSystem"/> + <allow send_interface="org.gnome.GConf.Defaults.SetMandatory"/> + <allow send_interface="org.gnome.GConf.Defaults.UnsetMandatory"/> + </policy> + +</busconfig> diff --git a/defaults/org.gnome.GConf.Defaults.service.in b/defaults/org.gnome.GConf.Defaults.service.in new file mode 100644 index 00000000..5d40384d --- /dev/null +++ b/defaults/org.gnome.GConf.Defaults.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.gnome.GConf.Defaults +Exec=@LIBEXECDIR@/gconf-defaults-mechanism +User=root diff --git a/defaults/org.gnome.gconf.defaults.policy.in b/defaults/org.gnome.gconf.defaults.policy.in new file mode 100644 index 00000000..bd75e370 --- /dev/null +++ b/defaults/org.gnome.gconf.defaults.policy.in @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> +<policyconfig> + <vendor>The GNOME Project</vendor> + <vendor_url>http://www.gnome.org/projects/gconf/</vendor_url> + <icon_name>gconf-editor</icon_name> + + <action id="org.gnome.gconf.defaults.set-system"> + <_description>Change GConf system values</_description> + <_message>Privileges are required to change GConf system values</_message> + <defaults> + <allow_inactive>no</allow_inactive> + <allow_active>auth_admin</allow_active> + </defaults> + </action> + + <action id="org.gnome.gconf.defaults.set-mandatory"> + <_description>Change GConf mandatory values</_description> + <_message>Privileges are required to change GConf mandatory values</_message> + <defaults> + <allow_inactive>no</allow_inactive> + <allow_active>auth_admin</allow_active> + </defaults> + </action> + +</policyconfig> |