summaryrefslogtreecommitdiff
path: root/defaults
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2008-07-11 16:34:29 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2008-07-11 16:34:29 +0000
commit7d4caf65c7292dcd3da8f97fb6c644561a8e6fb0 (patch)
tree88b67095994a64d102e19c71bb98d46e2b33f717 /defaults
parent492bedfb37600ae2296198c7a97759a9deae0ffb (diff)
downloadgconf-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.am56
-rw-r--r--defaults/gconf-defaults-main.c174
-rw-r--r--defaults/gconf-defaults.c753
-rw-r--r--defaults/gconf-defaults.h88
-rw-r--r--defaults/gconf-defaults.xml64
-rw-r--r--defaults/org.gnome.GConf.Defaults.conf23
-rw-r--r--defaults/org.gnome.GConf.Defaults.service.in4
-rw-r--r--defaults/org.gnome.gconf.defaults.policy.in28
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>