summaryrefslogtreecommitdiff
path: root/dbus/dbus-gobject.c
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-10-30 16:34:12 -0700
committerAnas Nashif <anas.nashif@intel.com>2012-10-30 16:34:12 -0700
commit7680502dee971654fd425d2c35e843232d91530a (patch)
tree6f677465ea9b10446acef3277e8b3f3ff604d58e /dbus/dbus-gobject.c
downloaddbus-glib-7680502dee971654fd425d2c35e843232d91530a.tar.gz
dbus-glib-7680502dee971654fd425d2c35e843232d91530a.tar.bz2
dbus-glib-7680502dee971654fd425d2c35e843232d91530a.zip
Imported Upstream version 0.100upstream/0.100
Diffstat (limited to 'dbus/dbus-gobject.c')
-rw-r--r--dbus/dbus-gobject.c3501
1 files changed, 3501 insertions, 0 deletions
diff --git a/dbus/dbus-gobject.c b/dbus/dbus-gobject.c
new file mode 100644
index 0000000..bac91be
--- /dev/null
+++ b/dbus/dbus-gobject.c
@@ -0,0 +1,3501 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gobject.c Exporting a GObject remotely
+ *
+ * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2005 Nokia
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include <gobject/gvaluecollector.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include "dbus-gtest.h"
+#include "dbus-gutils.h"
+#include "dbus-gobject.h"
+#include "dbus-gsignature.h"
+#include "dbus-gvalue.h"
+#include "dbus-gmarshal.h"
+#include "dbus-gvalue-utils.h"
+#include <string.h>
+
+#include <gio/gio.h>
+
+G_GNUC_NORETURN static void
+oom (const gchar *explanation)
+{
+ g_error ("%s", explanation == NULL ? "Out of memory" : explanation);
+ g_assert_not_reached ();
+}
+
+static DBusMessage *
+reply_or_die (DBusMessage *in_reply_to)
+{
+ DBusMessage *reply;
+
+ g_return_val_if_fail (in_reply_to != NULL, NULL);
+
+ reply = dbus_message_new_method_return (in_reply_to);
+
+ if (reply == NULL)
+ oom ("dbus_message_new_method_return failed: out of memory?");
+
+ return reply;
+}
+
+static DBusMessage *
+error_or_die (DBusMessage *in_reply_to,
+ const gchar *error_name,
+ const gchar *error_message)
+{
+ DBusMessage *reply;
+
+ g_return_val_if_fail (in_reply_to != NULL, NULL);
+ /* error names are syntactically the same as interface names */
+ g_return_val_if_fail (g_dbus_is_interface_name (error_name), NULL);
+ g_return_val_if_fail (g_utf8_validate (error_message, -1, NULL), NULL);
+
+ reply = dbus_message_new_error (in_reply_to, error_name, error_message);
+
+ if (reply == NULL)
+ oom ("dbus_message_new_error failed: out of memory?");
+
+ return reply;
+}
+
+static void
+connection_send_or_die (DBusConnection *connection,
+ DBusMessage *message)
+{
+ g_return_if_fail (connection != NULL);
+ g_return_if_fail (message != NULL);
+
+ if (!dbus_connection_send (connection, message, NULL))
+ oom ("dbus_connection_send failed: out of memory?");
+}
+
+static char *lookup_property_name (GObject *object,
+ const char *wincaps_propiface,
+ const char *requested_propname);
+
+typedef struct
+{
+ char *default_iface;
+ GType code_enum;
+} DBusGErrorInfo;
+
+static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT;
+/* See comments in check_property_access */
+static gboolean disable_legacy_property_access = FALSE;
+static GHashTable *marshal_table = NULL;
+static GData *error_metadata = NULL;
+
+static char*
+uscore_to_wincaps_full (const char *uscore,
+ gboolean uppercase_first,
+ gboolean strip_underscores)
+{
+ const char *p;
+ GString *str;
+ gboolean last_was_uscore;
+
+ last_was_uscore = uppercase_first;
+
+ str = g_string_new (NULL);
+ p = uscore;
+ while (p && *p)
+ {
+ if (*p == '-' || (strip_underscores && *p == '_'))
+ {
+ last_was_uscore = TRUE;
+ }
+ else
+ {
+ if (last_was_uscore)
+ {
+ g_string_append_c (str, g_ascii_toupper (*p));
+ last_was_uscore = FALSE;
+ }
+ else
+ g_string_append_c (str, *p);
+ }
+ ++p;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+/* Ugly yes - but we have to accept strings from both formats */
+static gboolean
+compare_strings_ignoring_uscore_vs_dash (const char *a, const char *b)
+{
+ guint i;
+
+ for (i = 0; a[i] && b[i]; i++)
+ {
+ if ((a[i] == '-' && b[i] == '_')
+ || (a[i] == '_' && b[i] == '-'))
+ continue;
+ if (a[i] != b[i])
+ return FALSE;
+ }
+ return (a[i] == '\0') && (b[i] == '\0');
+}
+
+static char *
+uscore_to_wincaps (const char *uscore)
+{
+ return uscore_to_wincaps_full (uscore, TRUE, TRUE);
+}
+
+static const char *
+string_table_next (const char *table)
+{
+ return (table + (strlen (table) + 1));
+}
+
+static const char *
+string_table_lookup (const char *table, int index)
+{
+ const char *ret;
+
+ ret = table;
+
+ while (index--)
+ ret = string_table_next (ret);
+
+ return ret;
+}
+
+static const char *
+get_method_data (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method)
+{
+ return object->data + method->data_offset;
+}
+
+static char *
+object_error_domain_prefix_from_object_info (const DBusGObjectInfo *info)
+{
+ /* FIXME */
+ return NULL;
+}
+
+static char *
+object_error_code_from_object_info (const DBusGObjectInfo *info, GQuark domain, gint code)
+{
+ /* FIXME */
+ return NULL;
+}
+
+static const char *
+method_interface_from_object_info (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method)
+{
+ return string_table_lookup (get_method_data (object, method), 0);
+}
+
+static const char *
+method_name_from_object_info (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method)
+{
+ return string_table_lookup (get_method_data (object, method), 1);
+}
+
+static const char *
+method_arg_info_from_object_info (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method)
+{
+ return string_table_lookup (get_method_data (object, method), 3);/*RB was 2*/
+}
+
+typedef enum
+{
+ RETVAL_NONE,
+ RETVAL_NOERROR,
+ RETVAL_ERROR
+} RetvalType;
+
+/*
+ * arg_iterate:
+ * @data: a pointer to the beginning of an argument entry in a string table
+ * @name: (out) (allow-none): used to return the name of the next argument
+ * @in: (out) (allow-none): used to return %TRUE for an "in" argument or
+ * %FALSE for an "out" argument
+ * @constval: (out) (allow-none): used to return %TRUE if the argument is
+ * an "out" argument and has the "C" (const) flag indicating that it
+ * should not be freed after it is returned; normally, "out" arguments
+ * are freed
+ * @retval: (out) (allow-none): used to return %RETVAL_NONE if this
+ * D-Bus argument is not an "out" argument or is obtained like a C "out"
+ * parameter, %RETVAL_ERROR if this argument is obtained from the C
+ * return value and is also used to signal errors, or %RETVAL_NOERROR
+ * if this argument is obtained from the C return value and the method
+ * can never raise an error
+ * @type: (out) (allow-none): used to return the D-Bus signature of this
+ * argument
+ *
+ * The data format is:
+ *
+ * argument name
+ * \0
+ * direction: I or O
+ * \0
+ * if direction == "O":
+ * freeable? F or C
+ * \0
+ * retval? N, E or R
+ * \0
+ * signature
+ * \0
+ *
+ * If none of the arguments has @retval != %RETVAL_NONE, the method is
+ * assumed to return a gboolean, which behaves like %RETVAL_ERROR but is
+ * not sent over D-Bus at all.
+ *
+ * Returns: the value of @data to use for the next call, or a pointer to '\0'
+ * if this function must not be called again
+ */
+static const char *
+arg_iterate (const char *data,
+ const char **name,
+ gboolean *in,
+ gboolean *constval,
+ RetvalType *retval,
+ const char **type)
+{
+ gboolean inarg;
+
+ if (name)
+ *name = data;
+
+ data = string_table_next (data);
+ switch (*data)
+ {
+ case 'I':
+ inarg = TRUE;
+ break;
+ case 'O':
+ inarg = FALSE;
+ break;
+ default:
+ g_warning ("invalid arg direction '%c'", *data);
+ inarg = FALSE;
+ break;
+ }
+ if (in)
+ *in = inarg;
+
+ if (!inarg)
+ {
+ data = string_table_next (data);
+ switch (*data)
+ {
+ case 'F':
+ if (constval)
+ *constval = FALSE;
+ break;
+ case 'C':
+ if (constval)
+ *constval = TRUE;
+ break;
+ default:
+ g_warning ("invalid arg const value '%c'", *data);
+ break;
+ }
+ data = string_table_next (data);
+ switch (*data)
+ {
+ case 'N':
+ if (retval)
+ *retval = RETVAL_NONE;
+ break;
+ case 'E':
+ if (retval)
+ *retval = RETVAL_ERROR;
+ break;
+ case 'R':
+ if (retval)
+ *retval = RETVAL_NOERROR;
+ break;
+ default:
+ g_warning ("invalid arg ret value '%c'", *data);
+ break;
+ }
+ }
+ else
+ {
+ if (constval)
+ *constval = FALSE;
+ if (retval)
+ *retval = FALSE;
+ }
+
+ data = string_table_next (data);
+ if (type)
+ *type = data;
+
+ return string_table_next (data);
+}
+
+static char *
+method_dir_signature_from_object_info (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method,
+ gboolean in)
+{
+ const char *arg;
+ GString *ret;
+
+ arg = method_arg_info_from_object_info (object, method);
+
+ ret = g_string_new (NULL);
+
+ while (*arg)
+ {
+ const char *name;
+ gboolean arg_in;
+ const char *type;
+
+ arg = arg_iterate (arg, &name, &arg_in, NULL, NULL, &type);
+
+ if (arg_in == in)
+ g_string_append (ret, type);
+ }
+
+ return g_string_free (ret, FALSE);
+}
+
+static char *
+method_input_signature_from_object_info (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method)
+{
+ return method_dir_signature_from_object_info (object, method, TRUE);
+}
+
+static char *
+method_output_signature_from_object_info (const DBusGObjectInfo *object,
+ const DBusGMethodInfo *method)
+{
+ return method_dir_signature_from_object_info (object, method, FALSE);
+}
+
+static const char *
+signal_iterate (const char *data, const char **iface, const char **name)
+{
+ *iface = data;
+
+ data = string_table_next (data);
+ *name = data;
+
+ return string_table_next (data);
+}
+
+static const char *
+property_iterate (const char *data,
+ int format_version,
+ const char **iface,
+ const char **exported_name,
+ const char **name_uscored,
+ const char **access_type)
+{
+ *iface = data;
+
+ data = string_table_next (data);
+ *exported_name = data;
+
+ data = string_table_next (data);
+ if (format_version == 1)
+ {
+ *name_uscored = data;
+ data = string_table_next (data);
+ *access_type = data;
+ return string_table_next (data);
+ }
+ else
+ {
+ /* This tells the caller they need to compute it */
+ *name_uscored = NULL;
+ /* We don't know here, however note that we will still check against the
+ * readable/writable flags from GObject's metadata.
+ */
+ *access_type = "readwrite";
+ return data;
+ }
+}
+
+/**
+ * property_info_from_object_info:
+ * @object: introspection data
+ * @interface_name: (allow-none): Expected interface name, or %NULL for any
+ * @property_name: Expected property name (can use "-" or "_" as separator)
+ * @access_type: (out): Can be one of "read", "write", "readwrite"
+ *
+ * Look up property introspection data for the given interface/name pair.
+ *
+ * Returns: %TRUE if property was found
+ */
+static gboolean
+property_info_from_object_info (const DBusGObjectInfo *object,
+ const char *interface_name,
+ const char *property_name,
+ const char **access_type)
+{
+ const char *properties_iter;
+
+ properties_iter = object->exported_properties;
+ while (properties_iter != NULL && *properties_iter)
+ {
+ const char *cur_interface_name;
+ const char *cur_property_name;
+ const char *cur_uscore_property_name;
+ const char *cur_access_type;
+
+
+ properties_iter = property_iterate (properties_iter, object->format_version,
+ &cur_interface_name, &cur_property_name,
+ &cur_uscore_property_name, &cur_access_type);
+
+ if (interface_name && strcmp (interface_name, cur_interface_name) != 0)
+ continue;
+
+ /* This big pile of ugly is necessary to support the matrix resulting from multiplying
+ * (v0 data, v1 data) * (FooBar, foo-bar)
+ * In v1 data we have both forms of string, so we do a comparison against both without
+ * having to malloc.
+ * For v0 data, we need to reconstruct the foo-bar form.
+ *
+ * Adding to the complexity is that we *also* have to ignore the distinction between
+ * '-' and '_', because g_object_{get,set} does.
+ */
+ /* First, compare against the primary property name - no malloc required */
+ if (!compare_strings_ignoring_uscore_vs_dash (property_name, cur_property_name))
+ {
+ if (cur_uscore_property_name != NULL
+ && !compare_strings_ignoring_uscore_vs_dash (property_name, cur_uscore_property_name))
+ continue;
+ else
+ {
+ /* v0 metadata, construct uscore */
+ char *tmp_uscored;
+ gboolean matches;
+ tmp_uscored = _dbus_gutils_wincaps_to_uscore (cur_property_name);
+ matches = compare_strings_ignoring_uscore_vs_dash (property_name, tmp_uscored);
+ g_free (tmp_uscored);
+ if (!matches)
+ continue;
+ }
+ }
+
+ *access_type = cur_access_type;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static GQuark
+dbus_g_object_type_dbus_metadata_quark (void)
+{
+ static GQuark quark;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("DBusGObjectTypeDBusMetadataQuark");
+ return quark;
+}
+
+/* Iterator function should return FALSE to stop iteration, TRUE to continue */
+typedef gboolean (*ForeachObjectInfoFn) (const DBusGObjectInfo *info,
+ GType gtype,
+ gpointer user_data);
+
+static void
+foreach_object_info (GObject *object,
+ ForeachObjectInfoFn callback,
+ gpointer user_data)
+{
+ GType *interfaces, *p;
+ const DBusGObjectInfo *info;
+ GType classtype;
+
+ interfaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (object), NULL);
+
+ for (p = interfaces; *p != 0; p++)
+ {
+ info = g_type_get_qdata (*p, dbus_g_object_type_dbus_metadata_quark ());
+ if (info != NULL && info->format_version >= 0)
+ {
+ if (!callback (info, *p, user_data))
+ break;
+ }
+ }
+
+ g_free (interfaces);
+
+ for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
+ {
+ info = g_type_get_qdata (classtype, dbus_g_object_type_dbus_metadata_quark ());
+ if (info != NULL && info->format_version >= 0)
+ {
+ if (!callback (info, classtype, user_data))
+ break;
+ }
+ }
+
+}
+
+static gboolean
+lookup_object_info_cb (const DBusGObjectInfo *info,
+ GType gtype,
+ gpointer user_data)
+{
+ GList **list = (GList **) user_data;
+
+ *list = g_list_prepend (*list, (gpointer) info);
+ return TRUE;
+}
+
+static GList *
+lookup_object_info (GObject *object)
+{
+ GList *info_list = NULL;
+
+ foreach_object_info (object, lookup_object_info_cb, &info_list);
+
+ return info_list;
+}
+
+typedef struct {
+ const char *iface;
+ const DBusGObjectInfo *info;
+ gboolean fallback;
+ GType iface_type;
+} LookupObjectInfoByIfaceData;
+
+static gboolean
+lookup_object_info_by_iface_cb (const DBusGObjectInfo *info,
+ GType gtype,
+ gpointer user_data)
+{
+ LookupObjectInfoByIfaceData *lookup_data = (LookupObjectInfoByIfaceData *) user_data;
+
+ /* If interface is not specified, choose the first info */
+ if (lookup_data->fallback && (!lookup_data->iface || strlen (lookup_data->iface) == 0))
+ {
+ lookup_data->info = info;
+ lookup_data->iface_type = gtype;
+ }
+ else if (info->exported_properties && !strcmp (info->exported_properties, lookup_data->iface))
+ {
+ lookup_data->info = info;
+ lookup_data->iface_type = gtype;
+ }
+
+ return !lookup_data->info;
+}
+
+static const DBusGObjectInfo *
+lookup_object_info_by_iface (GObject *object,
+ const char *iface,
+ gboolean fallback,
+ GType *out_iface_type)
+{
+ LookupObjectInfoByIfaceData data;
+
+ data.iface = iface;
+ data.info = NULL;
+ data.fallback = fallback;
+ data.iface_type = 0;
+
+ foreach_object_info (object, lookup_object_info_by_iface_cb, &data);
+
+ if (out_iface_type && data.info)
+ *out_iface_type = data.iface_type;
+
+ return data.info;
+}
+
+typedef struct {
+ /* owned */
+ GSList *registrations;
+ /* weak ref, or NULL if the object has been disposed */
+ GObject *object;
+} ObjectExport;
+
+typedef struct {
+ /* pseudo-weak ref, never NULL */
+ DBusGConnection *connection;
+ /* owned, never NULL */
+ gchar *object_path;
+ /* borrowed pointer to parent, never NULL */
+ ObjectExport *export;
+} ObjectRegistration;
+
+static void object_export_object_died (gpointer user_data, GObject *dead);
+
+static void
+object_export_unregister_all (ObjectExport *oe)
+{
+ while (oe->registrations != NULL)
+ {
+ GSList *old = oe->registrations;
+ ObjectRegistration *o = oe->registrations->data;
+
+ dbus_connection_unregister_object_path (
+ DBUS_CONNECTION_FROM_G_CONNECTION (o->connection), o->object_path);
+
+ /* the link should have been removed by doing that */
+ g_assert (oe->registrations != old);
+ }
+}
+
+static void
+object_export_free (ObjectExport *oe)
+{
+ g_slice_free (ObjectExport, oe);
+}
+
+static ObjectExport *
+object_export_new (void)
+{
+ return g_slice_new0 (ObjectExport);
+}
+
+static ObjectRegistration *
+object_registration_new (DBusGConnection *connection,
+ const gchar *object_path,
+ ObjectExport *export)
+{
+ ObjectRegistration *o = g_slice_new0 (ObjectRegistration);
+
+ o->connection = connection;
+ o->object_path = g_strdup (object_path);
+ o->export = export;
+
+ return o;
+}
+
+static void
+object_registration_free (ObjectRegistration *o)
+{
+ g_assert (o->export != NULL);
+ o->export->registrations = g_slist_remove (o->export->registrations, o);
+
+ g_free (o->object_path);
+
+ g_slice_free (ObjectRegistration, o);
+}
+
+/* Called when the object falls off the bus (e.g. because connection just
+ * closed) */
+static void
+object_registration_unregistered (DBusConnection *connection,
+ void *user_data)
+{
+ object_registration_free (user_data);
+}
+
+typedef struct
+{
+ GObject *object;
+ GString *xml;
+ GType gtype;
+ const DBusGObjectInfo *object_info;
+} DBusGLibWriteIterfaceData;
+
+typedef struct
+{
+ GSList *methods;
+ GSList *signals;
+ GSList *properties;
+} DBusGLibWriteInterfaceValues;
+
+static void
+write_interface (gpointer key, gpointer val, gpointer user_data)
+{
+ const char *name;
+ GSList *methods;
+ GSList *signals;
+ GSList *properties;
+ GString *xml;
+ const DBusGObjectInfo *object_info;
+ DBusGLibWriteIterfaceData *data;
+ DBusGLibWriteInterfaceValues *values;
+
+ name = key;
+
+ values = val;
+ methods = values->methods;
+ signals = values->signals;
+ properties = values->properties;
+
+ data = user_data;
+ xml = data->xml;
+ object_info = data->object_info;
+
+ g_string_append_printf (xml, " <interface name=\"%s\">\n", name);
+
+ /* FIXME: recurse to parent types ? */
+ for (; methods; methods = methods->next)
+ {
+ DBusGMethodInfo *method;
+ const char *args;
+ method = methods->data;
+
+ g_string_append_printf (xml, " <method name=\"%s\">\n",
+ method_name_from_object_info (object_info, method));
+
+ args = method_arg_info_from_object_info (object_info, method);
+
+ while (*args)
+ {
+ const char *name;
+ gboolean arg_in;
+ const char *type;
+
+ args = arg_iterate (args, &name, &arg_in, NULL, NULL, &type);
+
+ /* FIXME - handle container types */
+ g_string_append_printf (xml, " <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
+ name, type, arg_in ? "in" : "out");
+
+ }
+ g_string_append (xml, " </method>\n");
+
+ }
+ g_slist_free (values->methods);
+
+ for (; signals; signals = signals->next)
+ {
+ guint id;
+ guint arg;
+ const char *signame;
+ GSignalQuery query;
+ char *s;
+
+ signame = signals->data;
+
+ s = _dbus_gutils_wincaps_to_uscore (signame);
+
+ id = g_signal_lookup (s, data->gtype);
+ g_assert (id != 0);
+
+ g_signal_query (id, &query);
+ g_assert (query.return_type == G_TYPE_NONE);
+
+ g_string_append_printf (xml, " <signal name=\"%s\">\n", signame);
+
+ for (arg = 0; arg < query.n_params; arg++)
+ {
+ char *dbus_type = _dbus_gtype_to_signature (query.param_types[arg]);
+
+ g_assert (dbus_type != NULL);
+
+ g_string_append (xml, " <arg type=\"");
+ g_string_append (xml, dbus_type);
+ g_string_append (xml, "\"/>\n");
+ g_free (dbus_type);
+ }
+
+ g_string_append (xml, " </signal>\n");
+ g_free (s);
+ }
+ g_slist_free (values->signals);
+
+ for (; properties; properties = properties->next)
+ {
+ const char *iface;
+ const char *propname;
+ const char *propname_uscore;
+ const char *access_type;
+ GParamSpec *spec;
+ char *dbus_type;
+ gboolean can_set;
+ gboolean can_get;
+ char *s;
+
+ spec = NULL;
+
+ property_iterate (properties->data, object_info->format_version, &iface, &propname, &propname_uscore, &access_type);
+
+ s = lookup_property_name (data->object, name, propname);
+
+ spec = g_object_class_find_property (g_type_class_peek (data->gtype), s);
+ g_assert (spec != NULL);
+ g_free (s);
+
+ dbus_type = _dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
+ g_assert (dbus_type != NULL);
+
+ can_set = strcmp (access_type, "readwrite") == 0
+ && ((spec->flags & G_PARAM_WRITABLE) != 0
+ && (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
+
+ can_get = (spec->flags & G_PARAM_READABLE) != 0;
+
+ if (can_set || can_get)
+ {
+ g_string_append_printf (xml, " <property name=\"%s\" ", propname);
+ g_string_append (xml, "type=\"");
+ g_string_append (xml, dbus_type);
+ g_string_append (xml, "\" access=\"");
+
+ if (can_set && can_get)
+ g_string_append (xml, "readwrite");
+ else if (can_get)
+ g_string_append (xml, "read");
+ else
+ {
+ g_assert (can_set);
+ g_string_append (xml, "write");
+ }
+
+ g_string_append (xml, "\"/>\n");
+ }
+
+ g_free (dbus_type);
+ }
+ g_slist_free (values->properties);
+
+ g_free (values);
+ g_string_append (xml, " </interface>\n");
+}
+
+static DBusGLibWriteInterfaceValues *
+lookup_values (GHashTable *interfaces, const char *method_interface)
+{
+ DBusGLibWriteInterfaceValues *values;
+ if ((values = g_hash_table_lookup (interfaces, (gpointer) method_interface)) == NULL)
+ {
+ values = g_new0 (DBusGLibWriteInterfaceValues, 1);
+ g_hash_table_insert (interfaces, (gpointer) method_interface, values);
+ }
+ return values;
+}
+
+static void
+introspect_interfaces (GObject *object, GString *xml)
+{
+ GList *info_list;
+ const GList *info_list_walk;
+ const DBusGObjectInfo *info;
+ DBusGLibWriteIterfaceData data;
+ int i;
+ GHashTable *interfaces;
+ DBusGLibWriteInterfaceValues *values;
+ const char *propsig;
+
+ info_list = lookup_object_info (object);
+
+ g_assert (info_list != NULL);
+
+ /* Gather a list of all interfaces, indexed into their methods */
+ for (info_list_walk = info_list; info_list_walk != NULL; info_list_walk = g_list_next (info_list_walk))
+ {
+ info = (DBusGObjectInfo *) info_list_walk->data;
+ interfaces = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_assert (info != NULL);
+
+ for (i = 0; i < info->n_method_infos; i++)
+ {
+ const char *method_interface;
+ const DBusGMethodInfo *method;
+
+ method = &(info->method_infos[i]);
+
+ method_interface = method_interface_from_object_info (info, method);
+
+ values = lookup_values (interfaces, method_interface);
+ values->methods = g_slist_prepend (values->methods, (gpointer) method);
+ }
+
+ propsig = info->exported_signals;
+ while (propsig != NULL && *propsig)
+ {
+ const char *iface;
+ const char *signame;
+
+ propsig = signal_iterate (propsig, &iface, &signame);
+
+ values = lookup_values (interfaces, iface);
+ values->signals = g_slist_prepend (values->signals, (gpointer) signame);
+ }
+
+ propsig = info->exported_properties;
+ while (propsig != NULL && *propsig)
+ {
+ const char *iface;
+ const char *propname;
+ const char *propname_uscore;
+ const char *access_type;
+
+ propsig = property_iterate (propsig, info->format_version, &iface, &propname, &propname_uscore, &access_type);
+
+ values = lookup_values (interfaces, iface);
+ values->properties = g_slist_prepend (values->properties, (gpointer)iface);
+ }
+
+ memset (&data, 0, sizeof (data));
+ data.xml = xml;
+ data.gtype = G_TYPE_FROM_INSTANCE (object);
+ data.object_info = info;
+ data.object = object;
+
+ g_hash_table_foreach (interfaces, write_interface, &data);
+ g_hash_table_destroy (interfaces);
+ }
+
+ g_list_free (info_list);
+}
+
+static DBusHandlerResult
+handle_introspect (DBusConnection *connection,
+ DBusMessage *message,
+ GObject *object)
+{
+ GString *xml;
+ unsigned int i;
+ DBusMessage *ret;
+ char **children;
+
+ if (!dbus_connection_list_registered (connection,
+ dbus_message_get_path (message),
+ &children))
+ oom (NULL);
+
+ xml = g_string_new (NULL);
+
+ g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+ g_string_append (xml, "<node>\n");
+
+ /* We are introspectable, though I guess that was pretty obvious */
+ g_string_append_printf (xml, " <interface name=\"%s\">\n", DBUS_INTERFACE_INTROSPECTABLE);
+ g_string_append (xml, " <method name=\"Introspect\">\n");
+ g_string_append_printf (xml, " <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
+ g_string_append (xml, " </method>\n");
+ g_string_append (xml, " </interface>\n");
+
+ /* We support get/set/getall properties */
+ g_string_append_printf (xml, " <interface name=\"%s\">\n", DBUS_INTERFACE_PROPERTIES);
+ g_string_append (xml, " <method name=\"Get\">\n");
+ g_string_append_printf (xml, " <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
+ g_string_append_printf (xml, " <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
+ g_string_append_printf (xml, " <arg name=\"value\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_VARIANT_AS_STRING);
+ g_string_append (xml, " </method>\n");
+ g_string_append (xml, " <method name=\"Set\">\n");
+ g_string_append_printf (xml, " <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
+ g_string_append_printf (xml, " <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
+ g_string_append_printf (xml, " <arg name=\"value\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_VARIANT_AS_STRING);
+ g_string_append (xml, " </method>\n");
+ g_string_append (xml, " <method name=\"GetAll\">\n");
+ g_string_append_printf (xml, " <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
+ g_string_append_printf (xml, " <arg name=\"props\" direction=\"out\" type=\"%s\"/>\n",
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ );
+
+ g_string_append (xml, " </method>\n");
+ g_string_append (xml, " </interface>\n");
+
+ introspect_interfaces (object, xml);
+
+ /* Append child nodes */
+ for (i = 0; children[i]; i++)
+ g_string_append_printf (xml, " <node name=\"%s\"/>\n",
+ children[i]);
+
+ /* Close the XML, and send it to the requesting app */
+ g_string_append (xml, "</node>\n");
+
+ ret = reply_or_die (message);
+
+ dbus_message_append_args (ret,
+ DBUS_TYPE_STRING, &xml->str,
+ DBUS_TYPE_INVALID);
+
+ connection_send_or_die (connection, ret);
+ dbus_message_unref (ret);
+
+ g_string_free (xml, TRUE);
+
+ dbus_free_string_array (children);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusMessage*
+set_object_property (DBusConnection *connection,
+ DBusMessage *message,
+ DBusMessageIter *iter,
+ GObject *object,
+ GParamSpec *pspec)
+{
+ GValue value = { 0, };
+ DBusMessage *ret;
+ DBusMessageIter sub;
+ DBusGValueMarshalCtx context;
+
+ dbus_message_iter_recurse (iter, &sub);
+
+ context.recursion_depth = 0;
+ context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
+ context.proxy = NULL;
+
+ g_value_init (&value, pspec->value_type);
+ if (_dbus_gvalue_demarshal (&context, &sub, &value, NULL))
+ {
+ g_object_set_property (object,
+ pspec->name,
+ &value);
+
+ g_value_unset (&value);
+
+ ret = reply_or_die (message);
+ }
+ else
+ {
+ ret = error_or_die (message,
+ DBUS_ERROR_INVALID_ARGS,
+ "Argument's D-BUS type can't be converted to a GType");
+ }
+
+ return ret;
+}
+
+/*
+ * @pspec: the paramspec for a D-Bus-exported property
+ *
+ * Returns: a reply for the Get() D-Bus method, either successful or error
+ */
+static DBusMessage*
+get_object_property (DBusConnection *connection,
+ DBusMessage *message,
+ GObject *object,
+ GParamSpec *pspec)
+{
+ GType value_gtype;
+ GValue value = {0, };
+ gchar *variant_sig;
+ DBusMessage *ret;
+ DBusMessageIter iter, subiter;
+ gchar *error_message = NULL;
+
+ ret = reply_or_die (message);
+
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (object, pspec->name, &value);
+
+ variant_sig = _dbus_gvalue_to_signature (&value);
+ if (variant_sig == NULL)
+ {
+ value_gtype = G_VALUE_TYPE (&value);
+ error_message = g_strdup_printf (
+ "Internal error: cannot marshal type \"%s\" in variant",
+ g_type_name (value_gtype));
+ goto out;
+ }
+
+ dbus_message_iter_init_append (ret, &iter);
+ if (!dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_VARIANT,
+ variant_sig,
+ &subiter))
+ {
+ error_message = g_strdup_printf (
+ "Internal error: cannot open variant container for signature %s",
+ variant_sig);
+ goto out;
+ }
+
+ if (!_dbus_gvalue_marshal (&subiter, &value))
+ {
+ dbus_message_iter_abandon_container (&iter, &subiter);
+ error_message = g_strdup_printf (
+ "Internal error: could not marshal type \"%s\" in variant",
+ G_VALUE_TYPE_NAME (&value));
+ goto out;
+ }
+
+ dbus_message_iter_close_container (&iter, &subiter);
+
+out:
+ g_value_unset (&value);
+ g_free (variant_sig);
+
+ if (error_message != NULL)
+ {
+ dbus_message_unref (ret);
+ ret = error_or_die (message, DBUS_ERROR_FAILED, error_message);
+ g_critical ("%s", error_message);
+ g_free (error_message);
+ }
+
+ return ret;
+}
+
+#define SHADOW_PROP_QUARK (dbus_g_object_type_dbus_shadow_property_quark ())
+
+static GQuark
+dbus_g_object_type_dbus_shadow_property_quark (void)
+{
+ static GQuark quark;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("DBusGObjectTypeDBusShadowPropertyQuark");
+ return quark;
+}
+
+/* Look for shadow properties on the given interface first, otherwise
+ * just return the original property name. This allows implementations to
+ * get around the glib limitation of unique property names among all
+ * GInterfaces by registering a "shadow" property name that the get/set
+ * request will be redirected to.
+ *
+ * Shadow property data is stored as qdata on each GInterface. If there
+ * is no interface info, or there is no registered shadow property, just
+ * return the original property name.
+ */
+static char *
+lookup_property_name (GObject *object,
+ const char *wincaps_propiface,
+ const char *requested_propname)
+{
+ const DBusGObjectInfo *object_info;
+ GHashTable *shadow_props;
+ char *shadow_prop_name = NULL, *uscore_name;
+ GType iface_type = 0;
+
+ g_assert (wincaps_propiface != NULL);
+ g_assert (requested_propname != NULL);
+
+ uscore_name = _dbus_gutils_wincaps_to_uscore (requested_propname);
+
+ object_info = lookup_object_info_by_iface (object, wincaps_propiface, FALSE, &iface_type);
+ if (!object_info)
+ return uscore_name;
+
+ shadow_props = (GHashTable *) g_type_get_qdata (iface_type, SHADOW_PROP_QUARK);
+ if (shadow_props)
+ {
+ shadow_prop_name = g_strdup (g_hash_table_lookup (shadow_props, requested_propname));
+ if (shadow_prop_name)
+ g_free (uscore_name);
+ }
+
+ return shadow_prop_name ? shadow_prop_name : uscore_name;
+}
+
+/**
+ * dbus_g_object_type_register_shadow_property:
+ * @iface_type: #GType for the #GInterface
+ * @dbus_prop_name: D-Bus property name (as specified in the introspection data)
+ * to override with the shadow property name (as specified in the GType's
+ * initialization function, ie glib-style)
+ * @shadow_prop_name: property name which should override the shadow property
+ *
+ * Registers a new property name @shadow_prop_name that overrides the
+ * @dbus_prop_name in D-Bus property get/set requests. Since all properties for
+ * all interfaces implemented by a GObject exist in the same namespace, this
+ * allows implementations to use the same property name in two or more D-Bus
+ * interfaces implemented by the same GObject, as long as one of those D-Bus
+ * interface properties is registered with a shadow property name.
+ *
+ * For example, if both org.foobar.Baz.InterfaceA and org.foobar.Baz.InterfaceB
+ * have a D-Bus property called "Bork", the developer assigns a shadow property
+ * name to the conflicting property name in one or both of these GInterfaces to
+ * resolve the conflict. Assume the GInterface implementing
+ * org.foobar.Baz.InterfaceA registers a shadow property called "a-bork", while
+ * the GInterface implementing org.foobar.Baz.InterfaceB registers a shadow
+ * property called "b-bork". The GObject implementing both these GInterfaces
+ * would then use #g_object_class_override_property() to implement both
+ * "a-bork" and "b-bork" and D-Bus requests for "Bork" on either D-Bus interface
+ * will not conflict.
+ */
+void
+dbus_g_object_type_register_shadow_property (GType iface_type,
+ const char *dbus_prop_name,
+ const char *shadow_prop_name)
+{
+ GHashTable *shadow_props;
+
+ g_return_if_fail (G_TYPE_IS_CLASSED (iface_type) || G_TYPE_IS_INTERFACE (iface_type));
+ g_return_if_fail (dbus_prop_name != NULL);
+ g_return_if_fail (shadow_prop_name != NULL);
+
+ shadow_props = (GHashTable *) g_type_get_qdata (iface_type, SHADOW_PROP_QUARK);
+ if (!shadow_props)
+ {
+ shadow_props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ g_type_set_qdata (iface_type,
+ dbus_g_object_type_dbus_shadow_property_quark (),
+ shadow_props);
+ }
+
+ g_assert (shadow_props);
+ g_hash_table_insert (shadow_props, g_strdup (dbus_prop_name), g_strdup (shadow_prop_name));
+}
+
+static DBusMessage*
+get_all_object_properties (DBusConnection *connection,
+ DBusMessage *message,
+ const DBusGObjectInfo *object_info,
+ const char *wincaps_propiface,
+ GObject *object)
+{
+ DBusMessage *ret;
+ DBusMessageIter iter_ret;
+ DBusMessageIter iter_dict;
+ DBusMessageIter iter_dict_entry;
+ DBusMessageIter iter_dict_value;
+ const char *p;
+ char *uscore_propname;
+
+ ret = reply_or_die (message);
+
+ dbus_message_iter_init_append (ret, &iter_ret);
+
+ /* the types are all hard-coded, so this can only fail via OOM */
+ if (!dbus_message_iter_open_container (&iter_ret,
+ DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &iter_dict))
+ oom (NULL);
+
+ p = object_info->exported_properties;
+ while (p != NULL && *p != '\0')
+ {
+ const char *prop_ifname;
+ const char *prop_name;
+ const char *prop_uscored;
+ const char *access_flags;
+ GParamSpec *pspec;
+ GType value_gtype;
+ GValue value = {0, };
+ gchar *variant_sig;
+
+ p = property_iterate (p, object_info->format_version, &prop_ifname, &prop_name, &prop_uscored, &access_flags);
+
+ /* Conventionally, property names are valid member names, but dbus-glib
+ * doesn't enforce this, and some dbus-glib services use GObject-style
+ * property names (e.g. "foo-bar"). */
+ if (!g_utf8_validate (prop_name, -1, NULL))
+ {
+ g_critical ("property name isn't UTF-8: %s", prop_name);
+ continue;
+ }
+
+ uscore_propname = lookup_property_name (object, wincaps_propiface, prop_name);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), uscore_propname);
+ if (pspec == NULL)
+ {
+ g_warning ("introspection data references non-existing property %s", uscore_propname);
+ g_free (uscore_propname);
+ continue;
+ }
+
+ g_free (uscore_propname);
+
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (object, pspec->name, &value);
+
+ variant_sig = _dbus_gvalue_to_signature (&value);
+ if (variant_sig == NULL)
+ {
+ value_gtype = G_VALUE_TYPE (&value);
+ g_warning ("Cannot marshal type \"%s\" in variant", g_type_name (value_gtype));
+ g_value_unset (&value);
+ continue;
+ }
+
+ /* a signature returned by _dbus_gvalue_to_signature had better be
+ * valid */
+ g_assert (g_variant_is_signature (variant_sig));
+
+ /* type is hard-coded, so this can't fail except by OOM */
+ if (!dbus_message_iter_open_container (&iter_dict,
+ DBUS_TYPE_DICT_ENTRY,
+ NULL,
+ &iter_dict_entry))
+ oom (NULL);
+
+ /* prop_name is valid UTF-8, so this can't fail except by OOM; no point
+ * in abandoning @iter_dict_entry since we're about to crash out */
+ if (!dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name))
+ oom (NULL);
+
+ /* variant_sig has been asserted to be valid, so this can't fail
+ * except by OOM */
+ if (!dbus_message_iter_open_container (&iter_dict_entry,
+ DBUS_TYPE_VARIANT,
+ variant_sig,
+ &iter_dict_value))
+ oom (NULL);
+
+ g_free (variant_sig);
+
+ /* this can fail via programming error: the GObject property was
+ * malformed (non-UTF8 string or something) */
+ if (!_dbus_gvalue_marshal (&iter_dict_value, &value))
+ {
+ gchar *contents = g_strdup_value_contents (&value);
+ gchar *error_message = g_strdup_printf (
+ "cannot GetAll(%s): failed to serialize %s value of type %s: %s",
+ wincaps_propiface, prop_name, G_VALUE_TYPE_NAME (&value),
+ contents);
+
+ g_critical ("%s", error_message);
+
+ /* abandon ship! */
+ dbus_message_iter_abandon_container (&iter_dict_entry,
+ &iter_dict_value);
+ dbus_message_iter_abandon_container (&iter_dict, &iter_dict_entry);
+ dbus_message_unref (ret);
+ ret = error_or_die (message, DBUS_ERROR_FAILED, error_message);
+
+ g_free (contents);
+ g_free (error_message);
+ g_value_unset (&value);
+ return ret;
+ }
+
+ /* these shouldn't fail except by OOM now that we were successful */
+ if (!dbus_message_iter_close_container (&iter_dict_entry,
+ &iter_dict_value))
+ oom (NULL);
+ if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
+ oom (NULL);
+
+ g_value_unset (&value);
+ }
+
+ if (!dbus_message_iter_close_container (&iter_ret, &iter_dict))
+ oom (NULL);
+
+ return ret;
+}
+
+static gboolean
+lookup_object_and_method (GObject *object,
+ DBusMessage *message,
+ const DBusGObjectInfo **object_ret,
+ const DBusGMethodInfo **method_ret)
+{
+ const char *interface;
+ const char *member;
+ const char *signature;
+ GList *info_list;
+ const GList *info_list_walk;
+ const DBusGObjectInfo *info;
+ int i;
+
+ interface = dbus_message_get_interface (message);
+ member = dbus_message_get_member (message);
+ signature = dbus_message_get_signature (message);
+
+ info_list = lookup_object_info (object);
+
+ for (info_list_walk = info_list; info_list_walk != NULL; info_list_walk = g_list_next (info_list_walk))
+ {
+ info = (DBusGObjectInfo *) info_list_walk->data;
+ *object_ret = info;
+
+ for (i = 0; i < info->n_method_infos; i++)
+ {
+ const char *expected_member;
+ const char *expected_interface;
+ char *expected_signature;
+ const DBusGMethodInfo *method;
+
+ method = &(info->method_infos[i]);
+
+ /* Check method interface/name and input signature */
+ expected_interface = method_interface_from_object_info (*object_ret, method);
+ expected_member = method_name_from_object_info (*object_ret, method);
+ expected_signature = method_input_signature_from_object_info (*object_ret, method);
+
+ if ((interface == NULL
+ || strcmp (expected_interface, interface) == 0)
+ && strcmp (expected_member, member) == 0
+ && strcmp (expected_signature, signature) == 0)
+ {
+ g_free (expected_signature);
+ *method_ret = method;
+ g_list_free (info_list);
+ return TRUE;
+ }
+ g_free (expected_signature);
+ }
+ }
+
+ if (info_list)
+ g_list_free (info_list);
+
+ return FALSE;
+}
+
+static char *
+gerror_domaincode_to_dbus_error_name (const DBusGObjectInfo *object_info,
+ const char *msg_interface,
+ GQuark domain, gint code)
+{
+ const char *domain_str;
+ const char *code_str;
+ GString *dbus_error_name;
+
+ domain_str = object_error_domain_prefix_from_object_info (object_info);
+ code_str = object_error_code_from_object_info (object_info, domain, code);
+
+ if (!domain_str || !code_str)
+ {
+ DBusGErrorInfo *info;
+
+ g_static_rw_lock_reader_lock (&globals_lock);
+
+ if (error_metadata != NULL)
+ info = g_datalist_id_get_data (&error_metadata, domain);
+ else
+ info = NULL;
+
+ g_static_rw_lock_reader_unlock (&globals_lock);
+
+ if (info)
+ {
+ GEnumValue *value;
+ GEnumClass *klass;
+
+ klass = g_type_class_ref (info->code_enum);
+ value = g_enum_get_value (klass, code);
+ g_type_class_unref (klass);
+
+ domain_str = info->default_iface;
+ if (value)
+ {
+ code_str = value->value_nick;
+ }
+ else
+ {
+ g_warning ("Error code %d out of range for GError domain %s",
+ code, g_quark_to_string (domain));
+ code_str = NULL;
+ }
+ }
+ }
+
+ if (!domain_str)
+ domain_str = msg_interface;
+
+ if (!domain_str || !code_str)
+ {
+ const char *domain_string;
+ /* If we can't map it sensibly, make up an error name */
+
+ dbus_error_name = g_string_new ("org.freedesktop.DBus.GLib.UnmappedError.");
+
+ domain_string = g_quark_to_string (domain);
+ if (domain_string != NULL)
+ {
+ char *uscored = uscore_to_wincaps (domain_string);
+ g_string_append (dbus_error_name, uscored);
+ g_string_append_c (dbus_error_name, '.');
+ g_free (uscored);
+ }
+
+ /* Map -1 to (unsigned) -1 to avoid "-", which is not valid */
+ g_string_append_printf (dbus_error_name, "Code%u", (unsigned) code);
+ }
+ else
+ {
+ gchar *code_str_wincaps;
+ dbus_error_name = g_string_new (domain_str);
+ g_string_append_c (dbus_error_name, '.');
+ /* We can't uppercase here for backwards compatibility
+ * reasons; if someone had a lowercase enumeration value,
+ * previously we'd just send it across unaltered.
+ */
+ code_str_wincaps = uscore_to_wincaps_full (code_str, FALSE, FALSE);
+ g_string_append (dbus_error_name, code_str_wincaps);
+ g_free (code_str_wincaps);
+ }
+
+ return g_string_free (dbus_error_name, FALSE);
+}
+
+static DBusMessage *
+gerror_to_dbus_error_message (const DBusGObjectInfo *object_info,
+ DBusMessage *message,
+ const GError *error)
+{
+ DBusMessage *reply;
+
+ if (!error)
+ {
+ char *error_msg;
+
+ error_msg = g_strdup_printf ("Method invoked for %s returned FALSE but did not set error", dbus_message_get_member (message));
+ reply = error_or_die (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg);
+ g_free (error_msg);
+ }
+ else
+ {
+ if (error->domain == DBUS_GERROR)
+ {
+ const gchar *name = DBUS_ERROR_FAILED;
+
+ switch (error->code)
+ {
+ case DBUS_GERROR_FAILED:
+ name = DBUS_ERROR_FAILED;
+ break;
+ case DBUS_GERROR_NO_MEMORY:
+ name = DBUS_ERROR_NO_MEMORY;
+ break;
+ case DBUS_GERROR_SERVICE_UNKNOWN:
+ name = DBUS_ERROR_SERVICE_UNKNOWN;
+ break;
+ case DBUS_GERROR_NAME_HAS_NO_OWNER:
+ name = DBUS_ERROR_NAME_HAS_NO_OWNER;
+ break;
+ case DBUS_GERROR_NO_REPLY:
+ name = DBUS_ERROR_NO_REPLY;
+ break;
+ case DBUS_GERROR_IO_ERROR:
+ name = DBUS_ERROR_IO_ERROR;
+ break;
+ case DBUS_GERROR_BAD_ADDRESS:
+ name = DBUS_ERROR_BAD_ADDRESS;
+ break;
+ case DBUS_GERROR_NOT_SUPPORTED:
+ name = DBUS_ERROR_NOT_SUPPORTED;
+ break;
+ case DBUS_GERROR_LIMITS_EXCEEDED:
+ name = DBUS_ERROR_LIMITS_EXCEEDED;
+ break;
+ case DBUS_GERROR_ACCESS_DENIED:
+ name = DBUS_ERROR_ACCESS_DENIED;
+ break;
+ case DBUS_GERROR_AUTH_FAILED:
+ name = DBUS_ERROR_AUTH_FAILED;
+ break;
+ case DBUS_GERROR_NO_SERVER:
+ name = DBUS_ERROR_NO_SERVER;
+ break;
+ case DBUS_GERROR_TIMEOUT:
+ name = DBUS_ERROR_TIMEOUT;
+ break;
+ case DBUS_GERROR_NO_NETWORK:
+ name = DBUS_ERROR_NO_NETWORK;
+ break;
+ case DBUS_GERROR_ADDRESS_IN_USE:
+ name = DBUS_ERROR_ADDRESS_IN_USE;
+ break;
+ case DBUS_GERROR_DISCONNECTED:
+ name = DBUS_ERROR_DISCONNECTED;
+ break;
+ case DBUS_GERROR_INVALID_ARGS:
+ name = DBUS_ERROR_INVALID_ARGS;
+ break;
+ case DBUS_GERROR_FILE_NOT_FOUND:
+ name = DBUS_ERROR_FILE_NOT_FOUND;
+ break;
+ case DBUS_GERROR_REMOTE_EXCEPTION:
+ name = dbus_g_error_get_name ((GError*) error);
+ break;
+ }
+
+ reply = error_or_die (message, name, error->message);
+ }
+ else
+ {
+ char *error_name;
+ error_name = gerror_domaincode_to_dbus_error_name (object_info,
+ dbus_message_get_interface (message),
+ error->domain, error->code);
+ reply = error_or_die (message, error_name, error->message);
+ g_free (error_name);
+ }
+ }
+
+ return reply;
+}
+
+/**
+ * SECTION:dbus-gmethod
+ * @short_description: GMethod Info & Invocation
+ * @see_also: #DBusGMessage
+ * @stability: Stable
+ *
+ * These types are used to call methods on #GObject objects.
+ */
+
+/**
+ * DBusGMethodInvocation:
+ *
+ * The context of an asynchronous method call. See dbus_g_method_return() and
+ * dbus_g_method_return_error().
+ */
+struct _DBusGMethodInvocation {
+ DBusGConnection *connection; /**< The connection */
+ DBusGMessage *message; /**< The message which generated the method call */
+ const DBusGObjectInfo *object; /**< The object the method was called on */
+ const DBusGMethodInfo *method; /**< The method called */
+ gboolean send_reply;
+};
+
+static DBusHandlerResult
+invoke_object_method (GObject *object,
+ const DBusGObjectInfo *object_info,
+ const DBusGMethodInfo *method,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ gboolean had_error, is_async, send_reply;
+ GError *gerror;
+ GValueArray *value_array;
+ GValue return_value = {0,};
+ GClosure closure;
+ char *in_signature;
+ GArray *out_param_values = NULL;
+ GValueArray *out_param_gvalues = NULL;
+ int out_param_count;
+ int out_param_pos, out_param_gvalue_pos;
+ DBusMessage *reply = NULL;
+ gboolean have_retval;
+ gboolean retval_signals_error;
+ gboolean retval_is_synthetic;
+ gboolean retval_is_constant;
+ const char *arg_metadata;
+
+ gerror = NULL;
+
+ /* This flag says whether invokee is handed a special DBusGMethodInvocation structure,
+ * instead of being required to fill out all return values in the context of the function.
+ * Some additional data is also exposed, such as the message sender.
+ */
+ is_async = strcmp (string_table_lookup (get_method_data (object_info, method), 2), "A") == 0;
+
+ /* Messages can be sent with a flag that says "I don't need a reply". This is an optimization
+ * normally, but in the context of the system bus it's important to not send a reply
+ * to these kinds of messages, because they will be unrequested replies, and thus subject
+ * to denial and logging. We don't want to fill up logs.
+ * http://bugs.freedesktop.org/show_bug.cgi?id=19441
+ */
+ send_reply = !dbus_message_get_no_reply (message);
+
+ have_retval = FALSE;
+ retval_signals_error = FALSE;
+ retval_is_synthetic = FALSE;
+ retval_is_constant = FALSE;
+
+ /* This is evil. We do this to work around the fact that
+ * the generated glib marshallers check a flag in the closure object
+ * which we don't care about. We don't need/want to create
+ * a new closure for each invocation.
+ */
+ memset (&closure, 0, sizeof (closure));
+
+ in_signature = method_input_signature_from_object_info (object_info, method);
+
+ /* Convert method IN parameters to GValueArray */
+ {
+ GArray *types_array;
+ guint n_params;
+ const GType *types;
+ DBusGValueMarshalCtx context;
+ GError *error = NULL;
+
+ context.recursion_depth = 0;
+ context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
+ context.proxy = NULL;
+
+ types_array = _dbus_gtypes_from_arg_signature (in_signature, FALSE);
+ n_params = types_array->len;
+ types = (const GType*) types_array->data;
+
+ value_array = _dbus_gvalue_demarshal_message (&context, message, n_params, types, &error);
+ if (value_array == NULL)
+ {
+ g_free (in_signature);
+ g_array_free (types_array, TRUE);
+ reply = error_or_die (message, "org.freedesktop.DBus.GLib.ErrorError", error->message);
+ connection_send_or_die (connection, reply);
+ dbus_message_unref (reply);
+ g_error_free (error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ g_array_free (types_array, TRUE);
+ }
+
+ /* Prepend object as first argument */
+ g_value_array_prepend (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 0), G_TYPE_OBJECT);
+ g_value_set_object (g_value_array_get_nth (value_array, 0), object);
+
+ if (is_async)
+ {
+ GValue context_value = {0,};
+ DBusGMethodInvocation *context;
+ context = g_new (DBusGMethodInvocation, 1);
+ context->connection = dbus_g_connection_ref (DBUS_G_CONNECTION_FROM_CONNECTION (connection));
+ context->message = dbus_g_message_ref (DBUS_G_MESSAGE_FROM_MESSAGE (message));
+ context->object = object_info;
+ context->method = method;
+ context->send_reply = send_reply;
+ g_value_init (&context_value, G_TYPE_POINTER);
+ g_value_set_pointer (&context_value, context);
+ g_value_array_append (value_array, &context_value);
+ }
+ else
+ {
+ RetvalType retval;
+ gboolean arg_in;
+ gboolean arg_const;
+ const char *argsig;
+
+ arg_metadata = method_arg_info_from_object_info (object_info, method);
+
+ /* Count number of output parameters, and look for a return value */
+ out_param_count = 0;
+ while (*arg_metadata)
+ {
+ arg_metadata = arg_iterate (arg_metadata, NULL, &arg_in, &arg_const, &retval, &argsig);
+ if (arg_in)
+ continue;
+ if (retval != RETVAL_NONE)
+ {
+ DBusSignatureIter tmp_sigiter;
+ /* This is the function return value */
+ g_assert (!have_retval);
+ have_retval = TRUE;
+ retval_is_synthetic = FALSE;
+
+ switch (retval)
+ {
+ case RETVAL_NONE:
+ g_assert_not_reached ();
+ break;
+ case RETVAL_NOERROR:
+ retval_signals_error = FALSE;
+ break;
+ case RETVAL_ERROR:
+ retval_signals_error = TRUE;
+ break;
+ }
+
+ retval_is_constant = arg_const;
+
+ /* Initialize our return GValue with the specified type */
+ dbus_signature_iter_init (&tmp_sigiter, argsig);
+ g_value_init (&return_value, _dbus_gtype_from_signature_iter (&tmp_sigiter, FALSE));
+ }
+ else
+ {
+ /* It's a regular output value */
+ out_param_count++;
+ }
+ }
+
+ /* For compatibility, if we haven't found a return value, we assume
+ * the function returns a gboolean for signalling an error
+ * (and therefore also takes a GError). We also note that it
+ * is a "synthetic" return value; i.e. we aren't going to be
+ * sending it over the bus, it's just to signal an error.
+ */
+ if (!have_retval)
+ {
+ have_retval = TRUE;
+ retval_is_synthetic = TRUE;
+ retval_signals_error = TRUE;
+ g_value_init (&return_value, G_TYPE_BOOLEAN);
+ }
+
+ /* Create an array to store the actual values of OUT parameters
+ * (other than the real function return, if any). Then, create
+ * a GValue boxed POINTER to each of those values, and append to
+ * the invocation, so the method can return the OUT parameters.
+ */
+ out_param_values = g_array_sized_new (FALSE, TRUE, sizeof (GTypeCValue), out_param_count);
+
+ /* We have a special array of GValues for toplevel GValue return
+ * types.
+ */
+ out_param_gvalues = g_value_array_new (out_param_count);
+ out_param_pos = 0;
+ out_param_gvalue_pos = 0;
+
+ /* Reset argument metadata pointer */
+ arg_metadata = method_arg_info_from_object_info (object_info, method);
+
+ /* Iterate over output arguments again, this time allocating space for
+ * them as appopriate.
+ */
+ while (*arg_metadata)
+ {
+ GValue value = {0, };
+ GTypeCValue storage;
+ DBusSignatureIter tmp_sigiter;
+ GType current_gtype;
+
+ arg_metadata = arg_iterate (arg_metadata, NULL, &arg_in, NULL, &retval, &argsig);
+ /* Skip over input arguments and the return value, if any */
+ if (arg_in || retval != RETVAL_NONE)
+ continue;
+
+ dbus_signature_iter_init (&tmp_sigiter, argsig);
+ current_gtype = _dbus_gtype_from_signature_iter (&tmp_sigiter, FALSE);
+
+ g_value_init (&value, G_TYPE_POINTER);
+
+ /* We special case variants to make method invocation a bit nicer */
+ if (current_gtype != G_TYPE_VALUE)
+ {
+ memset (&storage, 0, sizeof (storage));
+ g_array_append_val (out_param_values, storage);
+ g_value_set_pointer (&value, &(g_array_index (out_param_values, GTypeCValue, out_param_pos)));
+ out_param_pos++;
+ }
+ else
+ {
+ g_value_array_append (out_param_gvalues, NULL);
+ g_value_set_pointer (&value, out_param_gvalues->values + out_param_gvalue_pos);
+ out_param_gvalue_pos++;
+ }
+ g_value_array_append (value_array, &value);
+ }
+ }
+
+ /* Append GError as final argument if necessary */
+ if (retval_signals_error)
+ {
+ g_assert (have_retval);
+ g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, value_array->n_values - 1), G_TYPE_POINTER);
+ g_value_set_pointer (g_value_array_get_nth (value_array, value_array->n_values - 1), &gerror);
+ }
+
+ /* Actually invoke method */
+ method->marshaller (&closure, have_retval ? &return_value : NULL,
+ value_array->n_values,
+ value_array->values,
+ NULL, method->function);
+ if (is_async)
+ {
+ goto done;
+ }
+
+ if (retval_signals_error)
+ had_error = _dbus_gvalue_signals_error (&return_value);
+ else
+ had_error = FALSE;
+
+ if (!had_error)
+ {
+ DBusMessageIter iter;
+
+ /* Careful here - there are two major cases in this section of the code.
+ * If send_reply is TRUE, we're constructing a dbus message and freeing
+ * the return values. If it's FALSE, then we just need to free the
+ * values.
+ */
+ if (send_reply)
+ {
+ reply = reply_or_die (message);
+
+ /* Append output arguments to reply */
+ dbus_message_iter_init_append (reply, &iter);
+ }
+
+ /* First, append the return value, unless it's synthetic */
+ if (have_retval && !retval_is_synthetic)
+ {
+ if (reply != NULL && !_dbus_gvalue_marshal (&iter, &return_value))
+ {
+ gchar *desc = g_strdup_value_contents (&return_value);
+
+ g_critical ("unable to append retval of type %s for %s: %s",
+ G_VALUE_TYPE_NAME (&return_value),
+ method_name_from_object_info (object_info, method),
+ desc);
+ g_free (desc);
+ /* the reply is now unusable but we still need to free
+ * everything */
+ dbus_message_unref (reply);
+ reply = NULL;
+ }
+
+ if (!retval_is_constant)
+ g_value_unset (&return_value);
+ }
+
+ /* Grab the argument metadata and iterate over it */
+ arg_metadata = method_arg_info_from_object_info (object_info, method);
+
+ /* Now append any remaining return values */
+ out_param_pos = 0;
+ out_param_gvalue_pos = 0;
+ while (*arg_metadata)
+ {
+ GValue gvalue = {0, };
+ const char *arg_name;
+ gboolean arg_in;
+ gboolean constval;
+ RetvalType retval;
+ const char *arg_signature;
+ DBusSignatureIter argsigiter;
+
+ do
+ {
+ /* Iterate over only output values; skip over input
+ arguments and the return value */
+ arg_metadata = arg_iterate (arg_metadata, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ }
+ while ((arg_in || retval != RETVAL_NONE) && *arg_metadata);
+
+ /* If the last argument we saw was input or the return
+ * value, we must be done iterating over output arguments.
+ */
+ if (arg_in || retval != RETVAL_NONE)
+ break;
+
+ dbus_signature_iter_init (&argsigiter, arg_signature);
+
+ g_value_init (&gvalue, _dbus_gtype_from_signature_iter (&argsigiter, FALSE));
+ if (G_VALUE_TYPE (&gvalue) != G_TYPE_VALUE)
+ {
+ if (!_dbus_gvalue_take (&gvalue,
+ &(g_array_index (out_param_values, GTypeCValue, out_param_pos))))
+ g_assert_not_reached ();
+ out_param_pos++;
+ }
+ else
+ {
+ g_value_set_static_boxed (&gvalue, out_param_gvalues->values + out_param_gvalue_pos);
+ out_param_gvalue_pos++;
+ }
+
+ if (reply && !_dbus_gvalue_marshal (&iter, &gvalue))
+ {
+ gchar *desc = g_strdup_value_contents (&gvalue);
+
+ g_critical ("unable to append OUT arg of type %s for %s: %s",
+ G_VALUE_TYPE_NAME (&gvalue),
+ method_name_from_object_info (object_info, method),
+ desc);
+ g_free (desc);
+ /* the reply is now unusable but we still need to free
+ * everything */
+ dbus_message_unref (reply);
+ reply = NULL;
+ }
+
+ /* Here we actually free the allocated value; we
+ * took ownership of it with _dbus_gvalue_take, unless
+ * an annotation has specified this value as constant.
+ */
+ if (!constval)
+ g_value_unset (&gvalue);
+ }
+ }
+ else if (send_reply)
+ reply = gerror_to_dbus_error_message (object_info, message, gerror);
+
+ if (reply)
+ {
+ connection_send_or_die (connection, reply);
+ dbus_message_unref (reply);
+ }
+
+done:
+ g_free (in_signature);
+
+ if (!is_async)
+ {
+ g_array_free (out_param_values, TRUE);
+ g_value_array_free (out_param_gvalues);
+ }
+
+ if (gerror != NULL)
+ g_clear_error (&gerror);
+
+ g_value_array_free (value_array);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/*
+ * @wincaps_propiface: the D-Bus interface name, conventionally WindowsCaps
+ * @requested_propname: the D-Bus property name, conventionally WindowsCaps
+ * @uscore_propname: the GObject property name, conventionally
+ * words_with_underscores or words-with-dashes
+ * @is_set: %TRUE if we're going to set the property, %FALSE if we're going
+ * to get it
+ *
+ * Check that the requested property exists and the requested access is
+ * allowed. If not, reply with a D-Bus AccessDenied error message.
+ *
+ * Returns: %TRUE if property access can continue, or %FALSE if an error
+ * reply has been sent
+ */
+static gboolean
+check_property_access (DBusConnection *connection,
+ DBusMessage *message,
+ GObject *object,
+ const char *wincaps_propiface,
+ const char *requested_propname,
+ const char *uscore_propname,
+ gboolean is_set)
+{
+ const DBusGObjectInfo *object_info;
+ const char *access_type;
+ DBusMessage *ret;
+ gchar *error_message;
+
+ if (!is_set && !disable_legacy_property_access)
+ return TRUE;
+
+ object_info = lookup_object_info_by_iface (object, wincaps_propiface, TRUE, NULL);
+ if (!object_info)
+ {
+ error_message = g_strdup_printf (
+ "Interface \"%s\" isn't exported (or may not exist), can't access property \"%s\"",
+ wincaps_propiface, requested_propname);
+
+ goto error;
+ }
+
+ /* Try both forms of property names: "foo_bar" or "FooBar"; for historical
+ * reasons we accept both.
+ */
+ if (object_info
+ && !(property_info_from_object_info (object_info, wincaps_propiface, requested_propname, &access_type)
+ || property_info_from_object_info (object_info, wincaps_propiface, uscore_propname, &access_type)))
+ {
+ error_message = g_strdup_printf (
+ "Property \"%s\" of interface \"%s\" isn't exported (or may not exist)",
+ requested_propname, wincaps_propiface);
+
+ goto error;
+ }
+
+ if (strcmp (access_type, "readwrite") == 0)
+ return TRUE;
+
+ if (is_set ? strcmp (access_type, "read") == 0
+ : strcmp (access_type, "write") == 0)
+ {
+ error_message = g_strdup_printf (
+ "Property \"%s\" of interface \"%s\" is not %s",
+ requested_propname,
+ wincaps_propiface,
+ is_set ? "settable" : "readable");
+
+ goto error;
+ }
+
+ return TRUE;
+
+error:
+ ret = error_or_die (message, DBUS_ERROR_ACCESS_DENIED, error_message);
+ g_free (error_message);
+
+ connection_send_or_die (connection, ret);
+ dbus_message_unref (ret);
+ return FALSE;
+}
+
+static DBusHandlerResult
+object_registration_message (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GParamSpec *pspec;
+ GObject *object;
+ gboolean setter;
+ gboolean getter;
+ gboolean getall;
+ char *s;
+ const char *requested_propname;
+ const char *wincaps_propiface;
+ DBusMessageIter iter;
+ const DBusGMethodInfo *method;
+ const DBusGObjectInfo *object_info;
+ DBusMessage *ret;
+ ObjectRegistration *o;
+
+ o = user_data;
+ /* export is always non-NULL. If the object has been disposed, the weak-ref
+ * callback removes all registrations from the DBusConnection, so this
+ * should never be reached with object = NULL. */
+ object = G_OBJECT (o->export->object);
+ g_assert (object != NULL);
+
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ return handle_introspect (connection, message, object);
+
+ /* Try the metainfo, which lets us invoke methods */
+ object_info = NULL;
+ if (lookup_object_and_method (object, message, &object_info, &method))
+ return invoke_object_method (object, object_info, method, connection, message);
+
+ /* If no metainfo, we can still do properties and signals
+ * via standard GLib introspection. Note we do now check
+ * property access against the metainfo if available.
+ */
+ getter = FALSE;
+ setter = FALSE;
+ getall = FALSE;
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_PROPERTIES,
+ "Get"))
+ getter = TRUE;
+ else if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_PROPERTIES,
+ "Set"))
+ setter = TRUE;
+ else if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_PROPERTIES,
+ "GetAll"))
+ getall = TRUE;
+
+ if (!(setter || getter || getall))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ ret = NULL;
+
+ dbus_message_iter_init (message, &iter);
+
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ {
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
+ "First argument to Get(), GetAll() or Set() must be an interface string");
+ goto out;
+ }
+
+ dbus_message_iter_get_basic (&iter, &wincaps_propiface);
+ dbus_message_iter_next (&iter);
+
+ if (getall)
+ {
+ object_info = lookup_object_info_by_iface (object, wincaps_propiface, TRUE, NULL);
+ if (object_info != NULL)
+ ret = get_all_object_properties (connection, message, object_info, wincaps_propiface, object);
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else
+ {
+ g_assert (getter || setter);
+
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ {
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
+ "Second argument to Get() or Set() must be a property name string");
+ goto out;
+ }
+
+ dbus_message_iter_get_basic (&iter, &requested_propname);
+ dbus_message_iter_next (&iter);
+
+ s = lookup_property_name (object, wincaps_propiface, requested_propname);
+
+ if (!check_property_access (connection, message, object, wincaps_propiface, requested_propname, s, setter))
+ {
+ g_free (s);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
+ s);
+
+ g_free (s);
+
+ if (pspec != NULL)
+ {
+ if (setter)
+ {
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
+ {
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
+ "Third argument to Set() must be a variant");
+ goto out;
+ }
+
+ ret = set_object_property (connection, message, &iter,
+ object, pspec);
+ dbus_message_iter_next (&iter);
+ }
+ else
+ {
+ g_assert (getter);
+ ret = get_object_property (connection, message,
+ object, pspec);
+ }
+ }
+ else
+ {
+ gchar *error_message = g_strdup_printf ("No such property %s",
+ requested_propname);
+
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS, error_message);
+ g_free (error_message);
+ }
+ }
+
+ g_assert (ret != NULL);
+
+ /* FIXME: this should be returned as a D-Bus error, not spammed out
+ * as a warning. This is too late to do that, though - we've already
+ * had any side-effects we were going to have - and it would break
+ * anything that's relying on ability to give us too many arguments. */
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
+ g_warning ("Property get, set or set all had too many arguments\n");
+
+out:
+ connection_send_or_die (connection, ret);
+ dbus_message_unref (ret);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static const DBusObjectPathVTable gobject_dbus_vtable = {
+ object_registration_unregistered,
+ object_registration_message,
+ NULL
+};
+
+typedef struct {
+ GClosure closure;
+ DBusGConnection *connection;
+ GObject *object;
+ const char *signame;
+ const char *sigiface;
+} DBusGSignalClosure;
+
+static GClosure *
+dbus_g_signal_closure_new (GObject *object,
+ const char *signame,
+ const char *sigiface)
+{
+ DBusGSignalClosure *closure;
+
+ closure = (DBusGSignalClosure*) g_closure_new_simple (sizeof (DBusGSignalClosure), NULL);
+
+ closure->object = object;
+ closure->signame = signame;
+ closure->sigiface = sigiface;
+ return (GClosure*) closure;
+}
+
+static void
+emit_signal_for_registration (ObjectRegistration *o,
+ DBusGSignalClosure *sigclosure,
+ GValue *retval,
+ guint n_param_values,
+ const GValue *param_values)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+ guint i;
+
+ g_assert (g_variant_is_object_path (o->object_path));
+ g_assert (g_dbus_is_interface_name (sigclosure->sigiface));
+ g_assert (g_dbus_is_member_name (sigclosure->signame));
+
+ signal = dbus_message_new_signal (o->object_path,
+ sigclosure->sigiface,
+ sigclosure->signame);
+ if (!signal)
+ oom (NULL);
+
+ dbus_message_iter_init_append (signal, &iter);
+
+ /* First argument is the object itself, and we can't marshall that */
+ for (i = 1; i < n_param_values; i++)
+ {
+ if (!_dbus_gvalue_marshal (&iter,
+ (GValue *) (&(param_values[i]))))
+ {
+ g_warning ("failed to marshal parameter %d for signal %s",
+ i, sigclosure->signame);
+ goto out;
+ }
+ }
+
+ connection_send_or_die (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection),
+ signal);
+out:
+ dbus_message_unref (signal);
+}
+
+static void
+signal_emitter_marshaller (GClosure *closure,
+ GValue *retval,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ DBusGSignalClosure *sigclosure;
+ const ObjectExport *oe;
+ const GSList *iter;
+
+ sigclosure = (DBusGSignalClosure *) closure;
+
+ g_assert (retval == NULL);
+
+ oe = g_object_get_data (sigclosure->object, "dbus_glib_object_registrations");
+ /* If the object has ever been exported, this should exist; it persists until
+ * the object is actually freed. */
+ g_assert (oe != NULL);
+
+ for (iter = oe->registrations; iter; iter = iter->next)
+ {
+ ObjectRegistration *o = iter->data;
+
+ emit_signal_for_registration (o, sigclosure, retval, n_param_values, param_values);
+ }
+}
+
+static void
+export_signals (const GList *info_list, GObject *object)
+{
+ GType gtype;
+ const char *sigdata;
+ const char *iface;
+ const char *signame;
+ const DBusGObjectInfo *info;
+
+ gtype = G_TYPE_FROM_INSTANCE (object);
+
+ for (; info_list != NULL; info_list = g_list_next (info_list))
+ {
+ info = (DBusGObjectInfo *) info_list->data;
+
+ sigdata = info->exported_signals;
+
+ while (*sigdata != '\0')
+ {
+ guint id;
+ GSignalQuery query;
+ GClosure *closure;
+ char *s;
+
+ sigdata = signal_iterate (sigdata, &iface, &signame);
+
+ if (!g_dbus_is_interface_name (iface))
+ {
+ g_critical ("invalid interface name found in %s: %s",
+ g_type_name (gtype), iface);
+ continue;
+ }
+
+ if (!g_dbus_is_member_name (signame))
+ {
+ g_critical ("invalid signal name found in %s: %s",
+ g_type_name (gtype), signame);
+ continue;
+ }
+
+ s = _dbus_gutils_wincaps_to_uscore (signame);
+
+ id = g_signal_lookup (s, gtype);
+ if (id == 0)
+ {
+ g_warning ("signal \"%s\" (from \"%s\") exported but not found in object class \"%s\"",
+ s, signame, g_type_name (gtype));
+ g_free (s);
+ continue;
+ }
+
+ g_signal_query (id, &query);
+
+ if (query.return_type != G_TYPE_NONE)
+ {
+ g_warning ("Not exporting signal \"%s\" for object class \"%s\" as it has a return type \"%s\"",
+ s, g_type_name (gtype), g_type_name (query.return_type));
+ g_free (s);
+ continue; /* FIXME: these could be listed as methods ? */
+ }
+
+ closure = dbus_g_signal_closure_new (object, signame, (char*) iface);
+ g_closure_set_marshal (closure, signal_emitter_marshaller);
+
+ g_signal_connect_closure_by_id (object,
+ id,
+ 0,
+ closure,
+ FALSE);
+
+ g_free (s);
+ }
+ }
+}
+
+static gint
+dbus_error_to_gerror_code (const char *derr)
+{
+ if (0) ;
+ else if (!strcmp (derr, DBUS_ERROR_FAILED ))
+ return DBUS_GERROR_FAILED ;
+ else if (!strcmp (derr, DBUS_ERROR_NO_MEMORY ))
+ return DBUS_GERROR_NO_MEMORY ;
+ else if (!strcmp (derr, DBUS_ERROR_SERVICE_UNKNOWN ))
+ return DBUS_GERROR_SERVICE_UNKNOWN ;
+ else if (!strcmp (derr, DBUS_ERROR_NAME_HAS_NO_OWNER ))
+ return DBUS_GERROR_NAME_HAS_NO_OWNER ;
+ else if (!strcmp (derr, DBUS_ERROR_NO_REPLY ))
+ return DBUS_GERROR_NO_REPLY ;
+ else if (!strcmp (derr, DBUS_ERROR_IO_ERROR ))
+ return DBUS_GERROR_IO_ERROR ;
+ else if (!strcmp (derr, DBUS_ERROR_BAD_ADDRESS ))
+ return DBUS_GERROR_BAD_ADDRESS ;
+ else if (!strcmp (derr, DBUS_ERROR_NOT_SUPPORTED ))
+ return DBUS_GERROR_NOT_SUPPORTED ;
+ else if (!strcmp (derr, DBUS_ERROR_LIMITS_EXCEEDED ))
+ return DBUS_GERROR_LIMITS_EXCEEDED ;
+ else if (!strcmp (derr, DBUS_ERROR_ACCESS_DENIED ))
+ return DBUS_GERROR_ACCESS_DENIED ;
+ else if (!strcmp (derr, DBUS_ERROR_AUTH_FAILED ))
+ return DBUS_GERROR_AUTH_FAILED ;
+ else if (!strcmp (derr, DBUS_ERROR_NO_SERVER ))
+ return DBUS_GERROR_NO_SERVER ;
+ else if (!strcmp (derr, DBUS_ERROR_TIMEOUT ))
+ return DBUS_GERROR_TIMEOUT ;
+ else if (!strcmp (derr, DBUS_ERROR_NO_NETWORK ))
+ return DBUS_GERROR_NO_NETWORK ;
+ else if (!strcmp (derr, DBUS_ERROR_ADDRESS_IN_USE ))
+ return DBUS_GERROR_ADDRESS_IN_USE ;
+ else if (!strcmp (derr, DBUS_ERROR_DISCONNECTED ))
+ return DBUS_GERROR_DISCONNECTED ;
+ else if (!strcmp (derr, DBUS_ERROR_INVALID_ARGS ))
+ return DBUS_GERROR_INVALID_ARGS ;
+ else if (!strcmp (derr, DBUS_ERROR_FILE_NOT_FOUND ))
+ return DBUS_GERROR_FILE_NOT_FOUND ;
+ else if (!strcmp (derr, DBUS_ERROR_FILE_EXISTS ))
+ return DBUS_GERROR_FILE_EXISTS ;
+ else if (!strcmp (derr, DBUS_ERROR_UNKNOWN_METHOD ))
+ return DBUS_GERROR_UNKNOWN_METHOD ;
+ else if (!strcmp (derr, DBUS_ERROR_TIMED_OUT ))
+ return DBUS_GERROR_TIMED_OUT ;
+ else if (!strcmp (derr, DBUS_ERROR_MATCH_RULE_NOT_FOUND ))
+ return DBUS_GERROR_MATCH_RULE_NOT_FOUND ;
+ else if (!strcmp (derr, DBUS_ERROR_MATCH_RULE_INVALID ))
+ return DBUS_GERROR_MATCH_RULE_INVALID ;
+ else if (!strcmp (derr, DBUS_ERROR_SPAWN_EXEC_FAILED ))
+ return DBUS_GERROR_SPAWN_EXEC_FAILED ;
+ else if (!strcmp (derr, DBUS_ERROR_SPAWN_FORK_FAILED ))
+ return DBUS_GERROR_SPAWN_FORK_FAILED ;
+ else if (!strcmp (derr, DBUS_ERROR_SPAWN_CHILD_EXITED ))
+ return DBUS_GERROR_SPAWN_CHILD_EXITED ;
+ else if (!strcmp (derr, DBUS_ERROR_SPAWN_CHILD_SIGNALED ))
+ return DBUS_GERROR_SPAWN_CHILD_SIGNALED ;
+ else if (!strcmp (derr, DBUS_ERROR_SPAWN_FAILED ))
+ return DBUS_GERROR_SPAWN_FAILED ;
+ else if (!strcmp (derr, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN ))
+ return DBUS_GERROR_UNIX_PROCESS_ID_UNKNOWN ;
+ else if (!strcmp (derr, DBUS_ERROR_INVALID_SIGNATURE ))
+ return DBUS_GERROR_INVALID_SIGNATURE ;
+ else if (!strcmp (derr, DBUS_ERROR_INVALID_FILE_CONTENT ))
+ return DBUS_GERROR_INVALID_FILE_CONTENT ;
+ else if (!strcmp (derr, DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN ))
+ return DBUS_GERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN ;
+ else
+ return DBUS_GERROR_REMOTE_EXCEPTION;
+}
+
+/**
+ * dbus_set_g_error:
+ * @gerror: an error
+ * @derror: a #DBusError
+ *
+ * Store the information from a DBus method error return into a
+ * GError. For the normal case of an arbitrary remote process,
+ * the error code will be DBUS_GERROR_REMOTE_EXCEPTION. Now,
+ * DBus errors have two components; a message and a "name".
+ * The former is an arbitrary (normally American English) string.
+ * The second is a string like com.example.FooFailure which
+ * programs can use as a conditional source. Because a GError
+ * only has one string, we use a hack to encode both values:
+ *
+ * &lt;human readable string&gt;&lt;null&gt;&lt;error name&gt;&lt;null&gt;
+ *
+ * You can use the following code to retrieve both values:
+ *
+ * |[const char *msg = error->message;
+ * size_t len = strlen(msg);
+ * const char *error_name = msg+len+1;]|
+ */
+void
+dbus_set_g_error (GError **gerror,
+ DBusError *derror)
+{
+ int code;
+
+ g_return_if_fail (derror != NULL);
+ g_return_if_fail (dbus_error_is_set (derror));
+ g_return_if_fail (gerror == NULL || *gerror == NULL);
+
+ code = dbus_error_to_gerror_code (derror->name);
+ if (code != DBUS_GERROR_REMOTE_EXCEPTION)
+ g_set_error (gerror, DBUS_GERROR,
+ code,
+ "%s",
+ derror->message);
+ else
+ g_set_error (gerror, DBUS_GERROR,
+ code,
+ "%s%c%s",
+ derror->message ? derror->message : "",
+ '\0',
+ derror->name);
+}
+
+static void
+dbus_g_error_info_free (gpointer p)
+{
+ DBusGErrorInfo *info;
+
+ info = p;
+
+ g_free (info->default_iface);
+ g_free (info);
+}
+
+/**
+ * SECTION:dbus-gobject
+ * @short_description: Exporting a #GObject remotely
+ * @see_also: #GObject
+ * @stability: Stable
+ *
+ * FIXME
+ */
+
+/**
+ * dbus_glib_global_set_disable_legacy_property_access:
+ *
+ * For historical reasons, DBus-GLib will allow read-only
+ * access to every GObject property of an object exported
+ * to the bus, regardless of whether or not the property
+ * is listed in the type info installed with
+ * dbus_g_object_type_install_info(). (Write access is
+ * denied however).
+ *
+ * If you wish to restrict even read-only access, you
+ * can call this method to globally change the behavior
+ * for the entire process.
+ *
+ * Since: 0.88
+ */
+void
+dbus_glib_global_set_disable_legacy_property_access (void)
+{
+ disable_legacy_property_access = TRUE;
+}
+
+/**
+ * dbus_g_object_type_install_info:
+ * @object_type: #GType for the object
+ * @info: introspection data generated by #dbus-glib-tool
+ *
+ * Install introspection information about the given object #GType
+ * sufficient to allow methods on the object to be invoked by name.
+ * The introspection information is normally generated by
+ * dbus-glib-tool, then this function is called in the
+ * class_init() for the object class.
+ *
+ * Once introspection information has been installed, instances of the
+ * object registered with dbus_g_connection_register_g_object() can have
+ * their methods invoked remotely.
+ */
+void
+dbus_g_object_type_install_info (GType object_type,
+ const DBusGObjectInfo *info)
+{
+ g_return_if_fail (G_TYPE_IS_CLASSED (object_type) || G_TYPE_IS_INTERFACE (object_type));
+
+ _dbus_g_value_types_init ();
+
+ g_type_set_qdata (object_type,
+ dbus_g_object_type_dbus_metadata_quark (),
+ (gpointer) info);
+}
+
+/**
+ * dbus_g_error_domain_register:
+ * @domain: the #GError domain
+ * @default_iface: the prefix used for error values, or %NULL
+ * @code_enum: a #GType for a #GEnum of the error codes
+ *
+ * Register a #GError domain and set of codes with D-Bus. When an object
+ * raises a #GError in the domain @domain from one of its D-Bus methods,
+ * the D-Bus error name used will be @default_iface, followed by a dot,
+ * followed by the #GEnumValue.value_nick corresponding to the #GError.code.
+ * For D-Bus, it's conventional to use an error name (value_nick) that is
+ * in CamelCase.
+ *
+ * (For instance, if a D-Bus method <code>com.example.MyObject.GetThings</code>
+ * can raise a #GError with domain <code>MY_ERROR</code> and code
+ * <code>MY_ERROR_NOT_HAPPY</code>, you could call
+ * <code>dbus_g_error_domain_register (MY_ERROR, "com.example.MyError",
+ * MY_TYPE_ERROR)</code>, and set up the value_nick for
+ * <code>MY_ERROR_NOT_HAPPY</code> to be <code>NotHappy</code>,
+ * resulting in the D-Bus error string
+ * <code>com.example.MyError.NotHappy</code>.)
+ *
+ * If @default_iface is %NULL, the D-Bus interface of the method that failed
+ * will be used.
+ *
+ * (For instance, if the above example had called
+ * <code>dbus_g_error_domain_register (MY_ERROR, NULL, MY_TYPE_ERROR)</code>
+ * instead, then the D-Bus error string would be
+ * <code>com.example.MyObject.NotHappy</code>.)
+ */
+void
+dbus_g_error_domain_register (GQuark domain,
+ const char *default_iface,
+ GType code_enum)
+{
+ DBusGErrorInfo *info;
+
+ g_return_if_fail (g_quark_to_string (domain) != NULL);
+ g_return_if_fail (code_enum != G_TYPE_INVALID);
+ g_return_if_fail (G_TYPE_FUNDAMENTAL (code_enum) == G_TYPE_ENUM);
+
+ g_static_rw_lock_writer_lock (&globals_lock);
+
+ if (error_metadata == NULL)
+ g_datalist_init (&error_metadata);
+
+ info = g_datalist_id_get_data (&error_metadata, domain);
+
+ if (info != NULL)
+ {
+ g_warning ("Metadata for error domain \"%s\" already registered\n",
+ g_quark_to_string (domain));
+ }
+ else
+ {
+ info = g_new0 (DBusGErrorInfo, 1);
+ info->default_iface = g_strdup (default_iface);
+ info->code_enum = code_enum;
+
+ g_datalist_id_set_data_full (&error_metadata,
+ domain,
+ info,
+ dbus_g_error_info_free);
+ }
+
+ g_static_rw_lock_writer_unlock (&globals_lock);
+}
+
+/* Called when the object is destroyed */
+static void
+object_export_object_died (gpointer user_data, GObject *dead)
+{
+ ObjectExport *oe = user_data;
+
+ g_assert (dead == oe->object);
+
+ /* this prevents the weak unref from taking place, which would cause an
+ * assertion failure since the object has already gone... */
+ oe->object = NULL;
+
+ /* ... while this results in a call to object_registration_unregistered
+ * for each contained registration */
+ object_export_unregister_all (oe);
+
+ /* We deliberately don't remove the ObjectExport yet, in case the object is
+ * resurrected and re-registered: if that happens, we wouldn't want to call
+ * export_signals() again. */
+}
+
+/**
+ * dbus_g_connection_unregister_g_object:
+ * @connection: the D-BUS connection
+ * @object: the object
+ *
+ * Removes @object from any object paths at which it is exported on
+ * @connection. Properties, methods, and signals
+ * of the object can no longer be accessed remotely.
+ */
+void
+dbus_g_connection_unregister_g_object (DBusGConnection *connection,
+ GObject *object)
+{
+ ObjectExport *oe;
+ GSList *registrations;
+
+ g_return_if_fail (connection != NULL);
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ oe = g_object_get_data (object, "dbus_glib_object_registrations");
+
+ g_return_if_fail (oe != NULL);
+ g_return_if_fail (oe->registrations != NULL);
+
+ /* Copy the list before iterating it: it will be modified in
+ * object_registration_free() each time an object path is unregistered.
+ */
+ for (registrations = g_slist_copy (oe->registrations);
+ registrations != NULL;
+ registrations = g_slist_delete_link (registrations, registrations))
+ {
+ ObjectRegistration *o = registrations->data;
+
+ if (o->connection != connection)
+ continue;
+
+ dbus_connection_unregister_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection),
+ o->object_path);
+ }
+}
+
+/**
+ * dbus_g_connection_register_g_object:
+ * @connection: the D-BUS connection
+ * @at_path: the path where the object will live (the object's name)
+ * @object: the object
+ *
+ * Registers a #GObject at the given path. Properties, methods, and signals
+ * of the object can then be accessed remotely. Methods are only available
+ * if method introspection data has been added to the object's class
+ * with dbus_g_object_type_install_info().
+ *
+ * The registration will be cancelled if either the #DBusConnection or
+ * the #GObject gets finalized, or if dbus_g_connection_unregister_g_object()
+ * is used.
+ *
+ * Note: If an object is registered multiple times, the first registration
+ * takes priority for cases such as turning an object into an object path.
+ */
+void
+dbus_g_connection_register_g_object (DBusGConnection *connection,
+ const char *at_path,
+ GObject *object)
+{
+ ObjectExport *oe;
+ GSList *iter;
+ ObjectRegistration *o;
+ DBusError error;
+
+ g_return_if_fail (connection != NULL);
+ g_return_if_fail (g_variant_is_object_path (at_path));
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ oe = g_object_get_data (object, "dbus_glib_object_registrations");
+
+ if (oe == NULL)
+ {
+ GList *info_list = lookup_object_info (object);
+
+ if (info_list == NULL)
+ {
+ g_warning ("No introspection data registered for object class \"%s\"",
+ g_type_name (G_TYPE_FROM_INSTANCE (object)));
+ return;
+ }
+
+ /* This adds a hook into every signal for the object. Only do this
+ * on the first registration, because inside the signal marshaller
+ * we emit a signal for each registration.
+ */
+ export_signals (info_list, object);
+ g_list_free (info_list);
+
+ oe = object_export_new ();
+ g_object_set_data_full (object, "dbus_glib_object_registrations", oe,
+ (GDestroyNotify) object_export_free);
+ }
+
+ if (oe->object == NULL)
+ {
+ /* Either the ObjectExport is newly-created, or it already existed but
+ * the object was disposed and resurrected, causing the weak ref to
+ * fall off */
+ oe->object = object;
+ g_object_weak_ref (object, object_export_object_died, oe);
+ }
+
+ for (iter = oe->registrations; iter; iter = iter->next)
+ {
+ o = iter->data;
+
+ /* Silently ignore duplicate registrations */
+ if (strcmp (o->object_path, at_path) == 0 && o->connection == connection)
+ return;
+ }
+
+ o = object_registration_new (connection, at_path, oe);
+
+ dbus_error_init (&error);
+ if (!dbus_connection_try_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
+ at_path,
+ &gobject_dbus_vtable,
+ o,
+ &error))
+ {
+ g_error ("Failed to register GObject with DBusConnection: %s %s",
+ error.name, error.message);
+ dbus_error_free (&error);
+ object_registration_free (o);
+ return;
+ }
+
+ oe->registrations = g_slist_append (oe->registrations, o);
+}
+
+/**
+ * dbus_g_connection_lookup_g_object:
+ * @connection: a #DBusGConnection
+ * @at_path: path
+ *
+ * FIXME
+ *
+ * Returns: the object at path @at_path
+ */
+GObject *
+dbus_g_connection_lookup_g_object (DBusGConnection *connection,
+ const char *at_path)
+{
+ gpointer p;
+ ObjectRegistration *o;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (g_variant_is_object_path (at_path), NULL);
+
+ if (!dbus_connection_get_object_path_data (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &p))
+ return NULL;
+
+ if (p == NULL)
+ return NULL;
+
+ o = p;
+
+ if (o->export->object == NULL)
+ return NULL;
+
+ return G_OBJECT (o->export->object);
+}
+
+typedef struct {
+ GType rettype;
+ guint n_params;
+ GType *params;
+} DBusGFuncSignature;
+
+static guint
+funcsig_hash (gconstpointer key)
+{
+ const DBusGFuncSignature *sig = key;
+ GType *types;
+ guint ret;
+ guint i;
+
+ ret = sig->rettype;
+ types = sig->params;
+
+ for (i = 0; i < sig->n_params; i++)
+ {
+ ret += (int) (*types);
+ types++;
+ }
+
+ return ret;
+}
+
+static gboolean
+funcsig_equal (gconstpointer aval,
+ gconstpointer bval)
+{
+ const DBusGFuncSignature *a = aval;
+ const DBusGFuncSignature *b = bval;
+ const GType *atypes;
+ const GType *btypes;
+ guint i;
+
+ if (a->rettype != b->rettype
+ || a->n_params != b->n_params)
+ return FALSE;
+
+ atypes = a->params;
+ btypes = b->params;
+
+ for (i = 0; i < a->n_params; i++)
+ {
+ if (*btypes != *atypes)
+ return FALSE;
+ atypes++;
+ btypes++;
+ }
+
+ return TRUE;
+}
+
+static void
+funcsig_free (DBusGFuncSignature *sig)
+{
+ g_free (sig->params);
+ g_free (sig);
+}
+
+GClosureMarshal
+_dbus_gobject_lookup_marshaller (GType rettype,
+ guint n_params,
+ const GType *param_types)
+{
+ GClosureMarshal ret;
+ DBusGFuncSignature sig;
+ GType *params;
+ guint i;
+
+ /* Convert to fundamental types */
+ rettype = G_TYPE_FUNDAMENTAL (rettype);
+ params = g_new (GType, n_params);
+ for (i = 0; i < n_params; i++)
+ params[i] = G_TYPE_FUNDAMENTAL (param_types[i]);
+
+ sig.rettype = rettype;
+ sig.n_params = n_params;
+ sig.params = params;
+
+ g_static_rw_lock_reader_lock (&globals_lock);
+
+ if (marshal_table)
+ ret = g_hash_table_lookup (marshal_table, &sig);
+ else
+ ret = NULL;
+
+ g_static_rw_lock_reader_unlock (&globals_lock);
+
+ if (ret == NULL)
+ {
+ if (rettype == G_TYPE_NONE)
+ {
+ if (n_params == 0)
+ ret = g_cclosure_marshal_VOID__VOID;
+ else if (n_params == 1)
+ {
+ switch (params[0])
+ {
+ case G_TYPE_BOOLEAN:
+ ret = g_cclosure_marshal_VOID__BOOLEAN;
+ break;
+ case G_TYPE_UCHAR:
+ ret = g_cclosure_marshal_VOID__UCHAR;
+ break;
+ case G_TYPE_INT:
+ ret = g_cclosure_marshal_VOID__INT;
+ break;
+ case G_TYPE_UINT:
+ ret = g_cclosure_marshal_VOID__UINT;
+ break;
+ case G_TYPE_DOUBLE:
+ ret = g_cclosure_marshal_VOID__DOUBLE;
+ break;
+ case G_TYPE_STRING:
+ ret = g_cclosure_marshal_VOID__STRING;
+ break;
+ case G_TYPE_BOXED:
+ ret = g_cclosure_marshal_VOID__BOXED;
+ break;
+ }
+ }
+ else if (n_params == 3
+ && params[0] == G_TYPE_STRING
+ && params[1] == G_TYPE_STRING
+ && params[2] == G_TYPE_STRING)
+ {
+ ret = _dbus_g_marshal_NONE__STRING_STRING_STRING;
+ }
+ }
+ }
+
+ g_free (params);
+ return ret;
+}
+
+/**
+ * dbus_g_object_register_marshaller:
+ * @marshaller: a GClosureMarshal to be used for invocation
+ * @rettype: a GType for the return type of the function
+ * @...: The parameter #GTypes, followed by %G_TYPE_INVALID
+ *
+ * Register a #GClosureMarshal to be used for signal invocations,
+ * giving its return type and a list of parameter types,
+ * followed by %G_TYPE_INVALID.
+ *
+ * This function will not be needed once GLib includes libffi.
+ */
+void
+dbus_g_object_register_marshaller (GClosureMarshal marshaller,
+ GType rettype,
+ ...)
+{
+ va_list args;
+ GArray *types;
+ GType gtype;
+
+ va_start (args, rettype);
+
+ types = g_array_new (TRUE, TRUE, sizeof (GType));
+
+ while ((gtype = va_arg (args, GType)) != G_TYPE_INVALID)
+ g_array_append_val (types, gtype);
+
+ dbus_g_object_register_marshaller_array (marshaller, rettype,
+ types->len, (GType*) types->data);
+
+ g_array_free (types, TRUE);
+ va_end (args);
+}
+
+/**
+ * dbus_g_object_register_marshaller_array:
+ * @marshaller: a #GClosureMarshal to be used for invocation
+ * @rettype: a #GType for the return type of the function
+ * @n_types: number of function parameters
+ * @types: a C array of GTypes values
+ *
+ * Register a #GClosureMarshal to be used for signal invocations.
+ * @see_also dbus_g_object_register_marshaller()
+ */
+void
+dbus_g_object_register_marshaller_array (GClosureMarshal marshaller,
+ GType rettype,
+ guint n_types,
+ const GType* types)
+{
+ DBusGFuncSignature *sig;
+ guint i;
+
+ g_static_rw_lock_writer_lock (&globals_lock);
+
+ if (marshal_table == NULL)
+ marshal_table = g_hash_table_new_full (funcsig_hash,
+ funcsig_equal,
+ (GDestroyNotify) funcsig_free,
+ NULL);
+ sig = g_new0 (DBusGFuncSignature, 1);
+ sig->rettype = G_TYPE_FUNDAMENTAL (rettype);
+ sig->n_params = n_types;
+ sig->params = g_new (GType, n_types);
+ for (i = 0; i < n_types; i++)
+ sig->params[i] = G_TYPE_FUNDAMENTAL (types[i]);
+
+ g_hash_table_insert (marshal_table, sig, marshaller);
+
+ g_static_rw_lock_writer_unlock (&globals_lock);
+}
+
+/**
+ * dbus_g_method_get_sender:
+ * @context: the method context
+ *
+ * Get the sender of a message so we can send a
+ * "reply" later (i.e. send a message directly
+ * to a service which invoked the method at a
+ * later time).
+ *
+ * Returns: the unique name of the sender. It
+ * is up to the caller to free the returned string.
+ */
+gchar *
+dbus_g_method_get_sender (DBusGMethodInvocation *context)
+{
+ const gchar *sender;
+
+ g_return_val_if_fail (context != NULL, NULL);
+
+ sender = dbus_message_get_sender (dbus_g_message_get_message (context->message));
+ return g_strdup (sender);
+}
+
+/**
+ * dbus_g_method_get_reply:
+ * @context: the method context
+ *
+ * Get the reply message to append reply values
+ * Used as a sidedoor when you can't generate dbus values
+ * of the correct type due to glib binding limitations
+ *
+ * Returns: a #DBusMessage with the reply
+ */
+DBusMessage *
+dbus_g_method_get_reply (DBusGMethodInvocation *context)
+{
+ g_return_val_if_fail (context != NULL, NULL);
+
+ return reply_or_die (dbus_g_message_get_message (context->message));
+}
+
+/**
+ * dbus_g_method_send_reply:
+ * @context: the method context
+ * @reply: the reply message, will be unreffed
+ *
+ * Send a manually created reply message.
+ *
+ * Used as a sidedoor when you can't generate dbus values
+ * of the correct type due to glib binding limitations
+ */
+void
+dbus_g_method_send_reply (DBusGMethodInvocation *context, DBusMessage *reply)
+{
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (reply != NULL);
+
+ connection_send_or_die (dbus_g_connection_get_connection (context->connection),
+ reply);
+ dbus_message_unref (reply);
+
+ dbus_g_connection_unref (context->connection);
+ dbus_g_message_unref (context->message);
+ g_free (context);
+}
+
+
+/**
+ * dbus_g_method_return:
+ * @context: the method context
+ * @...: zero or more values to return from the method, with their number
+ * and types given by its #DBusGObjectInfo
+ *
+ * Send a return message for a given method invocation, with arguments.
+ * This function also frees the sending context.
+ */
+void
+dbus_g_method_return (DBusGMethodInvocation *context, ...)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ va_list args;
+ char *out_sig;
+ GArray *argsig;
+ guint i;
+
+ g_return_if_fail (context != NULL);
+
+ /* This field was initialized inside invoke_object_method; we
+ * carry it over through the async invocation to here.
+ */
+ if (!context->send_reply)
+ goto out;
+
+ reply = dbus_g_method_get_reply (context);
+ out_sig = method_output_signature_from_object_info (context->object, context->method);
+ argsig = _dbus_gtypes_from_arg_signature (out_sig, FALSE);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ va_start (args, context);
+ for (i = 0; i < argsig->len; i++)
+ {
+ GValue value = {0,};
+ char *error;
+ g_value_init (&value, g_array_index (argsig, GType, i));
+ error = NULL;
+ G_VALUE_COLLECT (&value, args, G_VALUE_NOCOPY_CONTENTS, &error);
+ if (error)
+ {
+ g_warning("%s", error);
+ g_free (error);
+ }
+ else
+ {
+ if (!_dbus_gvalue_marshal (&iter, &value))
+ g_warning ("failed to marshal parameter %d for method %s",
+ i, dbus_message_get_member (
+ dbus_g_message_get_message (context->message)));
+ }
+ }
+ va_end (args);
+
+ connection_send_or_die (dbus_g_connection_get_connection (context->connection),
+ reply);
+ dbus_message_unref (reply);
+
+ g_free (out_sig);
+ g_array_free (argsig, TRUE);
+
+out:
+ dbus_g_connection_unref (context->connection);
+ dbus_g_message_unref (context->message);
+ g_free (context);
+}
+
+/**
+ * dbus_g_method_return_error:
+ * @context: the method context
+ * @error: the error to send
+ *
+ * Send a error message for a given method invocation.
+ * This function also frees the sending context.
+ */
+void
+dbus_g_method_return_error (DBusGMethodInvocation *context, const GError *error)
+{
+ DBusMessage *reply;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (error != NULL);
+
+ /* See comment in dbus_g_method_return */
+ if (!context->send_reply)
+ goto out;
+
+ reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error);
+ connection_send_or_die (
+ dbus_g_connection_get_connection (context->connection), reply);
+ dbus_message_unref (reply);
+
+out:
+ dbus_g_connection_unref (context->connection);
+ dbus_g_message_unref (context->message);
+ g_free (context);
+}
+
+const char *
+_dbus_gobject_get_path (GObject *obj)
+{
+ ObjectExport *oe;
+ ObjectRegistration *o;
+
+ oe = g_object_get_data (obj, "dbus_glib_object_registrations");
+
+ if (oe == NULL || oe->registrations == NULL)
+ return NULL;
+
+ /* First one to have been registered wins */
+ o = oe->registrations->data;
+
+ return o->object_path;
+}
+
+#ifdef DBUS_BUILD_TESTS
+#include <stdlib.h>
+
+static void
+_dummy_function (void)
+{
+}
+
+/* Data structures copied from one generated by current dbus-binding-tool;
+ * we need to support this layout forever
+ */
+static const DBusGMethodInfo dbus_glib_internal_test_methods[] = {
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 0 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 49 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 117 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 191 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 270 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 320 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 391 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 495 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 623 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 693 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 765 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 838 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 911 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 988 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1064 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1140 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1204 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1278 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1347 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1408 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1460 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1533 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1588 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1647 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1730 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1784 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1833 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1895 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1947 },
+ { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1999 },
+};
+
+const DBusGObjectInfo dbus_glib_internal_test_object_info = {
+ 0,
+ dbus_glib_internal_test_methods,
+ 30,
+"org.freedesktop.DBus.Tests.MyObject\0DoNothing\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Increment\0S\0x\0I\0u\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementRetval\0S\0x\0I\0u\0arg1\0O\0F\0R\0u\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementRetvalError\0S\0x\0I\0u\0arg1\0O\0F\0E\0u\0\0org.freedesktop.DBus.Tests.MyObject\0ThrowError\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Uppercase\0S\0arg0\0I\0s\0arg1\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0ManyArgs\0S\0x\0I\0u\0str\0I\0s\0trouble\0I\0d\0d_ret\0O\0F\0N\0d\0str_ret\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0ManyReturn\0S\0arg0\0O\0F\0N\0u\0arg1\0O\0F\0N\0s\0arg2\0O\0F\0N\0i\0arg3\0O\0F\0N\0u\0arg4\0O\0F\0N\0u\0arg5\0O\0C\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0Stringify\0S\0val\0I\0v\0arg1\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0Unstringify\0S\0val\0I\0s\0arg1\0O\0F\0N\0v\0\0org.freedesktop.DBus.Tests.MyObject\0Recursive1\0S\0arg0\0I\0au\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0Recursive2\0S\0arg0\0I\0u\0arg1\0O\0F\0N\0au\0\0org.freedesktop.DBus.Tests.MyObject\0ManyUppercase\0S\0arg0\0I\0as\0arg1\0O\0F\0N\0as\0\0org.freedesktop.DBus.Tests.MyObject\0StrHashLen\0S\0arg0\0I\0a{ss}\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0SendCar\0S\0arg0\0I\0(suv)\0arg1\0O\0F\0N\0(uo)\0\0org.freedesktop.DBus.Tests.MyObject\0GetHash\0S\0arg0\0O\0F\0N\0a{ss}\0\0org.freedesktop.DBus.Tests.MyObject\0RecArrays\0S\0val\0I\0aas\0arg1\0O\0F\0N\0aau\0\0org.freedesktop.DBus.Tests.MyObject\0Objpath\0S\0arg0\0I\0o\0arg1\0O\0C\0N\0o\0\0org.freedesktop.DBus.Tests.MyObject\0GetObjs\0S\0arg0\0O\0F\0N\0ao\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementVal\0S\0\0org.freedesktop.DBus.Tests.MyObject\0AsyncIncrement\0A\0x\0I\0u\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0AsyncThrowError\0A\0\0org.freedesktop.DBus.Tests.MyObject\0GetVal\0S\0arg0\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0ManyStringify\0S\0arg0\0I\0a{sv}\0arg1\0O\0F\0N\0a{sv}\0\0org.freedesktop.DBus.Tests.MyObject\0EmitFrobnicate\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Terminate\0S\0\0org.freedesktop.DBus.Tests.FooObject\0GetValue\0S\0arg0\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.FooObject\0EmitSignals\0S\0\0org.freedesktop.DBus.Tests.FooObject\0EmitSignal2\0S\0\0org.freedesktop.DBus.Tests.FooObject\0Terminate\0S\0\0\0",
+"org.freedesktop.DBus.Tests.MyObject\0Frobnicate\0org.freedesktop.DBus.Tests.FooObject\0Sig0\0org.freedesktop.DBus.Tests.FooObject\0Sig1\0org.freedesktop.DBus.Tests.FooObject\0Sig2\0\0",
+"\0"
+};
+
+
+/*
+ * Unit test for GLib GObject integration ("skeletons")
+ * Returns: %TRUE on success.
+ */
+gboolean
+_dbus_gobject_test (const char *test_data_dir)
+{
+ int i;
+ const char *arg;
+ const char *arg_name;
+ gboolean arg_in;
+ gboolean constval;
+ RetvalType retval;
+ const char *arg_signature;
+ const char *sigdata;
+ const char *iface;
+ const char *signame;
+
+ static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
+ { "SetFoo", "set_foo" },
+ { "Foo", "foo" },
+ { "GetFooBar", "get_foo_bar" },
+ { "Hello", "hello" }
+
+ /* Impossible-to-handle cases */
+ /* { "FrobateUIHandler", "frobate_ui_handler" } */
+ };
+
+ /* Test lookup in our hardcoded object info; if these tests fail
+ * then it likely means you changed the generated object info in an
+ * incompatible way and broke the lookup functions. In that case
+ * you need to bump the version and use a new structure instead. */
+ /* DoNothing */
+ arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+ &(dbus_glib_internal_test_methods[0]));
+ g_assert (*arg == '\0');
+
+ /* Increment */
+ arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+ &(dbus_glib_internal_test_methods[1]));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (!strcmp (arg_name, "x"));
+ g_assert (arg_in == TRUE);
+ g_assert (!strcmp (arg_signature, "u"));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (arg_in == FALSE);
+ g_assert (retval == RETVAL_NONE);
+ g_assert (!strcmp (arg_signature, "u"));
+ g_assert (*arg == '\0');
+
+ /* IncrementRetval */
+ arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+ &(dbus_glib_internal_test_methods[2]));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (!strcmp (arg_name, "x"));
+ g_assert (arg_in == TRUE);
+ g_assert (!strcmp (arg_signature, "u"));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (retval == RETVAL_NOERROR);
+ g_assert (arg_in == FALSE);
+ g_assert (!strcmp (arg_signature, "u"));
+ g_assert (*arg == '\0');
+
+ /* IncrementRetvalError */
+ arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+ &(dbus_glib_internal_test_methods[3]));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (!strcmp (arg_name, "x"));
+ g_assert (arg_in == TRUE);
+ g_assert (!strcmp (arg_signature, "u"));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (retval == RETVAL_ERROR);
+ g_assert (arg_in == FALSE);
+ g_assert (!strcmp (arg_signature, "u"));
+ g_assert (*arg == '\0');
+
+ /* Stringify */
+ arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+ &(dbus_glib_internal_test_methods[8]));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (!strcmp (arg_name, "val"));
+ g_assert (arg_in == TRUE);
+ g_assert (!strcmp (arg_signature, "v"));
+ g_assert (*arg != '\0');
+ arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+ g_assert (retval == RETVAL_NONE);
+ g_assert (arg_in == FALSE);
+ g_assert (!strcmp (arg_signature, "s"));
+ g_assert (*arg == '\0');
+
+ sigdata = dbus_glib_internal_test_object_info.exported_signals;
+ g_assert (*sigdata != '\0');
+ sigdata = signal_iterate (sigdata, &iface, &signame);
+ g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.MyObject"));
+ g_assert (!strcmp (signame, "Frobnicate"));
+ g_assert (*sigdata != '\0');
+ sigdata = signal_iterate (sigdata, &iface, &signame);
+ g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
+ g_assert (!strcmp (signame, "Sig0"));
+ g_assert (*sigdata != '\0');
+ sigdata = signal_iterate (sigdata, &iface, &signame);
+ g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
+ g_assert (!strcmp (signame, "Sig1"));
+ g_assert (*sigdata != '\0');
+ sigdata = signal_iterate (sigdata, &iface, &signame);
+ g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
+ g_assert (!strcmp (signame, "Sig2"));
+ g_assert (*sigdata == '\0');
+
+
+ i = 0;
+ while (i < (int) G_N_ELEMENTS (name_pairs))
+ {
+ char *uscore;
+ char *wincaps;
+
+ uscore = _dbus_gutils_wincaps_to_uscore (name_pairs[i].wincaps);
+ wincaps = uscore_to_wincaps (name_pairs[i].uscore);
+
+ if (strcmp (uscore, name_pairs[i].uscore) != 0)
+ {
+ g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
+ name_pairs[i].wincaps, name_pairs[i].uscore,
+ uscore);
+ exit (1);
+ }
+
+ if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
+ {
+ g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
+ name_pairs[i].uscore, name_pairs[i].wincaps,
+ wincaps);
+ exit (1);
+ }
+
+ g_free (uscore);
+ g_free (wincaps);
+
+ ++i;
+ }
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */