X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fdbus-gobject.c;h=0a019cc2571abd58339320bbb6d28b855d6b0ddc;hb=1a163e765c0d6a86d2aa2ffb18a1d7e29a052e7a;hp=e1e387e5fcaf02044c4b79816282d98444ed4dc1;hpb=9c3d566e95c9080f6040c64531b0ccae22bd5d74;p=platform%2Fupstream%2Fdbus.git diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c index e1e387e..0a019cc 100644 --- a/glib/dbus-gobject.c +++ b/glib/dbus-gobject.c @@ -1,7 +1,8 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-gobject.c Exporting a GObject remotely * - * Copyright (C) 2003, 2004 Red Hat, Inc. + * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. + * Copyright (C) 2005 Nokia * * Licensed under the Academic Free License version 2.1 * @@ -22,11 +23,16 @@ */ #include +#include #include #include #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 /** @@ -34,35 +40,15 @@ * @{ */ -static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT; -static GHashTable *info_hash = NULL; - -static char* -wincaps_to_uscore (const char *caps) +typedef struct { - const char *p; - GString *str; - - str = g_string_new (NULL); - p = caps; - while (*p) - { - if (g_ascii_isupper (*p)) - { - if (str->len > 0 && - (str->len < 2 || str->str[str->len-2] != '_')) - g_string_append_c (str, '_'); - g_string_append_c (str, g_ascii_tolower (*p)); - } - else - { - g_string_append_c (str, *p); - } - ++p; - } + char *default_iface; + GType code_enum; +} DBusGErrorInfo; - return g_string_free (str, FALSE); -} +static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT; +static GHashTable *marshal_table = NULL; +static GData *error_metadata = NULL; static char* uscore_to_wincaps (const char *uscore) @@ -97,182 +83,495 @@ uscore_to_wincaps (const char *uscore) return g_string_free (str, FALSE); } -static void -gobject_unregister_function (DBusConnection *connection, - void *user_data) +static const char * +string_table_next (const char *table) { - GObject *object; + return (table + (strlen (table) + 1)); +} - object = G_OBJECT (user_data); +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 int -gtype_to_dbus_type (GType type) +static const char * +method_interface_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) { - switch (type) - { - case G_TYPE_CHAR: - case G_TYPE_UCHAR: - return DBUS_TYPE_BYTE; - - case G_TYPE_BOOLEAN: - return DBUS_TYPE_BOOLEAN; + return string_table_lookup (get_method_data (object, method), 0); +} - /* long gets cut to 32 bits so the remote API is consistent - * on all architectures - */ - - case G_TYPE_LONG: - case G_TYPE_INT: - return DBUS_TYPE_INT32; - case G_TYPE_ULONG: - case G_TYPE_UINT: - return DBUS_TYPE_UINT32; - - case G_TYPE_INT64: - return DBUS_TYPE_INT64; - - case G_TYPE_UINT64: - return DBUS_TYPE_UINT64; - - case G_TYPE_FLOAT: - case G_TYPE_DOUBLE: - return DBUS_TYPE_DOUBLE; +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; + +static const char * +arg_iterate (const char *data, + const char **name, + gboolean *in, + gboolean *constval, + RetvalType *retval, + const char **type) +{ + gboolean inarg; - case G_TYPE_STRING: - return DBUS_TYPE_STRING; + if (name) + *name = data; + data = string_table_next (data); + switch (*data) + { + case 'I': + inarg = TRUE; + break; + case 'O': + inarg = FALSE; + break; default: - return DBUS_TYPE_INVALID; + 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 void -introspect_properties (GObject *object, GString *xml) +static char * +method_dir_signature_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method, + gboolean in) { - unsigned int i; - unsigned int n_specs; - GType last_type; - GParamSpec **specs; + const char *arg; + GString *ret; + + arg = method_arg_info_from_object_info (object, method); - last_type = G_TYPE_INVALID; - specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), - &n_specs); + ret = g_string_new (NULL); - for (i = 0; i < n_specs; i++ ) + while (*arg) { - char *s; - int dbus_type; - gboolean can_set; - gboolean can_get; - GParamSpec *spec = specs[i]; - - dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec)); - if (dbus_type == DBUS_TYPE_INVALID) - continue; - - if (spec->owner_type != last_type) - { - if (last_type != G_TYPE_INVALID) - g_string_append (xml, " \n"); + const char *name; + gboolean arg_in; + const char *type; + arg = arg_iterate (arg, &name, &arg_in, NULL, NULL, &type); - /* FIXME what should the namespace on the interface be in - * general? should people be able to set it for their - * objects? - */ - g_string_append (xml, " owner_type)); - g_string_append (xml, "\">\n"); + if (arg_in == in) + g_string_append (ret, type); + } - last_type = spec->owner_type; - } + return g_string_free (ret, FALSE); +} - can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 && - (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0); - - can_get = (spec->flags & G_PARAM_READABLE) != 0; +static char * +method_input_signature_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return method_dir_signature_from_object_info (object, method, TRUE); +} - s = uscore_to_wincaps (spec->name); - - if (can_set) - { - g_string_append (xml, " \n"); - - g_string_append (xml, " \n"); - } +static char * +method_output_signature_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return method_dir_signature_from_object_info (object, method, FALSE); +} - if (can_get) - { - g_string_append (xml, " \n"); - - g_string_append (xml, " \n"); - } +static const char * +propsig_iterate (const char *data, const char **iface, const char **name) +{ + *iface = data; - g_free (s); + data = string_table_next (data); + *name = data; + + return string_table_next (data); +} + +static GQuark +dbus_g_object_type_dbus_metadata_quark (void) +{ + static GQuark quark; + + if (!quark) + quark = g_quark_from_static_string ("DBusGObjectTypeDBusMetadataQuark"); + return quark; +} + +static const DBusGObjectInfo * +lookup_object_info (GObject *object) +{ + const DBusGObjectInfo *ret; + GType classtype; + + ret = NULL; + + for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype)) + { + const DBusGObjectInfo *info; + + info = g_type_get_qdata (classtype, dbus_g_object_type_dbus_metadata_quark ()); + + if (info != NULL && info->format_version >= 0) + { + ret = info; + break; + } } - if (last_type != G_TYPE_INVALID) - g_string_append (xml, " \n"); + return ret; +} + +static void +gobject_unregister_function (DBusConnection *connection, + void *user_data) +{ + GObject *object; + + object = G_OBJECT (user_data); + + /* FIXME */ - g_free (specs); } +typedef struct +{ + GString *xml; + GType gtype; + const DBusGObjectInfo *object_info; +} DBusGLibWriteIterfaceData; + +typedef struct +{ + GSList *methods; + GSList *signals; + GSList *properties; +} DBusGLibWriteInterfaceValues; + static void -introspect_signals (GType type, GString *xml) +write_interface (gpointer key, gpointer val, gpointer user_data) { - guint i; - guint *ids, n_ids; + const char *name; + GSList *methods; + GSList *signals; + GSList *properties; + GString *xml; + const DBusGObjectInfo *object_info; + DBusGLibWriteIterfaceData *data; + DBusGLibWriteInterfaceValues *values; + + name = key; - ids = g_signal_list_ids (type, &n_ids); - if (!n_ids) - return; + values = val; + methods = values->methods; + signals = values->signals; + properties = values->properties; - g_string_append (xml, " \n"); + data = user_data; + xml = data->xml; + object_info = data->object_info; + + g_string_append_printf (xml, " \n", name); /* FIXME: recurse to parent types ? */ - for (i = 0; i < n_ids; i++) + for (; methods; methods = methods->next) + { + DBusGMethodInfo *method; + const char *args; + method = methods->data; + + g_string_append_printf (xml, " \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, " \n", + name, type, arg_in ? "in" : "out"); + + } + g_string_append (xml, " \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); - g_signal_query (ids[i], &query); + id = g_signal_lookup (s, data->gtype); + g_assert (id != 0); - if (query.return_type) - continue; /* FIXME: these could be listed as methods ? */ + g_signal_query (id, &query); + g_assert (query.return_type == G_TYPE_NONE); - g_string_append (xml, " \n"); + g_string_append_printf (xml, " \n", signame); for (arg = 0; arg < query.n_params; arg++) { - int dbus_type = gtype_to_dbus_type (query.param_types[arg]); + char *dbus_type = _dbus_gtype_to_signature (query.param_types[arg]); + + g_assert (dbus_type != NULL); g_string_append (xml, " \n"); + g_free (dbus_type); } g_string_append (xml, " \n"); + g_free (s); + } + g_slist_free (values->signals); + + for (; properties; properties = properties->next) + { + const char *propname; + GParamSpec *spec; + char *dbus_type; + gboolean can_set; + gboolean can_get; + char *s; + + propname = properties->data; + spec = NULL; + + s = _dbus_gutils_wincaps_to_uscore (spec->name); + + 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 = ((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, " \n"); + } + + g_free (dbus_type); + g_free (s); + + g_string_append (xml, " \n"); } + g_slist_free (values->properties); + g_free (values); g_string_append (xml, " \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) +{ + const DBusGObjectInfo *info; + DBusGLibWriteIterfaceData data; + int i; + GHashTable *interfaces; + DBusGLibWriteInterfaceValues *values; + const char *propsig; + + info = lookup_object_info (object); + + g_assert (info != NULL); + + /* Gather a list of all interfaces, indexed into their methods */ + interfaces = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < info->n_method_infos; i++) + { + const char *method_name; + const char *method_interface; + const char *method_args; + const DBusGMethodInfo *method; + + method = &(info->method_infos[i]); + + method_interface = method_interface_from_object_info (info, method); + method_name = method_name_from_object_info (info, method); + method_args = method_arg_info_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) + { + const char *iface; + const char *signame; + + propsig = propsig_iterate (propsig, &iface, &signame); + + values = lookup_values (interfaces, iface); + values->signals = g_slist_prepend (values->signals, (gpointer) signame); + } + + propsig = info->exported_properties; + while (*propsig) + { + const char *iface; + const char *propname; + + propsig = propsig_iterate (propsig, &iface, &propname); + + values = lookup_values (interfaces, iface); + values->properties = g_slist_prepend (values->properties, (gpointer) propname); + } + + memset (&data, 0, sizeof (data)); + data.xml = xml; + data.gtype = G_TYPE_FROM_INSTANCE (object); + data.object_info = info; + g_hash_table_foreach (interfaces, write_interface, &data); + + g_hash_table_destroy (interfaces); +} + static DBusHandlerResult handle_introspect (DBusConnection *connection, DBusMessage *message, @@ -290,11 +589,33 @@ handle_introspect (DBusConnection *connection, xml = g_string_new (NULL); - introspect_signals (G_OBJECT_TYPE (object), xml); - introspect_properties (object, xml); - + g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + g_string_append (xml, "\n"); + /* We are introspectable, though I guess that was pretty obvious */ + g_string_append_printf (xml, " \n", DBUS_INTERFACE_INTROSPECTABLE); + g_string_append (xml, " \n"); + g_string_append_printf (xml, " \n", DBUS_TYPE_STRING_AS_STRING); + g_string_append (xml, " \n"); + g_string_append (xml, " \n"); + + /* We support get/set properties */ + g_string_append_printf (xml, " \n", DBUS_INTERFACE_PROPERTIES); + g_string_append (xml, " \n"); + g_string_append_printf (xml, " \n", DBUS_TYPE_STRING_AS_STRING); + g_string_append_printf (xml, " \n", DBUS_TYPE_STRING_AS_STRING); + g_string_append_printf (xml, " \n", DBUS_TYPE_VARIANT_AS_STRING); + g_string_append (xml, " \n"); + g_string_append (xml, " \n"); + g_string_append_printf (xml, " \n", DBUS_TYPE_STRING_AS_STRING); + g_string_append_printf (xml, " \n", DBUS_TYPE_STRING_AS_STRING); + g_string_append_printf (xml, " \n", DBUS_TYPE_VARIANT_AS_STRING); + g_string_append (xml, " \n"); + g_string_append (xml, " \n"); + + introspect_interfaces (object, xml); + /* Append child nodes */ for (i = 0; children[i]; i++) g_string_append_printf (xml, " \n", @@ -307,12 +628,12 @@ handle_introspect (DBusConnection *connection, if (ret == NULL) g_error ("Out of memory"); - dbus_message_append_args (message, + dbus_message_append_args (ret, DBUS_TYPE_STRING, &xml->str, DBUS_TYPE_INVALID); - dbus_connection_send (connection, message, NULL); - dbus_message_unref (message); + dbus_connection_send (connection, ret, NULL); + dbus_message_unref (ret); g_string_free (xml, TRUE); @@ -322,23 +643,24 @@ handle_introspect (DBusConnection *connection, } static DBusMessage* -set_object_property (DBusConnection *connection, - DBusMessage *message, - GObject *object, - GParamSpec *pspec) +set_object_property (DBusConnection *connection, + DBusMessage *message, + DBusMessageIter *iter, + GObject *object, + GParamSpec *pspec) { GValue value = { 0, }; DBusMessage *ret; - DBusMessageIter iter; + DBusMessageIter sub; + DBusGValueMarshalCtx context; - dbus_message_iter_init (message, &iter); + dbus_message_iter_recurse (iter, &sub); - /* The g_object_set_property() will transform some types, e.g. it - * will let you use a uchar to set an int property etc. Note that - * any error in value range or value conversion will just - * g_warning(). These GObject skels are not for secure applications. - */ - if (dbus_gvalue_demarshal (&iter, &value)) + 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, @@ -369,7 +691,7 @@ get_object_property (DBusConnection *connection, GParamSpec *pspec) { GType value_type; - GValue value; + GValue value = {0, }; DBusMessage *ret; DBusMessageIter iter; @@ -384,9 +706,9 @@ get_object_property (DBusConnection *connection, value_type = G_VALUE_TYPE (&value); - dbus_message_append_iter_init (message, &iter); + dbus_message_iter_init_append (message, &iter); - if (!dbus_gvalue_marshal (&iter, &value)) + if (!_dbus_gvalue_marshal (&iter, &value)) { dbus_message_unref (ret); ret = dbus_message_new_error (message, @@ -397,57 +719,573 @@ get_object_property (DBusConnection *connection, return ret; } -static DBusHandlerResult -gobject_message_function (DBusConnection *connection, - DBusMessage *message, - void *user_data) +static gboolean +lookup_object_and_method (GObject *object, + DBusMessage *message, + const DBusGObjectInfo **object_ret, + const DBusGMethodInfo **method_ret) { - const DBusGObjectInfo *info; - GParamSpec *pspec; - GObject *object; + const char *interface; const char *member; - gboolean setter; - gboolean getter; - char *s; + const char *signature; + gboolean ret; + const DBusGObjectInfo *info; + int i; - object = G_OBJECT (user_data); + interface = dbus_message_get_interface (message); + member = dbus_message_get_member (message); + signature = dbus_message_get_signature (message); + ret = FALSE; - if (dbus_message_is_method_call (message, - DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE, - "Introspect")) - return handle_introspect (connection, message, object); + info = lookup_object_info (object); + *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; + return TRUE; + } + g_free (expected_signature); + } - member = dbus_message_get_member (message); + return ret; +} - /* Try the metainfo, which lets us invoke methods */ +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; - g_static_mutex_lock (&info_hash_mutex); - /* FIXME this needs to walk up the inheritance tree, not - * just look at the most-derived class - */ - info = g_hash_table_lookup (info_hash, - G_OBJECT_GET_CLASS (object)); - g_static_mutex_unlock (&info_hash_mutex); + domain_str = object_error_domain_prefix_from_object_info (object_info); + code_str = object_error_code_from_object_info (object_info, domain, code); - if (info != NULL) + 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 no metainfo, we can still do properties and signals - * via standard GLib introspection - */ - setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't' && member[3] == '_'); - getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_'); + if (info) + { + GEnumValue *value; + GEnumClass *klass; - if (!(setter || getter)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + klass = g_type_class_ref (info->code_enum); + value = g_enum_get_value (klass, code); + g_type_class_unref (klass); - s = wincaps_to_uscore (&member[4]); + domain_str = info->default_iface; + code_str = value->value_nick; + } + } - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), + if (!domain_str) + domain_str = msg_interface; + + if (!domain_str || !code_str) + { + /* If we can't map it sensibly, make up an error name */ + char *domain_from_quark; + + dbus_error_name = g_string_new ("org.freedesktop.DBus.GLib.UnmappedError."); + + domain_from_quark = uscore_to_wincaps (g_quark_to_string (domain)); + g_string_append (dbus_error_name, domain_from_quark); + g_free (domain_from_quark); + + g_string_append_printf (dbus_error_name, ".Code%d", code); + } + else + { + dbus_error_name = g_string_new (domain_str); + g_string_append_c (dbus_error_name, '.'); + g_string_append (dbus_error_name, code_str); + } + + return g_string_free (dbus_error_name, FALSE); +} + +static DBusMessage * +gerror_to_dbus_error_message (const DBusGObjectInfo *object_info, + DBusMessage *message, + 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 = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg); + g_free (error_msg); + } + else + { + if (error->domain == DBUS_GERROR) + reply = dbus_message_new_error (message, + dbus_g_error_get_name (error), + 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 = dbus_message_new_error (message, error_name, error->message); + g_free (error_name); + } + } + return reply; +} + +/** + * 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 */ +}; + +static DBusHandlerResult +invoke_object_method (GObject *object, + const DBusGObjectInfo *object_info, + const DBusGMethodInfo *method, + DBusConnection *connection, + DBusMessage *message) +{ + gboolean had_error, call_only; + 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; + DBusHandlerResult result; + DBusMessage *reply; + gboolean have_retval; + gboolean retval_signals_error; + gboolean retval_is_synthetic; + gboolean retval_is_constant; + const char *arg_metadata; + + gerror = NULL; + + /* Determine whether or not this method should be invoked in a new + thread + */ + if (strcmp (string_table_lookup (get_method_data (object_info, method), 2), "A") == 0) + call_only = TRUE; + else + call_only = FALSE; + + 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.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 = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error->message); + dbus_connection_send (connection, reply, NULL); + 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 (call_only) + { + 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; + 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 (call_only) + { + result = DBUS_HANDLER_RESULT_HANDLED; + goto done; + } + if (retval_signals_error) + had_error = _dbus_gvalue_signals_error (&return_value); + else + had_error = FALSE; + + if (!had_error) + { + DBusMessageIter iter; + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + goto nomem; + + /* 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 (!_dbus_gvalue_marshal (&iter, &return_value)) + goto nomem; + 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 (!_dbus_gvalue_marshal (&iter, &gvalue)) + goto nomem; + /* 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 + reply = gerror_to_dbus_error_message (object_info, message, gerror); + + if (reply) + { + dbus_connection_send (connection, reply, NULL); + dbus_message_unref (reply); + } + + result = DBUS_HANDLER_RESULT_HANDLED; + done: + g_free (in_signature); + if (!call_only) + { + g_array_free (out_param_values, TRUE); + g_value_array_free (out_param_gvalues); + } + g_value_array_free (value_array); + return result; + nomem: + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto done; +} + +static DBusHandlerResult +gobject_message_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GParamSpec *pspec; + GObject *object; + gboolean setter; + gboolean getter; + char *s; + const char *wincaps_propname; + /* const char *wincaps_propiface; */ + DBusMessageIter iter; + const DBusGMethodInfo *method; + const DBusGObjectInfo *object_info; + + object = G_OBJECT (user_data); + + 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 */ + 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 + */ + getter = FALSE; + setter = 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; + + if (!(setter || getter)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + dbus_message_iter_init (message, &iter); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) + { + g_warning ("Property get or set does not have an interface string as first arg\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + /* We never use the interface name; if we did, we'd need to + * remember that it can be empty string for "pick one for me" + */ + /* dbus_message_iter_get_basic (&iter, &wincaps_propiface); */ + dbus_message_iter_next (&iter); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) + { + g_warning ("Property get or set does not have a property name string as second arg\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_message_iter_get_basic (&iter, &wincaps_propname); + dbus_message_iter_next (&iter); + + s = _dbus_gutils_wincaps_to_uscore (wincaps_propname); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), s); g_free (s); @@ -458,11 +1296,18 @@ gobject_message_function (DBusConnection *connection, if (setter) { - ret = set_object_property (connection, message, + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT) + { + g_warning ("Property set does not have a variant value as third arg\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + ret = set_object_property (connection, message, &iter, object, pspec); + dbus_message_iter_next (&iter); } else if (getter) - { + { ret = get_object_property (connection, message, object, pspec); } @@ -474,6 +1319,9 @@ gobject_message_function (DBusConnection *connection, g_assert (ret != NULL); + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) + g_warning ("Property get or set had too many arguments\n"); + dbus_connection_send (connection, ret, NULL); dbus_message_unref (ret); return DBUS_HANDLER_RESULT_HANDLED; @@ -482,12 +1330,187 @@ gobject_message_function (DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static DBusObjectPathVTable gobject_dbus_vtable = { +static const DBusObjectPathVTable gobject_dbus_vtable = { gobject_unregister_function, gobject_message_function, NULL }; +typedef struct { + GClosure closure; + DBusGConnection *connection; + GObject *object; + const char *signame; + const char *sigiface; +} DBusGSignalClosure; + +static GClosure * +dbus_g_signal_closure_new (DBusGConnection *connection, + GObject *object, + const char *signame, + const char *sigiface) +{ + DBusGSignalClosure *closure; + + closure = (DBusGSignalClosure*) g_closure_new_simple (sizeof (DBusGSignalClosure), NULL); + + closure->connection = dbus_g_connection_ref (connection); + closure->object = object; + closure->signame = signame; + closure->sigiface = sigiface; + return (GClosure*) closure; +} + +static void +dbus_g_signal_closure_finalize (gpointer data, + GClosure *closure) +{ + DBusGSignalClosure *sigclosure = (DBusGSignalClosure *) closure; + + dbus_g_connection_unref (sigclosure->connection); +} + +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; + DBusMessage *signal; + DBusMessageIter iter; + guint i; + const char *path; + + sigclosure = (DBusGSignalClosure *) closure; + + g_assert (retval == NULL); + + path = _dbus_gobject_get_path (sigclosure->object); + + g_assert (path != NULL); + + signal = dbus_message_new_signal (path, + sigclosure->sigiface, + sigclosure->signame); + if (!signal) + { + g_error ("out of memory"); + return; + } + + 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; + } + } + dbus_connection_send (DBUS_CONNECTION_FROM_G_CONNECTION (sigclosure->connection), + signal, NULL); + out: + dbus_message_unref (signal); +} + +static void +export_signals (DBusGConnection *connection, const DBusGObjectInfo *info, GObject *object) +{ + GType gtype; + const char *sigdata; + const char *iface; + const char *signame; + + gtype = G_TYPE_FROM_INSTANCE (object); + + sigdata = info->exported_signals; + + while (*sigdata != '\0') + { + guint id; + GSignalQuery query; + GClosure *closure; + char *s; + + sigdata = propsig_iterate (sigdata, &iface, &signame); + + 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 (connection, object, signame, (char*) iface); + g_closure_set_marshal (closure, signal_emitter_marshaller); + + g_signal_connect_closure_by_id (object, + id, + 0, + closure, + FALSE); + + g_closure_add_finalize_notifier (closure, NULL, + dbus_g_signal_closure_finalize); + g_free (s); + } +} + +#include "dbus-glib-error-switch.h" + +void +dbus_set_g_error (GError **gerror, + DBusError *error) +{ + int code; + + code = dbus_error_to_gerror_code (error->name); + if (code != DBUS_GERROR_REMOTE_EXCEPTION) + g_set_error (gerror, DBUS_GERROR, + code, + "%s", + error->message); + else + g_set_error (gerror, DBUS_GERROR, + code, + "%s%c%s", + error->message ? error->message : "", + '\0', + error->name); +} + +static void +dbus_g_error_info_free (gpointer p) +{ + DBusGErrorInfo *info; + + info = p; + + g_free (info->default_iface); + g_free (info); +} + /** @} */ /* end of internals */ /** @@ -496,7 +1519,7 @@ static DBusObjectPathVTable gobject_dbus_vtable = { */ /** - * Install introspection information about the given object class + * 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 @@ -506,25 +1529,76 @@ static DBusObjectPathVTable gobject_dbus_vtable = { * object registered with dbus_g_connection_register_g_object() can have * their methods invoked remotely. * - * @param object_class class struct of the object + * @param object_type GType for the object * @param info introspection data generated by dbus-glib-tool */ void -dbus_g_object_class_install_info (GObjectClass *object_class, - const DBusGObjectInfo *info) +dbus_g_object_type_install_info (GType object_type, + const DBusGObjectInfo *info) +{ + g_return_if_fail (G_TYPE_IS_CLASSED (object_type)); + + _dbus_g_value_types_init (); + + g_type_set_qdata (object_type, + dbus_g_object_type_dbus_metadata_quark (), + (gpointer) info); +} + +/** + * Register a GError domain and set of codes with D-BUS. You must + * have created a GEnum for the error codes. This function will not + * be needed with an introspection-capable GLib. + * + * @param domain the GError domain + * @param default_iface the D-BUS interface used for error values by default, or #NULL + * @param code_enum a GType for a GEnum of the error codes + */ +void +dbus_g_error_domain_register (GQuark domain, + const char *default_iface, + GType code_enum) { - g_return_if_fail (G_IS_OBJECT_CLASS (object_class)); + 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); - g_static_mutex_lock (&info_hash_mutex); + if (error_metadata == NULL) + g_datalist_init (&error_metadata); - if (info_hash == NULL) + 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_hash = g_hash_table_new (NULL, NULL); /* direct hash */ + 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_hash_table_replace (info_hash, object_class, (void*) info); + g_static_rw_lock_writer_unlock (&globals_lock); +} - g_static_mutex_unlock (&info_hash_mutex); +static void +unregister_gobject (DBusGConnection *connection, GObject *dead) +{ + char *path; + path = g_object_steal_data (dead, "dbus_glib_object_path"); + dbus_connection_unregister_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection), path); + g_free (path); } /** @@ -545,26 +1619,427 @@ dbus_g_connection_register_g_object (DBusGConnection *connection, const char *at_path, GObject *object) { + const DBusGObjectInfo *info; g_return_if_fail (connection != NULL); g_return_if_fail (at_path != NULL); g_return_if_fail (G_IS_OBJECT (object)); + info = lookup_object_info (object); + if (info == NULL) + { + g_warning ("No introspection data registered for object class \"%s\"", + g_type_name (G_TYPE_FROM_INSTANCE (object))); + return; + } + if (!dbus_connection_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &gobject_dbus_vtable, object)) - g_error ("Failed to register GObject with DBusConnection"); + { + g_error ("Failed to register GObject with DBusConnection"); + return; + } - /* FIXME set up memory management (so we break the - * registration if object or connection vanishes) - */ + export_signals (connection, info, object); + + g_object_set_data (object, "dbus_glib_object_path", g_strdup (at_path)); + g_object_weak_ref (object, (GWeakNotify)unregister_gobject, connection); +} + +GObject * +dbus_g_connection_lookup_g_object (DBusGConnection *connection, + const char *at_path) +{ + gpointer ret; + if (!dbus_connection_get_object_path_data (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &ret)) + return NULL; + return ret; +} + +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; +} + +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; +} + +/** + * 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. + * + * @param marshaller a GClosureMarshal to be used for invocation + * @param rettype a GType for the return type of the function + * @param ... The parameter GTypes, followed by G_TYPE_INVALID + */ +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); +} + +/** + * Register a GClosureMarshal to be used for signal invocations. + * See also #dbus_g_object_register_marshaller + * + * @param marshaller a GClosureMarshal to be used for invocation + * @param rettype a GType for the return type of the function + * @param n_types number of function parameters + * @param types a C array of GTypes values + */ +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, + g_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); +} + +/** + * 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). + * + * @param context the method context + * + * @return the unique name of teh sender + */ +gchar * +dbus_g_method_get_sender (DBusGMethodInvocation *context) +{ + const gchar *sender; + + sender = dbus_message_get_sender (dbus_g_message_get_message (context->message)); + + if (sender == NULL) + return NULL; + + return strdup (sender); +} + +/** + * 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 + * + * @param context the method context + */ +DBusMessage * +dbus_g_method_get_reply (DBusGMethodInvocation *context) +{ + return dbus_message_new_method_return (dbus_g_message_get_message (context->message)); +} + +/** + * 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 + * + * @param context the method context + * @param reply the reply message, will be unreffed + */ +void +dbus_g_method_send_reply (DBusGMethodInvocation *context, DBusMessage *reply) +{ + dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL); + dbus_message_unref (reply); + + dbus_g_connection_unref (context->connection); + dbus_g_message_unref (context->message); + g_free (context); +} + + +/** + * Send a return message for a given method invocation, with arguments. + * This function also frees the sending context. + * + * @param context the method context + */ +void +dbus_g_method_return (DBusGMethodInvocation *context, ...) +{ + DBusMessage *reply; + DBusMessageIter iter; + va_list args; + char *out_sig; + GArray *argsig; + guint i; + + reply = dbus_message_new_method_return (dbus_g_message_get_message (context->message)); + 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(error); + g_free (error); + } + _dbus_gvalue_marshal (&iter, &value); + } + va_end (args); + + dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL); + dbus_message_unref (reply); + + dbus_g_connection_unref (context->connection); + dbus_g_message_unref (context->message); + g_free (context); + g_free (out_sig); +} + +/** + * Send a error message for a given method invocation. + * This function also frees the sending context. + * + * @param context the method context + * @param error the error to send. + */ +void +dbus_g_method_return_error (DBusGMethodInvocation *context, GError *error) +{ + DBusMessage *reply; + reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error); + dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL); + dbus_message_unref (reply); + g_free (context); } /** @} */ /* end of public API */ +const char * _dbus_gobject_get_path (GObject *obj) +{ + return g_object_get_data (obj, "dbus_glib_object_path"); +} + #ifdef DBUS_BUILD_TESTS #include +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" +}; + + /** * @ingroup DBusGLibInternals * Unit test for GLib GObject integration ("skeletons") @@ -574,6 +2049,16 @@ 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" }, @@ -584,13 +2069,102 @@ _dbus_gobject_test (const char *test_data_dir) /* { "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 = propsig_iterate (sigdata, &iface, &signame); + g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.MyObject")); + g_assert (!strcmp (signame, "Frobnicate")); + g_assert (*sigdata != '\0'); + sigdata = propsig_iterate (sigdata, &iface, &signame); + g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject")); + g_assert (!strcmp (signame, "Sig0")); + g_assert (*sigdata != '\0'); + sigdata = propsig_iterate (sigdata, &iface, &signame); + g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject")); + g_assert (!strcmp (signame, "Sig1")); + g_assert (*sigdata != '\0'); + sigdata = propsig_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 = wincaps_to_uscore (name_pairs[i].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)