diff -r 000000000000 -r e4d67989cc36 ofdbus/dbus-glib/dbus/dbus-gobject.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofdbus/dbus-glib/dbus/dbus-gobject.c Tue Feb 02 02:01:42 2010 +0200 @@ -0,0 +1,2391 @@ +/* -*- 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 + * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SYMBIAN32__ +#include +#else +#include "config.h" +#endif //__SYMBIAN32__ +#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 + +#ifdef __SYMBIAN32__ +#include "libdbus_glib_wsd_solution.h" +#endif + + +typedef struct +{ + char *default_iface; + GType code_enum; +} DBusGErrorInfo; + + + +#if EMULATOR +GET_STATIC_VAR_FROM_TLS(marshal_table,dbus_gobject,GHashTable *) +#define marshal_table (*GET_DBUS_WSD_VAR_NAME(marshal_table,dbus_gobject,s)()) +GET_STATIC_VAR_FROM_TLS(error_metadata,dbus_gobject,GData *) +#define error_metadata (*GET_DBUS_WSD_VAR_NAME(error_metadata,dbus_gobject,s)()) +GET_STATIC_VAR_FROM_TLS(globals_lock,dbus_gobject,GStaticRWLock ) +#define globals_lock (*GET_DBUS_WSD_VAR_NAME(globals_lock,dbus_gobject,s)()) + +#else +static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT; + +static GHashTable *marshal_table = NULL; +static GData *error_metadata = NULL; + +#endif + + + + + +static char* +uscore_to_wincaps (const char *uscore) +{ + const char *p; + GString *str; + gboolean last_was_uscore; + + last_was_uscore = TRUE; + + str = g_string_new (NULL); + p = uscore; + while (p && *p) + { + if (*p == '-' || *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); +} + +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; + +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) + #ifndef __SYMBIAN32__ + *retval = FALSE; + #else + *retval = (RetvalType)FALSE; + #endif + } + + 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 * +propsig_iterate (const char *data, const char **iface, const char **name) +{ + *iface = data; + + data = string_table_next (data); + *name = data; + + return string_table_next (data); +} + +#if EMULATOR +GET_STATIC_VAR_FROM_TLS(quark,dbus_gobject,GQuark ) +#define quark (*GET_DBUS_WSD_VAR_NAME(quark,dbus_gobject,s)()) + +#endif + +static GQuark +dbus_g_object_type_dbus_metadata_quark (void) +{ +#ifndef EMULATOR + static GQuark quark; +#endif + + if (!quark) + quark = g_quark_from_static_string ("DBusGObjectTypeDBusMetadataQuark"); + return quark; +} + +static GList * +lookup_object_info (GObject *object) +{ + GType *interfaces, *p; + GList *info_list = NULL; + 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) + info_list = g_list_prepend (info_list, (gpointer) info); + } + + 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) + info_list = g_list_prepend (info_list, (gpointer) info); + } + + /* if needed only: + return g_list_reverse (info_list); + */ + + return info_list; +} + +static void +gobject_unregister_function (DBusConnection *connection, + void *user_data) +{ +#ifdef WINSCW + + GObject *object; + + object = G_OBJECT (user_data); +#endif + /* FIXME */ + +} + +typedef struct +{ + 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, " \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, " \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); + + 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, " \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, " \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 (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 = ((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_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) +{ + 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++) + { + #ifdef WINSCW + const char *method_name; + const char *method_args; + #endif + const char *method_interface; + + + const DBusGMethodInfo *method; + + method = &(info->method_infos[i]); + + method_interface = method_interface_from_object_info (info, method); + #ifdef WINSCW + method_name = method_name_from_object_info (info, method); + + method_args = method_arg_info_from_object_info (info, method); + #endif + 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); + } + + 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)) + g_error ("Out of memory"); + + xml = g_string_new (NULL); + + 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", + children[i]); + + /* Close the XML, and send it to the requesting app */ + g_string_append (xml, "\n"); + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + g_error ("Out of memory"); + + dbus_message_append_args (ret, + DBUS_TYPE_STRING, &xml->str, + DBUS_TYPE_INVALID); + + dbus_connection_send (connection, ret, NULL); + 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.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 = dbus_message_new_method_return (message); + if (ret == NULL) + g_error ("out of memory"); + } + else + { + ret = dbus_message_new_error (message, + DBUS_ERROR_INVALID_ARGS, + "Argument's D-BUS type can't be converted to a GType"); + if (ret == NULL) + g_error ("out of memory"); + } + + return ret; +} + +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; + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + g_error ("out of memory"); + + + 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); + return ret; + } + + dbus_message_iter_init_append (ret, &iter); + if (!dbus_message_iter_open_container (&iter, + DBUS_TYPE_VARIANT, + variant_sig, + &subiter)) + { + g_free (variant_sig); + g_value_unset (&value); + return ret; + } + + if (!_dbus_gvalue_marshal (&subiter, &value)) + { + dbus_message_unref (ret); + ret = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_METHOD, + "Can't convert GType of object property to a D-BUS type"); + } + + dbus_message_iter_close_container (&iter, &subiter); + + g_value_unset (&value); + g_free (variant_sig); + + 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; + code_str = value->value_nick; + } + } + + 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; +} + +/** + * SECTION:dbus-gmethod + * @short_description: GMethod Info & Invocation + * @see_also: #DBusGMessage + * @stability: Stable + * + * These types are used to call methods on #GObject objects. + */ + +/** + * 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, + #ifdef WINSCW + method->function); + #else + (gpointer)method->function); + #endif + + 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); + + if (pspec != NULL) + { + DBusMessage *ret; + + if (setter) + { + 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); + } + else + { + g_assert_not_reached (); + ret = NULL; + } + + 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; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +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 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 = 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" + +/** + * dbus_set_g_error: + * @gerror: an error + * @error: a #DBusError + * + * FIXME + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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); +} + +/** + * SECTION:dbus-gobject + * @short_description: Exporting a #GObject remotely + * @see_also: #GObject + * @stability: Stable + * + * FIXME + */ + +/** + * 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. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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 D-BUS interface used for error values by default, or #NULL + * @code_enum: a #GType for a #GEnum of the error codes + * + * 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. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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); +} + +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); +} + +/** + * 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 g_object_class_install_info(). + * + * The registration will be cancelled if either the #DBusConnection or + * the #GObject gets finalized. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +void +dbus_g_connection_register_g_object (DBusGConnection *connection, + const char *at_path, + GObject *object) +{ + GList *info_list; + g_return_if_fail (connection != NULL); + g_return_if_fail (at_path != NULL); + g_return_if_fail (G_IS_OBJECT (object)); + + 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; + } + + 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"); + return; + } + + export_signals (connection, info_list, object); + g_list_free (info_list); + + g_object_set_data (object, "dbus_glib_object_path", g_strdup (at_path)); + g_object_weak_ref (object, (GWeakNotify)unregister_gobject, connection); +} + +/** + * dbus_g_connection_lookup_g_object: + * @connection: a #DBusGConnection + * @at_path: path + * + * FIXME + * + * Returns: the object at path @at_path + */ + #ifdef __SYMBIAN32__ +EXPORT_C +#endif +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; +} + +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) + #ifdef WINSCW + ret = g_hash_table_lookup (marshal_table, &sig); + #else + ret = (GClosureMarshal)g_hash_table_lookup (marshal_table, &sig); + #endif + 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. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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 + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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]); +#ifdef WINSCW + g_hash_table_insert (marshal_table, sig, marshaller); + #else + g_hash_table_insert (marshal_table, sig, (gpointer)marshaller); + #endif + + 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. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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); +} + +/** + * 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 + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +DBusMessage * +dbus_g_method_get_reply (DBusGMethodInvocation *context) +{ + return dbus_message_new_method_return (dbus_g_message_get_message (context->message)); +} + +/** + * dbus_g_method_send_reply: + * Send a manually created reply message + * @context: the method context + * @reply: the reply message, will be unreffed + * + * Used as a sidedoor when you can't generate dbus values + * of the correct type due to glib binding limitations + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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); +} + + +/** + * dbus_g_method_return: + * @context: the method context + * + * Send a return message for a given method invocation, with arguments. + * This function also frees the sending context. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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); + g_array_free (argsig, TRUE); +} + +/** + * 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. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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); +} + +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") + * Returns: #TRUE on success. + */ + #ifdef __SYMBIAN32__ + EXPORT_C + #endif +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 = 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 = _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 */