/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gobject.c Exporting a GObject remotely
 *
 * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
 * Copyright (C) 2005 Nokia
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <config.h>
#include <gobject/gvaluecollector.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "dbus-gtest.h"
#include "dbus-gutils.h"
#include "dbus-gobject.h"
#include "dbus-gsignature.h"
#include "dbus-gvalue.h"
#include "dbus-gmarshal.h"
#include "dbus-gvalue-utils.h"
#include <string.h>

#include <gio/gio.h>

G_GNUC_NORETURN static void
oom (const gchar *explanation)
{
  g_error ("%s", explanation == NULL ? "Out of memory" : explanation);
  g_assert_not_reached ();
}

static DBusMessage *
reply_or_die (DBusMessage *in_reply_to)
{
  DBusMessage *reply;

  g_return_val_if_fail (in_reply_to != NULL, NULL);

  reply = dbus_message_new_method_return (in_reply_to);

  if (reply == NULL)
    oom ("dbus_message_new_method_return failed: out of memory?");

  return reply;
}

static DBusMessage *
error_or_die (DBusMessage *in_reply_to,
    const gchar *error_name,
    const gchar *error_message)
{
  DBusMessage *reply;

  g_return_val_if_fail (in_reply_to != NULL, NULL);
  /* error names are syntactically the same as interface names */
  g_return_val_if_fail (g_dbus_is_interface_name (error_name), NULL);
  g_return_val_if_fail (g_utf8_validate (error_message, -1, NULL), NULL);

  reply = dbus_message_new_error (in_reply_to, error_name, error_message);

  if (reply == NULL)
    oom ("dbus_message_new_error failed: out of memory?");

  return reply;
}

static void
connection_send_or_die (DBusConnection *connection,
    DBusMessage *message)
{
  g_return_if_fail (connection != NULL);
  g_return_if_fail (message != NULL);

  if (!dbus_connection_send (connection, message, NULL))
    oom ("dbus_connection_send failed: out of memory?");
}

static char *lookup_property_name (GObject    *object,
                                   const char *wincaps_propiface,
                                   const char *requested_propname);

typedef struct
{
  char *default_iface;
  GType code_enum;
} DBusGErrorInfo;

static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT;
/* See comments in check_property_access */
static gboolean disable_legacy_property_access = FALSE;
static GHashTable *marshal_table = NULL;
static GData *error_metadata = NULL;

static char*
uscore_to_wincaps_full (const char *uscore,
                        gboolean    uppercase_first,
                        gboolean    strip_underscores)
{
  const char *p;
  GString *str;
  gboolean last_was_uscore;

  last_was_uscore = uppercase_first;
  
  str = g_string_new (NULL);
  p = uscore;
  while (p && *p)
    {
      if (*p == '-' || (strip_underscores && *p == '_'))
        {
          last_was_uscore = TRUE;
        }
      else
        {
          if (last_was_uscore)
            {
              g_string_append_c (str, g_ascii_toupper (*p));
              last_was_uscore = FALSE;
            }
          else
            g_string_append_c (str, *p);
        }
      ++p;
    }

  return g_string_free (str, FALSE);
}

/* Ugly yes - but we have to accept strings from both formats */
static gboolean
compare_strings_ignoring_uscore_vs_dash (const char *a, const char *b)
{
  guint i;

  for (i = 0; a[i] && b[i]; i++)
    {
      if ((a[i] == '-' && b[i] == '_')
          || (a[i] == '_' && b[i] == '-'))
        continue;
      if (a[i] != b[i])
        return FALSE;
    }
  return (a[i] == '\0') && (b[i] == '\0');
}

static char *
uscore_to_wincaps (const char *uscore)
{
  return uscore_to_wincaps_full (uscore, TRUE, TRUE);
}

static const char *
string_table_next (const char *table)
{
  return (table + (strlen (table) + 1));
}

static const char *
string_table_lookup (const char *table, int index)
{
  const char *ret;

  ret = table;

  while (index--)
    ret = string_table_next (ret);

  return ret;
}

static const char *
get_method_data (const DBusGObjectInfo *object,
		 const DBusGMethodInfo *method)
{
  return object->data + method->data_offset;
}

static char *
object_error_domain_prefix_from_object_info (const DBusGObjectInfo *info)
{
  /* FIXME */
  return NULL;
}

static char *
object_error_code_from_object_info (const DBusGObjectInfo *info, GQuark domain, gint code)
{
  /* FIXME */
  return NULL;
}

static const char *
method_interface_from_object_info (const DBusGObjectInfo *object,
			      const DBusGMethodInfo *method)
{
  return string_table_lookup (get_method_data (object, method), 0);
}

static const char *
method_name_from_object_info (const DBusGObjectInfo *object,
			      const DBusGMethodInfo *method)
{
  return string_table_lookup (get_method_data (object, method), 1);
}

static const char *
method_arg_info_from_object_info (const DBusGObjectInfo *object,
				  const DBusGMethodInfo *method)
{
  return string_table_lookup (get_method_data (object, method), 3);/*RB was 2*/
}

typedef enum
{
  RETVAL_NONE,    
  RETVAL_NOERROR,    
  RETVAL_ERROR
} RetvalType;

/*
 * arg_iterate:
 * @data: a pointer to the beginning of an argument entry in a string table
 * @name: (out) (allow-none): used to return the name of the next argument
 * @in: (out) (allow-none): used to return %TRUE for an "in" argument or
 *    %FALSE for an "out" argument
 * @constval: (out) (allow-none): used to return %TRUE if the argument is
 *    an "out" argument and has the "C" (const) flag indicating that it
 *    should not be freed after it is returned; normally, "out" arguments
 *    are freed
 * @retval: (out) (allow-none): used to return %RETVAL_NONE if this
 *    D-Bus argument is not an "out" argument or is obtained like a C "out"
 *    parameter, %RETVAL_ERROR if this argument is obtained from the C
 *    return value and is also used to signal errors, or %RETVAL_NOERROR
 *    if this argument is obtained from the C return value and the method
 *    can never raise an error
 * @type: (out) (allow-none): used to return the D-Bus signature of this
 *    argument
 *
 * The data format is:
 *
 * argument name
 * \0
 * direction: I or O
 * \0
 * if direction == "O":
 *     freeable? F or C
 *     \0
 *     retval? N, E or R
 *     \0
 * signature
 * \0
 *
 * If none of the arguments has @retval != %RETVAL_NONE, the method is
 * assumed to return a gboolean, which behaves like %RETVAL_ERROR but is
 * not sent over D-Bus at all.
 *
 * Returns: the value of @data to use for the next call, or a pointer to '\0'
 *    if this function must not be called again
 */
static const char *
arg_iterate (const char    *data,
	     const char   **name,
	     gboolean      *in,
	     gboolean      *constval,
	     RetvalType    *retval,
	     const char   **type)
{
  gboolean inarg;

  if (name)
    *name = data;

  data = string_table_next (data);
  switch (*data)
    {
    case 'I':
      inarg = TRUE;
      break;
    case 'O':
      inarg = FALSE;
      break;
    default:
      g_warning ("invalid arg direction '%c'", *data);
      inarg = FALSE;
      break;
    }
  if (in)
    *in = inarg;

  if (!inarg)
    {
      data = string_table_next (data);
      switch (*data)
	{
	case 'F':
	  if (constval)
	    *constval = FALSE;
	  break;
	case 'C':
	  if (constval)
	    *constval = TRUE;
	  break;
	default:
	  g_warning ("invalid arg const value '%c'", *data);
	  break;
	}
      data = string_table_next (data);
      switch (*data)
	{
	case 'N':
	  if (retval)
	    *retval = RETVAL_NONE;
	  break;
	case 'E':
	  if (retval)
	    *retval = RETVAL_ERROR;
	  break;
	case 'R':
	  if (retval)
	    *retval = RETVAL_NOERROR;
	  break;
	default:
	  g_warning ("invalid arg ret value '%c'", *data);
	  break;
	}
    }
  else
    {
      if (constval)
	*constval = FALSE;
      if (retval)
	*retval = FALSE;
    }
  
  data = string_table_next (data);
  if (type)
    *type = data;

  return string_table_next (data);
}

static char *
method_dir_signature_from_object_info (const DBusGObjectInfo *object,
				       const DBusGMethodInfo *method,
				       gboolean               in)
{
  const char *arg;
  GString *ret;

  arg = method_arg_info_from_object_info (object, method);

  ret = g_string_new (NULL);

  while (*arg)
    {
      const char *name;
      gboolean arg_in;
      const char *type;

      arg = arg_iterate (arg, &name, &arg_in, NULL, NULL, &type);

      if (arg_in == in)
	g_string_append (ret, type);
    }

  return g_string_free (ret, FALSE);
}

static char *
method_input_signature_from_object_info (const DBusGObjectInfo *object,
					 const DBusGMethodInfo *method)
{
  return method_dir_signature_from_object_info (object, method, TRUE);
}

static char *
method_output_signature_from_object_info (const DBusGObjectInfo *object,
					  const DBusGMethodInfo *method)
{
  return method_dir_signature_from_object_info (object, method, FALSE);
}

static const char *
signal_iterate (const char *data, const char **iface, const char **name)
{
  *iface = data;

  data = string_table_next (data);
  *name = data;

  return string_table_next (data);
}

static const char *
property_iterate (const char  *data,
                  int          format_version,
                  const char **iface,
                  const char **exported_name,
                  const char **name_uscored,
                  const char **access_type)
{
  *iface = data;

  data = string_table_next (data);
  *exported_name = data;

  data = string_table_next (data);
  if (format_version == 1)
    {
      *name_uscored = data;
      data = string_table_next (data);
      *access_type = data;
      return string_table_next (data);
    }
  else
    {
      /* This tells the caller they need to compute it */
      *name_uscored = NULL;
      /* We don't know here, however note that we will still check against the
       * readable/writable flags from GObject's metadata.
       */
      *access_type = "readwrite";
      return data;
    }
}

/**
 * property_info_from_object_info:
 * @object: introspection data
 * @interface_name: (allow-none): Expected interface name, or %NULL for any
 * @property_name: Expected property name (can use "-" or "_" as separator)
 * @access_type: (out): Can be one of "read", "write", "readwrite"
 *
 * Look up property introspection data for the given interface/name pair.
 *
 * Returns: %TRUE if property was found
 */
static gboolean
property_info_from_object_info (const DBusGObjectInfo  *object,
                                const char             *interface_name,
                                const char             *property_name,
                                const char            **access_type)
{
  const char *properties_iter;

  properties_iter = object->exported_properties;
  while (properties_iter != NULL && *properties_iter)
    {
      const char *cur_interface_name;
      const char *cur_property_name;
      const char *cur_uscore_property_name;
      const char *cur_access_type;


      properties_iter = property_iterate (properties_iter, object->format_version,
                                          &cur_interface_name, &cur_property_name,
                                          &cur_uscore_property_name, &cur_access_type);

      if (interface_name && strcmp (interface_name, cur_interface_name) != 0)
        continue;

      /* This big pile of ugly is necessary to support the matrix resulting from multiplying
       * (v0 data, v1 data) * (FooBar, foo-bar)
       * In v1 data we have both forms of string, so we do a comparison against both without
       * having to malloc.
       * For v0 data, we need to reconstruct the foo-bar form.
       *
       * Adding to the complexity is that we *also* have to ignore the distinction between
       * '-' and '_', because g_object_{get,set} does.
       */
      /* First, compare against the primary property name - no malloc required */
      if (!compare_strings_ignoring_uscore_vs_dash (property_name, cur_property_name))
        {
          if (cur_uscore_property_name != NULL
              && !compare_strings_ignoring_uscore_vs_dash (property_name, cur_uscore_property_name))
            continue;
          else
            {
              /* v0 metadata, construct uscore */
              char *tmp_uscored;
              gboolean matches;
              tmp_uscored = _dbus_gutils_wincaps_to_uscore (cur_property_name);
              matches = compare_strings_ignoring_uscore_vs_dash (property_name, tmp_uscored);
              g_free (tmp_uscored);
              if (!matches)
                continue;
            }
        }

      *access_type = cur_access_type;
      return TRUE;
    }
  return FALSE;
}

static GQuark
dbus_g_object_type_dbus_metadata_quark (void)
{
  static GQuark quark;

  if (!quark)
    quark = g_quark_from_static_string ("DBusGObjectTypeDBusMetadataQuark");
  return quark;
}

/* Iterator function should return FALSE to stop iteration, TRUE to continue */
typedef gboolean (*ForeachObjectInfoFn) (const DBusGObjectInfo *info,
                                         GType                 gtype,
                                         gpointer              user_data);

static void
foreach_object_info (GObject *object,
		     ForeachObjectInfoFn callback,
		     gpointer user_data)
{
  GType *interfaces, *p;
  const DBusGObjectInfo *info;
  GType classtype;

  interfaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (object), NULL);

  for (p = interfaces; *p != 0; p++)
    {
      info = g_type_get_qdata (*p, dbus_g_object_type_dbus_metadata_quark ());
      if (info != NULL && info->format_version >= 0)
        {
	  if (!callback (info, *p, user_data))
	    break;
	}
    }

  g_free (interfaces);

  for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
    {
      info = g_type_get_qdata (classtype, dbus_g_object_type_dbus_metadata_quark ());
      if (info != NULL && info->format_version >= 0)
	{
	  if (!callback (info, classtype, user_data))
	    break;
	}
    }

}

static gboolean
lookup_object_info_cb (const DBusGObjectInfo *info,
                       GType gtype,
		       gpointer user_data)
{
  GList **list = (GList **) user_data;

  *list = g_list_prepend (*list, (gpointer) info);
  return TRUE;
}

static GList *
lookup_object_info (GObject *object)
{
  GList *info_list = NULL;

  foreach_object_info (object, lookup_object_info_cb, &info_list);

  return info_list;
}

typedef struct {
  const char *iface;
  const DBusGObjectInfo *info;
  gboolean fallback;
  GType iface_type;
} LookupObjectInfoByIfaceData;

static gboolean
lookup_object_info_by_iface_cb (const DBusGObjectInfo *info,
				GType gtype,
				gpointer user_data)
{
  LookupObjectInfoByIfaceData *lookup_data = (LookupObjectInfoByIfaceData *) user_data;

  /* If interface is not specified, choose the first info */
  if (lookup_data->fallback && (!lookup_data->iface || strlen (lookup_data->iface) == 0))
    {
      lookup_data->info = info;
      lookup_data->iface_type = gtype;
    }
  else if (info->exported_properties && !strcmp (info->exported_properties, lookup_data->iface))
    {
      lookup_data->info = info;
      lookup_data->iface_type = gtype;
    }

  return !lookup_data->info;
}

static const DBusGObjectInfo *
lookup_object_info_by_iface (GObject     *object,
			     const char  *iface,
			     gboolean     fallback,
			     GType       *out_iface_type)
{
  LookupObjectInfoByIfaceData data;

  data.iface = iface;
  data.info = NULL;
  data.fallback = fallback;
  data.iface_type = 0;

  foreach_object_info (object, lookup_object_info_by_iface_cb, &data);

  if (out_iface_type && data.info)
    *out_iface_type = data.iface_type;

  return data.info;
}

typedef struct {
    /* owned */
    GSList *registrations;
    /* weak ref, or NULL if the object has been disposed */
    GObject *object;
} ObjectExport;

typedef struct {
    /* pseudo-weak ref, never NULL */
    DBusGConnection *connection;
    /* owned, never NULL */
    gchar *object_path;
    /* borrowed pointer to parent, never NULL */
    ObjectExport *export;
} ObjectRegistration;

static void object_export_object_died (gpointer user_data, GObject *dead);

static void
object_export_unregister_all (ObjectExport *oe)
{
  while (oe->registrations != NULL)
    {
      GSList *old = oe->registrations;
      ObjectRegistration *o = oe->registrations->data;

      dbus_connection_unregister_object_path (
          DBUS_CONNECTION_FROM_G_CONNECTION (o->connection), o->object_path);

      /* the link should have been removed by doing that */
      g_assert (oe->registrations != old);
    }
}

static void
object_export_free (ObjectExport *oe)
{
  g_slice_free (ObjectExport, oe);
}

static ObjectExport *
object_export_new (void)
{
  return g_slice_new0 (ObjectExport);
}

static ObjectRegistration *
object_registration_new (DBusGConnection *connection,
                         const gchar *object_path,
                         ObjectExport *export)
{
  ObjectRegistration *o = g_slice_new0 (ObjectRegistration);

  o->connection = connection;
  o->object_path = g_strdup (object_path);
  o->export = export;

  return o;
}

static void
object_registration_free (ObjectRegistration *o)
{
  g_assert (o->export != NULL);
  o->export->registrations = g_slist_remove (o->export->registrations, o);

  g_free (o->object_path);

  g_slice_free (ObjectRegistration, o);
}

/* Called when the object falls off the bus (e.g. because connection just
 * closed) */
static void
object_registration_unregistered (DBusConnection *connection,
                                  void *user_data)
{
  object_registration_free (user_data);
}

typedef struct
{
  GObject *object;
  GString *xml;
  GType gtype;
  const DBusGObjectInfo *object_info;
} DBusGLibWriteIterfaceData;

typedef struct
{
  GSList *methods;
  GSList *signals;
  GSList *properties;
} DBusGLibWriteInterfaceValues;

static void
write_interface (gpointer key, gpointer val, gpointer user_data)
{
  const char *name;
  GSList *methods;
  GSList *signals;
  GSList *properties;
  GString *xml;
  const DBusGObjectInfo *object_info;
  DBusGLibWriteIterfaceData *data;
  DBusGLibWriteInterfaceValues *values;

  name = key;

  values = val;
  methods = values->methods;
  signals = values->signals;
  properties = values->properties;

  data = user_data;
  xml = data->xml;
  object_info = data->object_info;

  g_string_append_printf (xml, "  <interface name=\"%s\">\n", name);

  /* FIXME: recurse to parent types ? */
  for (; methods; methods = methods->next)
    {
      DBusGMethodInfo *method;
      const char *args;
      method = methods->data;

      g_string_append_printf (xml, "    <method name=\"%s\">\n",
			      method_name_from_object_info (object_info, method));

      args = method_arg_info_from_object_info (object_info, method);

      while (*args)
	{
	  const char *name;
	  gboolean arg_in;
	  const char *type;
	  
	  args = arg_iterate (args, &name, &arg_in, NULL, NULL, &type);

	  /* FIXME - handle container types */
	  g_string_append_printf (xml, "      <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
				  name, type, arg_in ? "in" : "out");

	}
      g_string_append (xml, "    </method>\n");

    }
  g_slist_free (values->methods);

  for (; signals; signals = signals->next)
    {
      guint id;
      guint arg;
      const char *signame;
      GSignalQuery query;
      char *s;

      signame = signals->data;

      s = _dbus_gutils_wincaps_to_uscore (signame);
      
      id = g_signal_lookup (s, data->gtype);
      g_assert (id != 0);

      g_signal_query (id, &query);
      g_assert (query.return_type == G_TYPE_NONE);

      g_string_append_printf (xml, "    <signal name=\"%s\">\n", signame);

      for (arg = 0; arg < query.n_params; arg++)
	{
	  char *dbus_type = _dbus_gtype_to_signature (query.param_types[arg]);

	  g_assert (dbus_type != NULL);

          g_string_append (xml, "      <arg type=\"");
          g_string_append (xml, dbus_type);
          g_string_append (xml, "\"/>\n");
	  g_free (dbus_type);
	}

      g_string_append (xml, "    </signal>\n");
      g_free (s);
    }
  g_slist_free (values->signals);

  for (; properties; properties = properties->next)
    {
      const char *iface;
      const char *propname;
      const char *propname_uscore;
      const char *access_type;
      GParamSpec *spec;
      char *dbus_type;
      gboolean can_set;
      gboolean can_get;
      char *s;

      spec = NULL;

      property_iterate (properties->data, object_info->format_version, &iface, &propname, &propname_uscore, &access_type);

      s = lookup_property_name (data->object, name, propname);

      spec = g_object_class_find_property (g_type_class_peek (data->gtype), s);
      g_assert (spec != NULL);
      g_free (s);
      
      dbus_type = _dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
      g_assert (dbus_type != NULL);

      can_set = strcmp (access_type, "readwrite") == 0
                    && ((spec->flags & G_PARAM_WRITABLE) != 0
                    && (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);

      can_get = (spec->flags & G_PARAM_READABLE) != 0;

      if (can_set || can_get)
	{
	  g_string_append_printf (xml, "    <property name=\"%s\" ", propname);
	  g_string_append (xml, "type=\"");
	  g_string_append (xml, dbus_type);
	  g_string_append (xml, "\" access=\"");

	  if (can_set && can_get)
	    g_string_append (xml, "readwrite");
	  else if (can_get)
	    g_string_append (xml, "read");
	  else
	    {
	      g_assert (can_set);
	      g_string_append (xml, "write");
	    }
          
	  g_string_append (xml, "\"/>\n");
	}
      
      g_free (dbus_type);
    }
  g_slist_free (values->properties);

  g_free (values);
  g_string_append (xml, "  </interface>\n");
}

static DBusGLibWriteInterfaceValues *
lookup_values (GHashTable *interfaces, const char *method_interface)
{
  DBusGLibWriteInterfaceValues *values;
  if ((values = g_hash_table_lookup (interfaces, (gpointer) method_interface)) == NULL)
    {
      values = g_new0 (DBusGLibWriteInterfaceValues, 1);
      g_hash_table_insert (interfaces, (gpointer) method_interface, values);
    }
  return values;
}

static void
introspect_interfaces (GObject *object, GString *xml)
{
  GList *info_list;
  const GList *info_list_walk;
  const DBusGObjectInfo *info;
  DBusGLibWriteIterfaceData data;
  int i;
  GHashTable *interfaces;
  DBusGLibWriteInterfaceValues *values;
  const char *propsig;

  info_list = lookup_object_info (object);

  g_assert (info_list != NULL);

  /* Gather a list of all interfaces, indexed into their methods */
  for (info_list_walk = info_list; info_list_walk != NULL; info_list_walk = g_list_next (info_list_walk))
    {
      info = (DBusGObjectInfo *) info_list_walk->data;
      interfaces = g_hash_table_new (g_str_hash, g_str_equal);
      
      g_assert (info != NULL);

      for (i = 0; i < info->n_method_infos; i++)
        {
          const char *method_interface;
          const DBusGMethodInfo *method;

          method = &(info->method_infos[i]);

          method_interface = method_interface_from_object_info (info, method);

          values = lookup_values (interfaces, method_interface);
          values->methods = g_slist_prepend (values->methods, (gpointer) method);
        }

      propsig = info->exported_signals;
      while (propsig != NULL && *propsig)
        {
          const char *iface;
          const char *signame;

          propsig = signal_iterate (propsig, &iface, &signame);

          values = lookup_values (interfaces, iface);
          values->signals = g_slist_prepend (values->signals, (gpointer) signame);
        }

      propsig = info->exported_properties;
      while (propsig != NULL && *propsig)
        {
          const char *iface;
          const char *propname;
          const char *propname_uscore;
          const char *access_type;

          propsig = property_iterate (propsig, info->format_version, &iface, &propname, &propname_uscore, &access_type);

          values = lookup_values (interfaces, iface);
          values->properties = g_slist_prepend (values->properties, (gpointer)iface);
        }

      memset (&data, 0, sizeof (data));
      data.xml = xml;
      data.gtype = G_TYPE_FROM_INSTANCE (object);
      data.object_info = info;
      data.object = object;

      g_hash_table_foreach (interfaces, write_interface, &data);
      g_hash_table_destroy (interfaces);
    }

  g_list_free (info_list);
}

static DBusHandlerResult
handle_introspect (DBusConnection *connection,
                   DBusMessage    *message,
                   GObject        *object)
{
  GString *xml;
  unsigned int i;
  DBusMessage *ret;
  char **children;
  
  if (!dbus_connection_list_registered (connection, 
                                        dbus_message_get_path (message),
                                        &children))
    oom (NULL);
  
  xml = g_string_new (NULL);

  g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
  
  g_string_append (xml, "<node>\n");

  /* We are introspectable, though I guess that was pretty obvious */
  g_string_append_printf (xml, "  <interface name=\"%s\">\n", DBUS_INTERFACE_INTROSPECTABLE);
  g_string_append (xml, "    <method name=\"Introspect\">\n");
  g_string_append_printf (xml, "      <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
  g_string_append (xml, "    </method>\n");
  g_string_append (xml, "  </interface>\n");

  /* We support get/set/getall properties */
  g_string_append_printf (xml, "  <interface name=\"%s\">\n", DBUS_INTERFACE_PROPERTIES);
  g_string_append (xml, "    <method name=\"Get\">\n");
  g_string_append_printf (xml, "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
  g_string_append_printf (xml, "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
  g_string_append_printf (xml, "      <arg name=\"value\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_VARIANT_AS_STRING);
  g_string_append (xml, "    </method>\n");
  g_string_append (xml, "    <method name=\"Set\">\n");
  g_string_append_printf (xml, "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
  g_string_append_printf (xml, "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
  g_string_append_printf (xml, "      <arg name=\"value\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_VARIANT_AS_STRING);
  g_string_append (xml, "    </method>\n");
  g_string_append (xml, "    <method name=\"GetAll\">\n");
  g_string_append_printf (xml, "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
  g_string_append_printf (xml, "      <arg name=\"props\" direction=\"out\" type=\"%s\"/>\n",
                          DBUS_TYPE_ARRAY_AS_STRING
                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                            DBUS_TYPE_STRING_AS_STRING
                            DBUS_TYPE_VARIANT_AS_STRING
                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING
                          );

  g_string_append (xml, "    </method>\n");
  g_string_append (xml, "  </interface>\n");
  
  introspect_interfaces (object, xml);

  /* Append child nodes */
  for (i = 0; children[i]; i++)
      g_string_append_printf (xml, "  <node name=\"%s\"/>\n",
                              children[i]);
  
  /* Close the XML, and send it to the requesting app */
  g_string_append (xml, "</node>\n");

  ret = reply_or_die (message);

  dbus_message_append_args (ret,
                            DBUS_TYPE_STRING, &xml->str,
                            DBUS_TYPE_INVALID);

  connection_send_or_die (connection, ret);
  dbus_message_unref (ret);

  g_string_free (xml, TRUE);

  dbus_free_string_array (children);
  
  return DBUS_HANDLER_RESULT_HANDLED;
}

static DBusMessage*
set_object_property (DBusConnection  *connection,
                     DBusMessage     *message,
                     DBusMessageIter *iter,
                     GObject         *object,
                     GParamSpec      *pspec)
{
  GValue value = { 0, };
  DBusMessage *ret;
  DBusMessageIter sub;
  DBusGValueMarshalCtx context;

  dbus_message_iter_recurse (iter, &sub);

  context.recursion_depth = 0;
  context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
  context.proxy = NULL;

  g_value_init (&value, pspec->value_type);
  if (_dbus_gvalue_demarshal (&context, &sub, &value, NULL))
    {
      g_object_set_property (object,
                             pspec->name,
                             &value);

      g_value_unset (&value);

      ret = reply_or_die (message);
    }
  else
    {
      ret = error_or_die (message,
          DBUS_ERROR_INVALID_ARGS,
          "Argument's D-BUS type can't be converted to a GType");
    }

  return ret;
}

/*
 * @pspec: the paramspec for a D-Bus-exported property
 *
 * Returns: a reply for the Get() D-Bus method, either successful or error
 */
static DBusMessage*
get_object_property (DBusConnection *connection,
                     DBusMessage    *message,
                     GObject        *object,
                     GParamSpec     *pspec)
{
  GType value_gtype;
  GValue value = {0, };
  gchar *variant_sig;
  DBusMessage *ret;
  DBusMessageIter iter, subiter;
  gchar *error_message = NULL;

  ret = reply_or_die (message);

  g_value_init (&value, pspec->value_type);
  g_object_get_property (object, pspec->name, &value);

  variant_sig = _dbus_gvalue_to_signature (&value);
  if (variant_sig == NULL)
    {
      value_gtype = G_VALUE_TYPE (&value);
      error_message = g_strdup_printf (
          "Internal error: cannot marshal type \"%s\" in variant",
          g_type_name (value_gtype));
      goto out;
    }

  dbus_message_iter_init_append (ret, &iter);
  if (!dbus_message_iter_open_container (&iter,
					 DBUS_TYPE_VARIANT,
					 variant_sig,
					 &subiter))
    {
      error_message = g_strdup_printf (
          "Internal error: cannot open variant container for signature %s",
          variant_sig);
      goto out;
    }

  if (!_dbus_gvalue_marshal (&subiter, &value))
    {
      dbus_message_iter_abandon_container (&iter, &subiter);
      error_message = g_strdup_printf (
          "Internal error: could not marshal type \"%s\" in variant",
          G_VALUE_TYPE_NAME (&value));
      goto out;
    }

  dbus_message_iter_close_container (&iter, &subiter);

out:
  g_value_unset (&value);
  g_free (variant_sig);

  if (error_message != NULL)
    {
      dbus_message_unref (ret);
      ret = error_or_die (message, DBUS_ERROR_FAILED, error_message);
      g_critical ("%s", error_message);
      g_free (error_message);
    }

  return ret;
}

#define SHADOW_PROP_QUARK (dbus_g_object_type_dbus_shadow_property_quark ())

static GQuark
dbus_g_object_type_dbus_shadow_property_quark (void)
{
  static GQuark quark;

  if (!quark)
    quark = g_quark_from_static_string ("DBusGObjectTypeDBusShadowPropertyQuark");
  return quark;
}

/* Look for shadow properties on the given interface first, otherwise
 * just return the original property name.  This allows implementations to
 * get around the glib limitation of unique property names among all
 * GInterfaces by registering a "shadow" property name that the get/set
 * request will be redirected to.
 *
 * Shadow property data is stored as qdata on each GInterface.  If there
 * is no interface info, or there is no registered shadow property, just
 * return the original property name.
 */
static char *
lookup_property_name (GObject    *object,
                      const char *wincaps_propiface,
                      const char *requested_propname)
{
  const DBusGObjectInfo *object_info;
  GHashTable *shadow_props;
  char *shadow_prop_name = NULL, *uscore_name;
  GType iface_type = 0;

  g_assert (wincaps_propiface != NULL);
  g_assert (requested_propname != NULL);

  uscore_name = _dbus_gutils_wincaps_to_uscore (requested_propname);

  object_info = lookup_object_info_by_iface (object, wincaps_propiface, FALSE, &iface_type);
  if (!object_info)
    return uscore_name;

  shadow_props = (GHashTable *) g_type_get_qdata (iface_type, SHADOW_PROP_QUARK);
  if (shadow_props)
    {
      shadow_prop_name = g_strdup (g_hash_table_lookup (shadow_props, requested_propname));
      if (shadow_prop_name)
        g_free (uscore_name);
    }

  return shadow_prop_name ? shadow_prop_name : uscore_name;
}

/**
 * dbus_g_object_type_register_shadow_property:
 * @iface_type: #GType for the #GInterface
 * @dbus_prop_name: D-Bus property name (as specified in the introspection data)
 *  to override with the shadow property name (as specified in the GType's
 *  initialization function, ie glib-style)
 * @shadow_prop_name: property name which should override the shadow property
 *
 * Registers a new property name @shadow_prop_name that overrides the
 * @dbus_prop_name in D-Bus property get/set requests.  Since all properties for
 * all interfaces implemented by a GObject exist in the same namespace, this
 * allows implementations to use the same property name in two or more D-Bus
 * interfaces implemented by the same GObject, as long as one of those D-Bus
 * interface properties is registered with a shadow property name.
 *
 * For example, if both org.foobar.Baz.InterfaceA and org.foobar.Baz.InterfaceB
 * have a D-Bus property called "Bork", the developer assigns a shadow property
 * name to the conflicting property name in one or both of these GInterfaces to
 * resolve the conflict.  Assume the GInterface implementing
 * org.foobar.Baz.InterfaceA registers a shadow property called "a-bork", while
 * the GInterface implementing org.foobar.Baz.InterfaceB registers a shadow
 * property called "b-bork".  The GObject implementing both these GInterfaces
 * would then use #g_object_class_override_property() to implement both
 * "a-bork" and "b-bork" and D-Bus requests for "Bork" on either D-Bus interface
 * will not conflict.
 */
void
dbus_g_object_type_register_shadow_property (GType      iface_type,
                                             const char *dbus_prop_name,
                                             const char *shadow_prop_name)
{
  GHashTable *shadow_props;

  g_return_if_fail (G_TYPE_IS_CLASSED (iface_type) || G_TYPE_IS_INTERFACE (iface_type));
  g_return_if_fail (dbus_prop_name != NULL);
  g_return_if_fail (shadow_prop_name != NULL);

  shadow_props = (GHashTable *) g_type_get_qdata (iface_type, SHADOW_PROP_QUARK);
  if (!shadow_props)
    {
      shadow_props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
      g_type_set_qdata (iface_type,
                        dbus_g_object_type_dbus_shadow_property_quark (),
                        shadow_props);
    }

  g_assert (shadow_props);
  g_hash_table_insert (shadow_props, g_strdup (dbus_prop_name), g_strdup (shadow_prop_name));
}

static DBusMessage*
get_all_object_properties (DBusConnection        *connection,
                           DBusMessage           *message,
                           const DBusGObjectInfo *object_info,
                           const char            *wincaps_propiface,
                           GObject               *object)
{
  DBusMessage *ret;
  DBusMessageIter iter_ret;
  DBusMessageIter iter_dict;
  DBusMessageIter iter_dict_entry;
  DBusMessageIter iter_dict_value;
  const char *p;
  char *uscore_propname;

  ret = reply_or_die (message);

  dbus_message_iter_init_append (ret, &iter_ret);

  /* the types are all hard-coded, so this can only fail via OOM */
  if (!dbus_message_iter_open_container (&iter_ret,
                                         DBUS_TYPE_ARRAY,
                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                         DBUS_TYPE_STRING_AS_STRING
                                         DBUS_TYPE_VARIANT_AS_STRING
                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                         &iter_dict))
    oom (NULL);

  p = object_info->exported_properties;
  while (p != NULL && *p != '\0')
    {
      const char *prop_ifname;
      const char *prop_name;
      const char *prop_uscored;
      const char *access_flags;
      GParamSpec *pspec;
      GType value_gtype;
      GValue value = {0, };
      gchar *variant_sig;

      p = property_iterate (p, object_info->format_version, &prop_ifname, &prop_name, &prop_uscored, &access_flags);

      /* Conventionally, property names are valid member names, but dbus-glib
       * doesn't enforce this, and some dbus-glib services use GObject-style
       * property names (e.g. "foo-bar"). */
      if (!g_utf8_validate (prop_name, -1, NULL))
        {
          g_critical ("property name isn't UTF-8: %s", prop_name);
          continue;
        }

      uscore_propname = lookup_property_name (object, wincaps_propiface, prop_name);

      pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), uscore_propname);
      if (pspec == NULL)
        {
          g_warning ("introspection data references non-existing property %s", uscore_propname);
          g_free (uscore_propname);
          continue;
        }

      g_free (uscore_propname);

      g_value_init (&value, pspec->value_type);
      g_object_get_property (object, pspec->name, &value);

      variant_sig = _dbus_gvalue_to_signature (&value);
      if (variant_sig == NULL)
        {
          value_gtype = G_VALUE_TYPE (&value);
          g_warning ("Cannot marshal type \"%s\" in variant", g_type_name (value_gtype));
          g_value_unset (&value);
          continue;
        }

      /* a signature returned by _dbus_gvalue_to_signature had better be
       * valid */
      g_assert (g_variant_is_signature (variant_sig));

      /* type is hard-coded, so this can't fail except by OOM */
      if (!dbus_message_iter_open_container (&iter_dict,
                                             DBUS_TYPE_DICT_ENTRY,
                                             NULL,
                                             &iter_dict_entry))
        oom (NULL);

      /* prop_name is valid UTF-8, so this can't fail except by OOM; no point
       * in abandoning @iter_dict_entry since we're about to crash out */
      if (!dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name))
        oom (NULL);

      /* variant_sig has been asserted to be valid, so this can't fail
       * except by OOM */
      if (!dbus_message_iter_open_container (&iter_dict_entry,
                                             DBUS_TYPE_VARIANT,
                                             variant_sig,
                                             &iter_dict_value))
        oom (NULL);

      g_free (variant_sig);

      /* this can fail via programming error: the GObject property was
       * malformed (non-UTF8 string or something) */
      if (!_dbus_gvalue_marshal (&iter_dict_value, &value))
        {
          gchar *contents = g_strdup_value_contents (&value);
          gchar *error_message = g_strdup_printf (
              "cannot GetAll(%s): failed to serialize %s value of type %s: %s",
              wincaps_propiface, prop_name, G_VALUE_TYPE_NAME (&value),
              contents);

          g_critical ("%s", error_message);

          /* abandon ship! */
          dbus_message_iter_abandon_container (&iter_dict_entry,
              &iter_dict_value);
          dbus_message_iter_abandon_container (&iter_dict, &iter_dict_entry);
          dbus_message_unref (ret);
          ret = error_or_die (message, DBUS_ERROR_FAILED, error_message);

          g_free (contents);
          g_free (error_message);
          g_value_unset (&value);
          return ret;
        }

      /* these shouldn't fail except by OOM now that we were successful */
      if (!dbus_message_iter_close_container (&iter_dict_entry,
                                              &iter_dict_value))
        oom (NULL);
      if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
        oom (NULL);

      g_value_unset (&value);
  }

  if (!dbus_message_iter_close_container (&iter_ret, &iter_dict))
    oom (NULL);

  return ret;
}

static gboolean
lookup_object_and_method (GObject      *object,
			  DBusMessage  *message,
			  const DBusGObjectInfo **object_ret,
			  const DBusGMethodInfo **method_ret)
{
  const char *interface;
  const char *member;
  const char *signature;
  GList *info_list;
  const GList *info_list_walk;
  const DBusGObjectInfo *info;
  int i;

  interface = dbus_message_get_interface (message);
  member = dbus_message_get_member (message);
  signature = dbus_message_get_signature (message);

  info_list = lookup_object_info (object);
  
  for (info_list_walk = info_list; info_list_walk != NULL; info_list_walk = g_list_next (info_list_walk))
    {
      info = (DBusGObjectInfo *) info_list_walk->data;
      *object_ret = info;

      for (i = 0; i < info->n_method_infos; i++)
        {
          const char *expected_member;
          const char *expected_interface;
          char *expected_signature;
          const DBusGMethodInfo *method;

          method = &(info->method_infos[i]);

          /* Check method interface/name and input signature */ 
          expected_interface = method_interface_from_object_info (*object_ret, method);
          expected_member = method_name_from_object_info (*object_ret, method);
          expected_signature = method_input_signature_from_object_info (*object_ret, method);

          if ((interface == NULL
              || strcmp (expected_interface, interface) == 0)
              && strcmp (expected_member, member) == 0
              && strcmp (expected_signature, signature) == 0)
            {
              g_free (expected_signature);
              *method_ret = method;
              g_list_free (info_list);
              return TRUE;
            }
            g_free (expected_signature);
        }
    }

  if (info_list)
    g_list_free (info_list);

  return FALSE;
}

static char *
gerror_domaincode_to_dbus_error_name (const DBusGObjectInfo *object_info,
				      const char *msg_interface,
				      GQuark domain, gint code)
{
  const char *domain_str;
  const char *code_str;
  GString *dbus_error_name;

  domain_str = object_error_domain_prefix_from_object_info (object_info);
  code_str = object_error_code_from_object_info (object_info, domain, code);

  if (!domain_str || !code_str)
    {
      DBusGErrorInfo *info;

      g_static_rw_lock_reader_lock (&globals_lock);

      if (error_metadata != NULL)
	info = g_datalist_id_get_data (&error_metadata, domain);
      else
	info = NULL;

      g_static_rw_lock_reader_unlock (&globals_lock);

      if (info)
	{
	  GEnumValue *value;
	  GEnumClass *klass;

	  klass = g_type_class_ref (info->code_enum);
	  value = g_enum_get_value (klass, code);
	  g_type_class_unref (klass);

	  domain_str = info->default_iface;
	  if (value)
            {
              code_str = value->value_nick;
            }
          else
            {
              g_warning ("Error code %d out of range for GError domain %s",
                         code, g_quark_to_string (domain));
              code_str = NULL;
            }
	}
    }

  if (!domain_str)
    domain_str = msg_interface;

  if (!domain_str || !code_str)
    {
      const char *domain_string;
      /* If we can't map it sensibly, make up an error name */
      
      dbus_error_name = g_string_new ("org.freedesktop.DBus.GLib.UnmappedError.");

      domain_string = g_quark_to_string (domain);
      if (domain_string != NULL)
        {
          char *uscored = uscore_to_wincaps (domain_string);
          g_string_append (dbus_error_name, uscored);
          g_string_append_c (dbus_error_name, '.');
          g_free (uscored);
        }

      /* Map -1 to (unsigned) -1 to avoid "-", which is not valid */
      g_string_append_printf (dbus_error_name, "Code%u", (unsigned) code);
    }
  else
    {
      gchar *code_str_wincaps;
      dbus_error_name = g_string_new (domain_str);
      g_string_append_c (dbus_error_name, '.');
      /* We can't uppercase here for backwards compatibility
       * reasons; if someone had a lowercase enumeration value,
       * previously we'd just send it across unaltered.
       */
      code_str_wincaps = uscore_to_wincaps_full (code_str, FALSE, FALSE);
      g_string_append (dbus_error_name, code_str_wincaps);
      g_free (code_str_wincaps);
    }

  return g_string_free (dbus_error_name, FALSE);
}

static DBusMessage *
gerror_to_dbus_error_message (const DBusGObjectInfo *object_info,
			      DBusMessage           *message,
			      const GError          *error)
{
  DBusMessage *reply;

  if (!error)
    {
      char *error_msg;
      
      error_msg = g_strdup_printf ("Method invoked for %s returned FALSE but did not set error", dbus_message_get_member (message));
      reply = error_or_die (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg);
      g_free (error_msg);
    }
  else
    {
      if (error->domain == DBUS_GERROR)
        {
          const gchar *name = DBUS_ERROR_FAILED;

          switch (error->code)
            {
            case DBUS_GERROR_FAILED:
              name = DBUS_ERROR_FAILED;
              break;
            case DBUS_GERROR_NO_MEMORY:
              name = DBUS_ERROR_NO_MEMORY;
              break;
            case DBUS_GERROR_SERVICE_UNKNOWN:
              name = DBUS_ERROR_SERVICE_UNKNOWN;
              break;
            case DBUS_GERROR_NAME_HAS_NO_OWNER:
              name = DBUS_ERROR_NAME_HAS_NO_OWNER;
              break;
            case DBUS_GERROR_NO_REPLY:
              name = DBUS_ERROR_NO_REPLY;
              break;
            case DBUS_GERROR_IO_ERROR:
              name = DBUS_ERROR_IO_ERROR;
              break;
            case DBUS_GERROR_BAD_ADDRESS:
              name = DBUS_ERROR_BAD_ADDRESS;
              break;
            case DBUS_GERROR_NOT_SUPPORTED:
              name = DBUS_ERROR_NOT_SUPPORTED;
              break;
            case DBUS_GERROR_LIMITS_EXCEEDED:
              name = DBUS_ERROR_LIMITS_EXCEEDED;
              break;
            case DBUS_GERROR_ACCESS_DENIED:
              name = DBUS_ERROR_ACCESS_DENIED;
              break;
            case DBUS_GERROR_AUTH_FAILED:
              name = DBUS_ERROR_AUTH_FAILED;
              break;
            case DBUS_GERROR_NO_SERVER:
              name = DBUS_ERROR_NO_SERVER;
              break;
            case DBUS_GERROR_TIMEOUT:
              name = DBUS_ERROR_TIMEOUT;
              break;
            case DBUS_GERROR_NO_NETWORK:
              name = DBUS_ERROR_NO_NETWORK;
              break;
            case DBUS_GERROR_ADDRESS_IN_USE:
              name = DBUS_ERROR_ADDRESS_IN_USE;
              break;
            case DBUS_GERROR_DISCONNECTED:
              name = DBUS_ERROR_DISCONNECTED;
              break;
            case DBUS_GERROR_INVALID_ARGS:
              name = DBUS_ERROR_INVALID_ARGS;
              break;
            case DBUS_GERROR_FILE_NOT_FOUND:
              name = DBUS_ERROR_FILE_NOT_FOUND;
              break;
            case DBUS_GERROR_REMOTE_EXCEPTION:
              name = dbus_g_error_get_name ((GError*) error);
              break;
            }

          reply = error_or_die (message, name, error->message);
        }
      else
	{
	  char *error_name;
	  error_name = gerror_domaincode_to_dbus_error_name (object_info,
							     dbus_message_get_interface (message),
							     error->domain, error->code);
          reply = error_or_die (message, error_name, error->message);
          g_free (error_name);
        }
    }

  return reply;
}

/**
 * SECTION:dbus-gmethod
 * @short_description: GMethod Info & Invocation
 * @see_also: #DBusGMessage
 * @stability: Stable
 *
 * These types are used to call methods on #GObject objects.
 */

/**
 * DBusGMethodInvocation:
 *
 * The context of an asynchronous method call.  See dbus_g_method_return() and
 * dbus_g_method_return_error().
 */
struct _DBusGMethodInvocation {
  DBusGConnection *connection; /**< The connection */
  DBusGMessage *message; /**< The message which generated the method call */
  const DBusGObjectInfo *object; /**< The object the method was called on */
  const DBusGMethodInfo *method; /**< The method called */
  gboolean send_reply;
};

static DBusHandlerResult
invoke_object_method (GObject         *object,
		      const DBusGObjectInfo *object_info,
		      const DBusGMethodInfo *method,
		      DBusConnection  *connection,
		      DBusMessage     *message)
{
  gboolean had_error, is_async, send_reply;
  GError *gerror;
  GValueArray *value_array;
  GValue return_value = {0,};
  GClosure closure;
  char *in_signature;
  GArray *out_param_values = NULL;
  GValueArray *out_param_gvalues = NULL;
  int out_param_count;
  int out_param_pos, out_param_gvalue_pos;
  DBusMessage *reply = NULL;
  gboolean have_retval;
  gboolean retval_signals_error;
  gboolean retval_is_synthetic;
  gboolean retval_is_constant;
  const char *arg_metadata;

  gerror = NULL;

  /* This flag says whether invokee is handed a special DBusGMethodInvocation structure,
   * instead of being required to fill out all return values in the context of the function.
   * Some additional data is also exposed, such as the message sender.
   */
  is_async = strcmp (string_table_lookup (get_method_data (object_info, method), 2), "A") == 0;
  
  /* Messages can be sent with a flag that says "I don't need a reply".  This is an optimization
   * normally, but in the context of the system bus it's important to not send a reply
   * to these kinds of messages, because they will be unrequested replies, and thus subject
   * to denial and logging.  We don't want to fill up logs.
   * http://bugs.freedesktop.org/show_bug.cgi?id=19441
   */
  send_reply = !dbus_message_get_no_reply (message); 

  have_retval = FALSE;
  retval_signals_error = FALSE;
  retval_is_synthetic = FALSE;
  retval_is_constant = FALSE;

  /* This is evil.  We do this to work around the fact that
   * the generated glib marshallers check a flag in the closure object
   * which we don't care about.  We don't need/want to create
   * a new closure for each invocation.
   */
  memset (&closure, 0, sizeof (closure));

  in_signature = method_input_signature_from_object_info (object_info, method); 
  
  /* Convert method IN parameters to GValueArray */
  {
    GArray *types_array;
    guint n_params;
    const GType *types;
    DBusGValueMarshalCtx context;
    GError *error = NULL;
    
    context.recursion_depth = 0;
    context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
    context.proxy = NULL;

    types_array = _dbus_gtypes_from_arg_signature (in_signature, FALSE);
    n_params = types_array->len;
    types = (const GType*) types_array->data;

    value_array = _dbus_gvalue_demarshal_message (&context, message, n_params, types, &error);
    if (value_array == NULL)
      {
	g_free (in_signature); 
	g_array_free (types_array, TRUE);
        reply = error_or_die (message, "org.freedesktop.DBus.GLib.ErrorError", error->message);
        connection_send_or_die (connection, reply);
	dbus_message_unref (reply);
	g_error_free (error);
	return DBUS_HANDLER_RESULT_HANDLED;
      }
    g_array_free (types_array, TRUE);
  }

  /* Prepend object as first argument */ 
  g_value_array_prepend (value_array, NULL);
  g_value_init (g_value_array_get_nth (value_array, 0), G_TYPE_OBJECT);
  g_value_set_object (g_value_array_get_nth (value_array, 0), object);
  
  if (is_async)
    {
      GValue context_value = {0,};
      DBusGMethodInvocation *context;
      context = g_new (DBusGMethodInvocation, 1);
      context->connection = dbus_g_connection_ref (DBUS_G_CONNECTION_FROM_CONNECTION (connection));
      context->message = dbus_g_message_ref (DBUS_G_MESSAGE_FROM_MESSAGE (message));
      context->object = object_info;
      context->method = method;
      context->send_reply = send_reply;
      g_value_init (&context_value, G_TYPE_POINTER);
      g_value_set_pointer (&context_value, context);
      g_value_array_append (value_array, &context_value);
    }
  else
    {
      RetvalType retval;
      gboolean arg_in;
      gboolean arg_const;
      const char *argsig;

      arg_metadata = method_arg_info_from_object_info (object_info, method);
      
      /* Count number of output parameters, and look for a return value */
      out_param_count = 0;
      while (*arg_metadata)
	{
	  arg_metadata = arg_iterate (arg_metadata, NULL, &arg_in, &arg_const, &retval, &argsig);
	  if (arg_in)
	    continue;
	  if (retval != RETVAL_NONE)
	    {
	      DBusSignatureIter tmp_sigiter;
	      /* This is the function return value */
	      g_assert (!have_retval);
	      have_retval = TRUE;
	      retval_is_synthetic = FALSE;

	      switch (retval)
		{
		case RETVAL_NONE:
		  g_assert_not_reached ();
		  break;
		case RETVAL_NOERROR:
		  retval_signals_error = FALSE;
		  break;
		case RETVAL_ERROR:
		  retval_signals_error = TRUE;
		  break;
		}

	      retval_is_constant = arg_const;

	      /* Initialize our return GValue with the specified type */
	      dbus_signature_iter_init (&tmp_sigiter, argsig);
	      g_value_init (&return_value, _dbus_gtype_from_signature_iter (&tmp_sigiter, FALSE));
	    }
	  else
	    {
	      /* It's a regular output value */
	      out_param_count++;
	    }
	}

      /* For compatibility, if we haven't found a return value, we assume
       * the function returns a gboolean for signalling an error
       * (and therefore also takes a GError).  We also note that it
       * is a "synthetic" return value; i.e. we aren't going to be
       * sending it over the bus, it's just to signal an error.
       */
      if (!have_retval)
	{
	  have_retval = TRUE;
	  retval_is_synthetic = TRUE;
	  retval_signals_error = TRUE;
	  g_value_init (&return_value, G_TYPE_BOOLEAN);
	}

      /* Create an array to store the actual values of OUT parameters
       * (other than the real function return, if any).  Then, create
       * a GValue boxed POINTER to each of those values, and append to
       * the invocation, so the method can return the OUT parameters.
       */
      out_param_values = g_array_sized_new (FALSE, TRUE, sizeof (GTypeCValue), out_param_count);

      /* We have a special array of GValues for toplevel GValue return
       * types.
       */
      out_param_gvalues = g_value_array_new (out_param_count);
      out_param_pos = 0;
      out_param_gvalue_pos = 0;

      /* Reset argument metadata pointer */
      arg_metadata = method_arg_info_from_object_info (object_info, method);
      
      /* Iterate over output arguments again, this time allocating space for
       * them as appopriate.
       */
      while (*arg_metadata)
	{
	  GValue value = {0, };
	  GTypeCValue storage;
	  DBusSignatureIter tmp_sigiter;
	  GType current_gtype;

	  arg_metadata = arg_iterate (arg_metadata, NULL, &arg_in, NULL, &retval, &argsig);
	  /* Skip over input arguments and the return value, if any */
	  if (arg_in || retval != RETVAL_NONE)
	    continue;

	  dbus_signature_iter_init (&tmp_sigiter, argsig);
	  current_gtype = _dbus_gtype_from_signature_iter (&tmp_sigiter, FALSE);

	  g_value_init (&value, G_TYPE_POINTER);

	  /* We special case variants to make method invocation a bit nicer */
	  if (current_gtype != G_TYPE_VALUE)
	    {
	      memset (&storage, 0, sizeof (storage));
	      g_array_append_val (out_param_values, storage);
	      g_value_set_pointer (&value, &(g_array_index (out_param_values, GTypeCValue, out_param_pos)));
	      out_param_pos++;
	    }
	  else
	    {
	      g_value_array_append (out_param_gvalues, NULL);
	      g_value_set_pointer (&value, out_param_gvalues->values + out_param_gvalue_pos);
	      out_param_gvalue_pos++;
	    }
	  g_value_array_append (value_array, &value);
	}
    }

  /* Append GError as final argument if necessary */
  if (retval_signals_error)
    {
      g_assert (have_retval);
      g_value_array_append (value_array, NULL);
      g_value_init (g_value_array_get_nth (value_array, value_array->n_values - 1), G_TYPE_POINTER);
      g_value_set_pointer (g_value_array_get_nth (value_array, value_array->n_values - 1), &gerror);
    }
  
  /* Actually invoke method */
  method->marshaller (&closure, have_retval ? &return_value : NULL,
		      value_array->n_values,
		      value_array->values,
		      NULL, method->function);
  if (is_async)
    {
      goto done;
    }

  if (retval_signals_error)
    had_error = _dbus_gvalue_signals_error (&return_value);
  else
    had_error = FALSE;

  if (!had_error)
    {
      DBusMessageIter iter;

      /* Careful here - there are two major cases in this section of the code.
       * If send_reply is TRUE, we're constructing a dbus message and freeing
       * the return values.  If it's FALSE, then we just need to free the
       * values.
       */
      if (send_reply)
        {
          reply = reply_or_die (message);

          /* Append output arguments to reply */
          dbus_message_iter_init_append (reply, &iter);
        }

      /* First, append the return value, unless it's synthetic */
      if (have_retval && !retval_is_synthetic)
	{
          if (reply != NULL && !_dbus_gvalue_marshal (&iter, &return_value))
            {
              gchar *desc = g_strdup_value_contents (&return_value);

              g_critical ("unable to append retval of type %s for %s: %s",
                  G_VALUE_TYPE_NAME (&return_value),
                  method_name_from_object_info (object_info, method),
                  desc);
              g_free (desc);
              /* the reply is now unusable but we still need to free
               * everything */
              dbus_message_unref (reply);
              reply = NULL;
            }

	  if (!retval_is_constant)
	    g_value_unset (&return_value);
	}

      /* Grab the argument metadata and iterate over it */
      arg_metadata = method_arg_info_from_object_info (object_info, method);
      
      /* Now append any remaining return values */
      out_param_pos = 0;
      out_param_gvalue_pos = 0;
      while (*arg_metadata)
	{
	  GValue gvalue = {0, };
	  const char *arg_name;
	  gboolean arg_in;
	  gboolean constval;
	  RetvalType retval;
	  const char *arg_signature;
	  DBusSignatureIter argsigiter;

	  do
	    {
	      /* Iterate over only output values; skip over input
		 arguments and the return value */
	      arg_metadata = arg_iterate (arg_metadata, &arg_name, &arg_in, &constval, &retval, &arg_signature);
	    }
	  while ((arg_in || retval != RETVAL_NONE) && *arg_metadata);

	  /* If the last argument we saw was input or the return
  	   * value, we must be done iterating over output arguments.
	   */
	  if (arg_in || retval != RETVAL_NONE)
	    break;

	  dbus_signature_iter_init (&argsigiter, arg_signature);
	  
	  g_value_init (&gvalue, _dbus_gtype_from_signature_iter (&argsigiter, FALSE));
	  if (G_VALUE_TYPE (&gvalue) != G_TYPE_VALUE)
	    {
	      if (!_dbus_gvalue_take (&gvalue,
				     &(g_array_index (out_param_values, GTypeCValue, out_param_pos))))
		g_assert_not_reached ();
	      out_param_pos++;
	    }
	  else
	    {
	      g_value_set_static_boxed (&gvalue, out_param_gvalues->values + out_param_gvalue_pos);
	      out_param_gvalue_pos++;
	    }

          if (reply && !_dbus_gvalue_marshal (&iter, &gvalue))
            {
              gchar *desc = g_strdup_value_contents (&gvalue);

              g_critical ("unable to append OUT arg of type %s for %s: %s",
                  G_VALUE_TYPE_NAME (&gvalue),
                  method_name_from_object_info (object_info, method),
                  desc);
              g_free (desc);
              /* the reply is now unusable but we still need to free
               * everything */
              dbus_message_unref (reply);
              reply = NULL;
            }

	  /* Here we actually free the allocated value; we
	   * took ownership of it with _dbus_gvalue_take, unless
	   * an annotation has specified this value as constant.
	   */
	  if (!constval)
	    g_value_unset (&gvalue);
	}
    }
  else if (send_reply)
    reply = gerror_to_dbus_error_message (object_info, message, gerror);

  if (reply)
    {
      connection_send_or_die (connection, reply);
      dbus_message_unref (reply);
    }

done:
  g_free (in_signature);

  if (!is_async)
    {
      g_array_free (out_param_values, TRUE);
      g_value_array_free (out_param_gvalues);
    }

  if (gerror != NULL)
    g_clear_error (&gerror);

  g_value_array_free (value_array);
  return DBUS_HANDLER_RESULT_HANDLED;
}

/*
 * @wincaps_propiface: the D-Bus interface name, conventionally WindowsCaps
 * @requested_propname: the D-Bus property name, conventionally WindowsCaps
 * @uscore_propname: the GObject property name, conventionally
 *    words_with_underscores or words-with-dashes
 * @is_set: %TRUE if we're going to set the property, %FALSE if we're going
 *    to get it
 *
 * Check that the requested property exists and the requested access is
 * allowed. If not, reply with a D-Bus AccessDenied error message.
 *
 * Returns: %TRUE if property access can continue, or %FALSE if an error
 *    reply has been sent
 */
static gboolean
check_property_access (DBusConnection  *connection,
                       DBusMessage     *message,
                       GObject         *object,
                       const char      *wincaps_propiface,
                       const char      *requested_propname,
                       const char      *uscore_propname,
                       gboolean         is_set)
{
  const DBusGObjectInfo *object_info;
  const char *access_type;
  DBusMessage *ret;
  gchar *error_message;

  if (!is_set && !disable_legacy_property_access)
    return TRUE;

  object_info = lookup_object_info_by_iface (object, wincaps_propiface, TRUE, NULL);
  if (!object_info)
    {
      error_message = g_strdup_printf (
          "Interface \"%s\" isn't exported (or may not exist), can't access property \"%s\"",
          wincaps_propiface, requested_propname);

      goto error;
    }

  /* Try both forms of property names: "foo_bar" or "FooBar"; for historical
   * reasons we accept both.
   */
  if (object_info
      && !(property_info_from_object_info (object_info, wincaps_propiface, requested_propname, &access_type)
           || property_info_from_object_info (object_info, wincaps_propiface, uscore_propname, &access_type)))
    {
      error_message = g_strdup_printf (
          "Property \"%s\" of interface \"%s\" isn't exported (or may not exist)",
          requested_propname, wincaps_propiface);

      goto error;
    }

  if (strcmp (access_type, "readwrite") == 0)
    return TRUE;

  if (is_set ? strcmp (access_type, "read") == 0
             : strcmp (access_type, "write") == 0)
    {
      error_message = g_strdup_printf (
          "Property \"%s\" of interface \"%s\" is not %s",
          requested_propname,
          wincaps_propiface,
          is_set ? "settable" : "readable");

      goto error;
    }

  return TRUE;

error:
  ret = error_or_die (message, DBUS_ERROR_ACCESS_DENIED, error_message);
  g_free (error_message);

  connection_send_or_die (connection, ret);
  dbus_message_unref (ret);
  return FALSE;
}

static DBusHandlerResult
object_registration_message (DBusConnection  *connection,
                             DBusMessage     *message,
                             void            *user_data)
{
  GParamSpec *pspec;
  GObject *object;
  gboolean setter;
  gboolean getter;
  gboolean getall;
  char *s;
  const char *requested_propname;
  const char *wincaps_propiface;
  DBusMessageIter iter;
  const DBusGMethodInfo *method;
  const DBusGObjectInfo *object_info;
  DBusMessage *ret;
  ObjectRegistration *o;

  o = user_data;
  /* export is always non-NULL. If the object has been disposed, the weak-ref
   * callback removes all registrations from the DBusConnection, so this
   * should never be reached with object = NULL. */
  object = G_OBJECT (o->export->object);
  g_assert (object != NULL);

  if (dbus_message_is_method_call (message,
                                   DBUS_INTERFACE_INTROSPECTABLE,
                                   "Introspect"))
    return handle_introspect (connection, message, object);

  /* Try the metainfo, which lets us invoke methods */
  object_info = NULL;
  if (lookup_object_and_method (object, message, &object_info, &method))
    return invoke_object_method (object, object_info, method, connection, message);

  /* If no metainfo, we can still do properties and signals
   * via standard GLib introspection.  Note we do now check
   * property access against the metainfo if available.
   */
  getter = FALSE;
  setter = FALSE;
  getall = FALSE;
  if (dbus_message_is_method_call (message,
                                   DBUS_INTERFACE_PROPERTIES,
                                   "Get"))
    getter = TRUE;
  else if (dbus_message_is_method_call (message,
                                        DBUS_INTERFACE_PROPERTIES,
                                        "Set"))
    setter = TRUE;
  else if (dbus_message_is_method_call (message,
                                   DBUS_INTERFACE_PROPERTIES,
                                   "GetAll"))
    getall = TRUE;

  if (!(setter || getter || getall))
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

  ret = NULL;

  dbus_message_iter_init (message, &iter);

  if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
    {
      ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
          "First argument to Get(), GetAll() or Set() must be an interface string");
      goto out;
    }

  dbus_message_iter_get_basic (&iter, &wincaps_propiface);
  dbus_message_iter_next (&iter);

  if (getall)
    {
      object_info = lookup_object_info_by_iface (object, wincaps_propiface, TRUE, NULL);
      if (object_info != NULL)
          ret = get_all_object_properties (connection, message, object_info, wincaps_propiface, object);
      else
          return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }
  else
    {
      g_assert (getter || setter);

      if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
        {
          ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
              "Second argument to Get() or Set() must be a property name string");
          goto out;
        }

      dbus_message_iter_get_basic (&iter, &requested_propname);
      dbus_message_iter_next (&iter);

      s = lookup_property_name (object, wincaps_propiface, requested_propname);

      if (!check_property_access (connection, message, object, wincaps_propiface, requested_propname, s, setter))
        {
          g_free (s);
          return DBUS_HANDLER_RESULT_HANDLED;
        }

      pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
                                            s);

      g_free (s);

      if (pspec != NULL)
        {
          if (setter)
            {
              if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
                {
                  ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
                      "Third argument to Set() must be a variant");
                  goto out;
                }

              ret = set_object_property (connection, message, &iter,
                                         object, pspec);
              dbus_message_iter_next (&iter);
            }
          else
            {
              g_assert (getter);
              ret = get_object_property (connection, message,
                                         object, pspec);
            }
        }
      else
        {
          gchar *error_message = g_strdup_printf ("No such property %s",
              requested_propname);

          ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS, error_message);
          g_free (error_message);
        }
    }

  g_assert (ret != NULL);

  /* FIXME: this should be returned as a D-Bus error, not spammed out
   * as a warning. This is too late to do that, though - we've already
   * had any side-effects we were going to have - and it would break
   * anything that's relying on ability to give us too many arguments. */
  if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
    g_warning ("Property get, set or set all had too many arguments\n");

out:
  connection_send_or_die (connection, ret);
  dbus_message_unref (ret);
  return DBUS_HANDLER_RESULT_HANDLED;
}

static const DBusObjectPathVTable gobject_dbus_vtable = {
  object_registration_unregistered,
  object_registration_message,
  NULL
};

typedef struct {
  GClosure         closure;
  DBusGConnection *connection;
  GObject         *object;
  const char      *signame;
  const char      *sigiface;
} DBusGSignalClosure;

static GClosure *
dbus_g_signal_closure_new (GObject         *object,
			   const char      *signame,
			   const char      *sigiface)
{
  DBusGSignalClosure *closure;
  
  closure = (DBusGSignalClosure*) g_closure_new_simple (sizeof (DBusGSignalClosure), NULL);

  closure->object = object;
  closure->signame = signame;
  closure->sigiface = sigiface;
  return (GClosure*) closure;
}

static void
emit_signal_for_registration (ObjectRegistration *o,
                              DBusGSignalClosure *sigclosure,
                              GValue             *retval,
                              guint               n_param_values,
                              const GValue       *param_values)
{
  DBusMessage *signal;
  DBusMessageIter iter;
  guint i;

  g_assert (g_variant_is_object_path (o->object_path));
  g_assert (g_dbus_is_interface_name (sigclosure->sigiface));
  g_assert (g_dbus_is_member_name (sigclosure->signame));

  signal = dbus_message_new_signal (o->object_path,
                                    sigclosure->sigiface,
                                    sigclosure->signame);
  if (!signal)
    oom (NULL);

  dbus_message_iter_init_append (signal, &iter);

  /* First argument is the object itself, and we can't marshall that */
  for (i = 1; i < n_param_values; i++)
    {
      if (!_dbus_gvalue_marshal (&iter,
                                (GValue *) (&(param_values[i]))))
        {
          g_warning ("failed to marshal parameter %d for signal %s",
                     i, sigclosure->signame);
          goto out;
        }
    }

  connection_send_or_die (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection),
      signal);
out:
  dbus_message_unref (signal);
}

static void
signal_emitter_marshaller (GClosure        *closure,
			   GValue          *retval,
			   guint            n_param_values,
			   const GValue    *param_values,
			   gpointer         invocation_hint,
			   gpointer         marshal_data)
{
  DBusGSignalClosure *sigclosure;
  const ObjectExport *oe;
  const GSList *iter;

  sigclosure = (DBusGSignalClosure *) closure;

  g_assert (retval == NULL);

  oe = g_object_get_data (sigclosure->object, "dbus_glib_object_registrations");
  /* If the object has ever been exported, this should exist; it persists until
   * the object is actually freed. */
  g_assert (oe != NULL);

  for (iter = oe->registrations; iter; iter = iter->next)
    {
      ObjectRegistration *o = iter->data;

      emit_signal_for_registration (o, sigclosure, retval, n_param_values, param_values);
    }
}

static void
export_signals (const GList *info_list, GObject *object)
{
  GType gtype;
  const char *sigdata;
  const char *iface;
  const char *signame;
  const DBusGObjectInfo *info;

  gtype = G_TYPE_FROM_INSTANCE (object);

  for (; info_list != NULL; info_list = g_list_next (info_list))
    {
      info = (DBusGObjectInfo *) info_list->data;
      
      sigdata = info->exported_signals;
      
      while (*sigdata != '\0')
        {
          guint id;
          GSignalQuery query;
          GClosure *closure;
          char *s;

          sigdata = signal_iterate (sigdata, &iface, &signame);

          if (!g_dbus_is_interface_name (iface))
            {
              g_critical ("invalid interface name found in %s: %s",
                  g_type_name (gtype), iface);
              continue;
            }

          if (!g_dbus_is_member_name (signame))
            {
              g_critical ("invalid signal name found in %s: %s",
                  g_type_name (gtype), signame);
              continue;
            }

          s = _dbus_gutils_wincaps_to_uscore (signame);

          id = g_signal_lookup (s, gtype);
          if (id == 0)
            {
              g_warning ("signal \"%s\" (from \"%s\") exported but not found in object class \"%s\"",
                     s, signame, g_type_name (gtype));
              g_free (s);
              continue;
            }

          g_signal_query (id, &query);

          if (query.return_type != G_TYPE_NONE)
            {
              g_warning ("Not exporting signal \"%s\" for object class \"%s\" as it has a return type \"%s\"",
                     s, g_type_name (gtype), g_type_name (query.return_type));
              g_free (s);
              continue; /* FIXME: these could be listed as methods ? */
            }
          
          closure = dbus_g_signal_closure_new (object, signame, (char*) iface);
          g_closure_set_marshal (closure, signal_emitter_marshaller);

          g_signal_connect_closure_by_id (object,
                          id,
                          0,
                          closure,
                          FALSE);

          g_free (s);
        }
    }
}

static gint
dbus_error_to_gerror_code (const char *derr)
{
  if (0) ; 
  else if (!strcmp (derr,  DBUS_ERROR_FAILED  )) 
    return  DBUS_GERROR_FAILED ;
  else if (!strcmp (derr,  DBUS_ERROR_NO_MEMORY  )) 
    return  DBUS_GERROR_NO_MEMORY ;
  else if (!strcmp (derr,  DBUS_ERROR_SERVICE_UNKNOWN  )) 
    return  DBUS_GERROR_SERVICE_UNKNOWN ;
  else if (!strcmp (derr,  DBUS_ERROR_NAME_HAS_NO_OWNER  )) 
    return  DBUS_GERROR_NAME_HAS_NO_OWNER ;
  else if (!strcmp (derr,  DBUS_ERROR_NO_REPLY  )) 
    return  DBUS_GERROR_NO_REPLY ;
  else if (!strcmp (derr,  DBUS_ERROR_IO_ERROR  )) 
    return  DBUS_GERROR_IO_ERROR ;
  else if (!strcmp (derr,  DBUS_ERROR_BAD_ADDRESS  )) 
    return  DBUS_GERROR_BAD_ADDRESS ;
  else if (!strcmp (derr,  DBUS_ERROR_NOT_SUPPORTED  )) 
    return  DBUS_GERROR_NOT_SUPPORTED ;
  else if (!strcmp (derr,  DBUS_ERROR_LIMITS_EXCEEDED  )) 
    return  DBUS_GERROR_LIMITS_EXCEEDED ;
  else if (!strcmp (derr,  DBUS_ERROR_ACCESS_DENIED  )) 
    return  DBUS_GERROR_ACCESS_DENIED ;
  else if (!strcmp (derr,  DBUS_ERROR_AUTH_FAILED  )) 
    return  DBUS_GERROR_AUTH_FAILED ;
  else if (!strcmp (derr,  DBUS_ERROR_NO_SERVER  )) 
    return  DBUS_GERROR_NO_SERVER ;
  else if (!strcmp (derr,  DBUS_ERROR_TIMEOUT  )) 
    return  DBUS_GERROR_TIMEOUT ;
  else if (!strcmp (derr,  DBUS_ERROR_NO_NETWORK  )) 
    return  DBUS_GERROR_NO_NETWORK ;
  else if (!strcmp (derr,  DBUS_ERROR_ADDRESS_IN_USE  )) 
    return  DBUS_GERROR_ADDRESS_IN_USE ;
  else if (!strcmp (derr,  DBUS_ERROR_DISCONNECTED  )) 
    return  DBUS_GERROR_DISCONNECTED ;
  else if (!strcmp (derr,  DBUS_ERROR_INVALID_ARGS  )) 
    return  DBUS_GERROR_INVALID_ARGS ;
  else if (!strcmp (derr,  DBUS_ERROR_FILE_NOT_FOUND  )) 
    return  DBUS_GERROR_FILE_NOT_FOUND ;
  else if (!strcmp (derr,  DBUS_ERROR_FILE_EXISTS  )) 
    return  DBUS_GERROR_FILE_EXISTS ;
  else if (!strcmp (derr,  DBUS_ERROR_UNKNOWN_METHOD  )) 
    return  DBUS_GERROR_UNKNOWN_METHOD ;
  else if (!strcmp (derr,  DBUS_ERROR_TIMED_OUT  )) 
    return  DBUS_GERROR_TIMED_OUT ;
  else if (!strcmp (derr,  DBUS_ERROR_MATCH_RULE_NOT_FOUND  )) 
    return  DBUS_GERROR_MATCH_RULE_NOT_FOUND ;
  else if (!strcmp (derr,  DBUS_ERROR_MATCH_RULE_INVALID  )) 
    return  DBUS_GERROR_MATCH_RULE_INVALID ;
  else if (!strcmp (derr,  DBUS_ERROR_SPAWN_EXEC_FAILED  )) 
    return  DBUS_GERROR_SPAWN_EXEC_FAILED ;
  else if (!strcmp (derr,  DBUS_ERROR_SPAWN_FORK_FAILED  )) 
    return  DBUS_GERROR_SPAWN_FORK_FAILED ;
  else if (!strcmp (derr,  DBUS_ERROR_SPAWN_CHILD_EXITED  )) 
    return  DBUS_GERROR_SPAWN_CHILD_EXITED ;
  else if (!strcmp (derr,  DBUS_ERROR_SPAWN_CHILD_SIGNALED  )) 
    return  DBUS_GERROR_SPAWN_CHILD_SIGNALED ;
  else if (!strcmp (derr,  DBUS_ERROR_SPAWN_FAILED  )) 
    return  DBUS_GERROR_SPAWN_FAILED ;
  else if (!strcmp (derr,  DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN  )) 
    return  DBUS_GERROR_UNIX_PROCESS_ID_UNKNOWN ;
  else if (!strcmp (derr,  DBUS_ERROR_INVALID_SIGNATURE  )) 
    return  DBUS_GERROR_INVALID_SIGNATURE ;
  else if (!strcmp (derr,  DBUS_ERROR_INVALID_FILE_CONTENT  )) 
    return  DBUS_GERROR_INVALID_FILE_CONTENT ;
  else if (!strcmp (derr,  DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN  )) 
    return  DBUS_GERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN ;
  else
    return DBUS_GERROR_REMOTE_EXCEPTION;
}

/**
 * dbus_set_g_error:
 * @gerror: an error
 * @derror: a #DBusError
 *
 * Store the information from a DBus method error return into a
 * GError.  For the normal case of an arbitrary remote process,
 * the error code will be DBUS_GERROR_REMOTE_EXCEPTION.  Now,
 * DBus errors have two components; a message and a "name". 
 * The former is an arbitrary (normally American English) string.  
 * The second is a string like com.example.FooFailure which 
 * programs can use as a conditional source.  Because a GError
 * only has one string, we use a hack to encode both values:
 *
 * &lt;human readable string&gt;&lt;null&gt;&lt;error name&gt;&lt;null&gt;
 * 
 * You can use the following code to retrieve both values:
 * 
 * |[const char *msg = error->message;
 * size_t len = strlen(msg);
 * const char *error_name = msg+len+1;]|
 */
void
dbus_set_g_error (GError    **gerror,
                  DBusError  *derror)
{
  int code;

  g_return_if_fail (derror != NULL);
  g_return_if_fail (dbus_error_is_set (derror));
  g_return_if_fail (gerror == NULL || *gerror == NULL);

  code = dbus_error_to_gerror_code (derror->name);
  if (code != DBUS_GERROR_REMOTE_EXCEPTION)
    g_set_error (gerror, DBUS_GERROR,
		 code,
		 "%s",
		 derror->message);
  else
    g_set_error (gerror, DBUS_GERROR,
		 code,
		 "%s%c%s",
		 derror->message ? derror->message : "",
		 '\0',
		 derror->name);
}

static void
dbus_g_error_info_free (gpointer p)
{
  DBusGErrorInfo *info;

  info = p;

  g_free (info->default_iface);
  g_free (info);
}

/**
 * SECTION:dbus-gobject
 * @short_description: Exporting a #GObject remotely
 * @see_also: #GObject
 * @stability: Stable
 *
 * FIXME
 */

/**
 * dbus_glib_global_set_disable_legacy_property_access:
 *
 * For historical reasons, DBus-GLib will allow read-only
 * access to every GObject property of an object exported
 * to the bus, regardless of whether or not the property
 * is listed in the type info installed with
 * dbus_g_object_type_install_info().  (Write access is
 * denied however).
 *
 * If you wish to restrict even read-only access, you
 * can call this method to globally change the behavior
 * for the entire process.
 *
 * Since: 0.88
 */
void
dbus_glib_global_set_disable_legacy_property_access (void)
{
  disable_legacy_property_access = TRUE;
}

/**
 * dbus_g_object_type_install_info:
 * @object_type: #GType for the object
 * @info: introspection data generated by #dbus-glib-tool
 *
 * Install introspection information about the given object #GType
 * sufficient to allow methods on the object to be invoked by name.
 * The introspection information is normally generated by
 * dbus-glib-tool, then this function is called in the
 * class_init() for the object class.
 *
 * Once introspection information has been installed, instances of the
 * object registered with dbus_g_connection_register_g_object() can have
 * their methods invoked remotely.
 */
void
dbus_g_object_type_install_info (GType                  object_type,
				 const DBusGObjectInfo *info)
{
  g_return_if_fail (G_TYPE_IS_CLASSED (object_type) || G_TYPE_IS_INTERFACE (object_type));

  _dbus_g_value_types_init ();

  g_type_set_qdata (object_type,
		    dbus_g_object_type_dbus_metadata_quark (),
		    (gpointer) info);
}

/**
 * dbus_g_error_domain_register:
 * @domain: the #GError domain
 * @default_iface: the prefix used for error values, or %NULL
 * @code_enum: a #GType for a #GEnum of the error codes
 *
 * Register a #GError domain and set of codes with D-Bus. When an object
 * raises a #GError in the domain @domain from one of its D-Bus methods,
 * the D-Bus error name used will be @default_iface, followed by a dot,
 * followed by the #GEnumValue.value_nick corresponding to the #GError.code.
 * For D-Bus, it's conventional to use an error name (value_nick) that is
 * in CamelCase.
 *
 * (For instance, if a D-Bus method <code>com.example.MyObject.GetThings</code>
 * can raise a #GError with domain <code>MY_ERROR</code> and code
 * <code>MY_ERROR_NOT_HAPPY</code>, you could call
 * <code>dbus_g_error_domain_register (MY_ERROR, "com.example.MyError",
 * MY_TYPE_ERROR)</code>, and set up the value_nick for
 * <code>MY_ERROR_NOT_HAPPY</code> to be <code>NotHappy</code>,
 * resulting in the D-Bus error string
 * <code>com.example.MyError.NotHappy</code>.)
 *
 * If @default_iface is %NULL, the D-Bus interface of the method that failed
 * will be used.
 *
 * (For instance, if the above example had called
 * <code>dbus_g_error_domain_register (MY_ERROR, NULL, MY_TYPE_ERROR)</code>
 * instead, then the D-Bus error string would be
 * <code>com.example.MyObject.NotHappy</code>.)
 */
void
dbus_g_error_domain_register (GQuark                domain,
			      const char           *default_iface,
			      GType                 code_enum)
{
  DBusGErrorInfo *info;
  
  g_return_if_fail (g_quark_to_string (domain) != NULL);
  g_return_if_fail (code_enum != G_TYPE_INVALID);
  g_return_if_fail (G_TYPE_FUNDAMENTAL (code_enum) == G_TYPE_ENUM);

  g_static_rw_lock_writer_lock (&globals_lock);

  if (error_metadata == NULL)
    g_datalist_init (&error_metadata);

  info = g_datalist_id_get_data (&error_metadata, domain);

  if (info != NULL)
    {
      g_warning ("Metadata for error domain \"%s\" already registered\n",
		 g_quark_to_string (domain));
    }
  else
    {
      info = g_new0 (DBusGErrorInfo, 1);
      info->default_iface = g_strdup (default_iface);
      info->code_enum = code_enum;

      g_datalist_id_set_data_full (&error_metadata,
				   domain,
				   info,
				   dbus_g_error_info_free);
    }

  g_static_rw_lock_writer_unlock (&globals_lock);
}

/* Called when the object is destroyed */
static void
object_export_object_died (gpointer user_data, GObject *dead)
{
  ObjectExport *oe = user_data;

  g_assert (dead == oe->object);

  /* this prevents the weak unref from taking place, which would cause an
   * assertion failure since the object has already gone... */
  oe->object = NULL;

  /* ... while this results in a call to object_registration_unregistered
   * for each contained registration */
  object_export_unregister_all (oe);

  /* We deliberately don't remove the ObjectExport yet, in case the object is
   * resurrected and re-registered: if that happens, we wouldn't want to call
   * export_signals() again. */
}

/**
 * dbus_g_connection_unregister_g_object:
 * @connection: the D-BUS connection
 * @object: the object
 *
 * Removes @object from any object paths at which it is exported on
 * @connection. Properties, methods, and signals
 * of the object can no longer be accessed remotely.
 */
void
dbus_g_connection_unregister_g_object (DBusGConnection *connection,
                                       GObject *object)
{
  ObjectExport *oe;
  GSList *registrations;

  g_return_if_fail (connection != NULL);
  g_return_if_fail (G_IS_OBJECT (object));

  oe = g_object_get_data (object, "dbus_glib_object_registrations");

  g_return_if_fail (oe != NULL);
  g_return_if_fail (oe->registrations != NULL);

  /* Copy the list before iterating it: it will be modified in
   * object_registration_free() each time an object path is unregistered.
   */
  for (registrations = g_slist_copy (oe->registrations);
      registrations != NULL;
      registrations = g_slist_delete_link (registrations, registrations))
    {
      ObjectRegistration *o = registrations->data;

      if (o->connection != connection)
        continue;

      dbus_connection_unregister_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection),
          o->object_path);
    }
}

/**
 * dbus_g_connection_register_g_object:
 * @connection: the D-BUS connection
 * @at_path: the path where the object will live (the object's name)
 * @object: the object
 *
 * Registers a #GObject at the given path. Properties, methods, and signals
 * of the object can then be accessed remotely. Methods are only available
 * if method introspection data has been added to the object's class
 * with dbus_g_object_type_install_info().
 *
 * The registration will be cancelled if either the #DBusConnection or
 * the #GObject gets finalized, or if dbus_g_connection_unregister_g_object()
 * is used.
 *
 * Note: If an object is registered multiple times, the first registration
 * takes priority for cases such as turning an object into an object path.
 */
void
dbus_g_connection_register_g_object (DBusGConnection       *connection,
                                     const char            *at_path,
                                     GObject               *object)
{
  ObjectExport *oe;
  GSList *iter;
  ObjectRegistration *o;
  DBusError error;

  g_return_if_fail (connection != NULL);
  g_return_if_fail (g_variant_is_object_path (at_path));
  g_return_if_fail (G_IS_OBJECT (object));

  oe = g_object_get_data (object, "dbus_glib_object_registrations");

  if (oe == NULL)
    {
      GList *info_list = lookup_object_info (object);

      if (info_list == NULL)
        {
          g_warning ("No introspection data registered for object class \"%s\"",
                     g_type_name (G_TYPE_FROM_INSTANCE (object)));
          return;
        }

      /* This adds a hook into every signal for the object.  Only do this
       * on the first registration, because inside the signal marshaller
       * we emit a signal for each registration.
       */
      export_signals (info_list, object);
      g_list_free (info_list);

      oe = object_export_new ();
      g_object_set_data_full (object, "dbus_glib_object_registrations", oe,
          (GDestroyNotify) object_export_free);
    }

  if (oe->object == NULL)
    {
      /* Either the ObjectExport is newly-created, or it already existed but
       * the object was disposed and resurrected, causing the weak ref to
       * fall off */
      oe->object = object;
      g_object_weak_ref (object, object_export_object_died, oe);
    }

  for (iter = oe->registrations; iter; iter = iter->next)
    {
      o = iter->data;

      /* Silently ignore duplicate registrations */
      if (strcmp (o->object_path, at_path) == 0 && o->connection == connection)
        return;
    }

  o = object_registration_new (connection, at_path, oe);

  dbus_error_init (&error);
  if (!dbus_connection_try_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
                                                 at_path,
                                                 &gobject_dbus_vtable,
                                                 o,
                                                 &error))
    {
      g_error ("Failed to register GObject with DBusConnection: %s %s",
               error.name, error.message);
      dbus_error_free (&error);
      object_registration_free (o);
      return;
    }

  oe->registrations = g_slist_append (oe->registrations, o);
}

/**
 * dbus_g_connection_lookup_g_object:
 * @connection: a #DBusGConnection
 * @at_path: path
 *
 * FIXME 
 *
 * Returns: the object at path @at_path
 */
GObject *
dbus_g_connection_lookup_g_object (DBusGConnection       *connection,
				   const char            *at_path)
{
  gpointer p;
  ObjectRegistration *o;

  g_return_val_if_fail (connection != NULL, NULL);
  g_return_val_if_fail (g_variant_is_object_path (at_path), NULL);

  if (!dbus_connection_get_object_path_data (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &p))
    return NULL;

  if (p == NULL)
    return NULL;

  o = p;

  if (o->export->object == NULL)
    return NULL;

  return G_OBJECT (o->export->object);
}

typedef struct {
  GType    rettype;
  guint    n_params;
  GType   *params;
} DBusGFuncSignature;

static guint
funcsig_hash (gconstpointer key)
{
  const DBusGFuncSignature *sig = key;
  GType *types;
  guint ret;
  guint i;

  ret = sig->rettype;
  types = sig->params;

  for (i = 0; i < sig->n_params; i++)
    {
      ret += (int) (*types);
      types++;
    }
      
  return ret;
}

static gboolean
funcsig_equal (gconstpointer aval,
	       gconstpointer bval)
{
  const DBusGFuncSignature *a = aval;
  const DBusGFuncSignature *b = bval;
  const GType *atypes;
  const GType *btypes;
  guint i;

  if (a->rettype != b->rettype
      || a->n_params != b->n_params)
    return FALSE;

  atypes = a->params;
  btypes = b->params;

  for (i = 0; i < a->n_params; i++)
    {
      if (*btypes != *atypes)
	return FALSE;
      atypes++;
      btypes++;
    }
      
  return TRUE;
}

static void
funcsig_free (DBusGFuncSignature *sig)
{
  g_free (sig->params);
  g_free (sig);
}

GClosureMarshal
_dbus_gobject_lookup_marshaller (GType        rettype,
				 guint        n_params,
				 const GType *param_types)
{
  GClosureMarshal ret;
  DBusGFuncSignature sig;
  GType *params;
  guint i;

  /* Convert to fundamental types */
  rettype = G_TYPE_FUNDAMENTAL (rettype);
  params = g_new (GType, n_params);
  for (i = 0; i < n_params; i++)
    params[i] = G_TYPE_FUNDAMENTAL (param_types[i]);

  sig.rettype = rettype;
  sig.n_params = n_params;
  sig.params = params;
  
  g_static_rw_lock_reader_lock (&globals_lock);

  if (marshal_table)
    ret = g_hash_table_lookup (marshal_table, &sig);
  else
    ret = NULL;

  g_static_rw_lock_reader_unlock (&globals_lock);

  if (ret == NULL)
    {
      if (rettype == G_TYPE_NONE)
	{
	  if (n_params == 0)
	    ret = g_cclosure_marshal_VOID__VOID;
	  else if (n_params == 1)
	    {
	      switch (params[0])
		{
		case G_TYPE_BOOLEAN:
		  ret = g_cclosure_marshal_VOID__BOOLEAN;
		  break;
		case G_TYPE_UCHAR:
		  ret = g_cclosure_marshal_VOID__UCHAR;
		  break;
		case G_TYPE_INT:
		  ret = g_cclosure_marshal_VOID__INT;
		  break;
		case G_TYPE_UINT:
		  ret = g_cclosure_marshal_VOID__UINT;
		  break;
		case G_TYPE_DOUBLE:
		  ret = g_cclosure_marshal_VOID__DOUBLE;
		  break;
		case G_TYPE_STRING:
		  ret = g_cclosure_marshal_VOID__STRING;
		  break;
		case G_TYPE_BOXED:
		  ret = g_cclosure_marshal_VOID__BOXED;
		  break;
		}
	    }
	  else if (n_params == 3
		   && params[0] == G_TYPE_STRING
		   && params[1] == G_TYPE_STRING
		   && params[2] == G_TYPE_STRING)
	    {
	      ret = _dbus_g_marshal_NONE__STRING_STRING_STRING;
	    }
	}
    }

  g_free (params);
  return ret;
}

/**
 * dbus_g_object_register_marshaller:
 * @marshaller: a GClosureMarshal to be used for invocation
 * @rettype: a GType for the return type of the function
 * @...: The parameter #GTypes, followed by %G_TYPE_INVALID
 *
 * Register a #GClosureMarshal to be used for signal invocations,
 * giving its return type and a list of parameter types,
 * followed by %G_TYPE_INVALID.
 *
 * This function will not be needed once GLib includes libffi.
 */
void
dbus_g_object_register_marshaller (GClosureMarshal  marshaller,
				   GType            rettype,
				   ...)
{
  va_list args;
  GArray *types;
  GType gtype;

  va_start (args, rettype);

  types = g_array_new (TRUE, TRUE, sizeof (GType));

  while ((gtype = va_arg (args, GType)) != G_TYPE_INVALID)
    g_array_append_val (types, gtype);

  dbus_g_object_register_marshaller_array (marshaller, rettype,
					   types->len, (GType*) types->data);

  g_array_free (types, TRUE);
  va_end (args);
}

/**
 * dbus_g_object_register_marshaller_array:
 * @marshaller: a #GClosureMarshal to be used for invocation
 * @rettype: a #GType for the return type of the function
 * @n_types: number of function parameters
 * @types: a C array of GTypes values
 *
 * Register a #GClosureMarshal to be used for signal invocations.
 * @see_also dbus_g_object_register_marshaller()
 */
void
dbus_g_object_register_marshaller_array (GClosureMarshal  marshaller,
					 GType            rettype,
					 guint            n_types,
					 const GType*     types)
{
  DBusGFuncSignature *sig;
  guint i;

  g_static_rw_lock_writer_lock (&globals_lock);

  if (marshal_table == NULL)
    marshal_table = g_hash_table_new_full (funcsig_hash,
					   funcsig_equal,
					   (GDestroyNotify) funcsig_free,
					   NULL);
  sig = g_new0 (DBusGFuncSignature, 1);
  sig->rettype = G_TYPE_FUNDAMENTAL (rettype);
  sig->n_params = n_types;
  sig->params = g_new (GType, n_types);
  for (i = 0; i < n_types; i++)
    sig->params[i] = G_TYPE_FUNDAMENTAL (types[i]);

  g_hash_table_insert (marshal_table, sig, marshaller);

  g_static_rw_lock_writer_unlock (&globals_lock);
}

/**
 * dbus_g_method_get_sender:
 * @context: the method context
 *
 * Get the sender of a message so we can send a
 * "reply" later (i.e. send a message directly
 * to a service which invoked the method at a
 * later time).
 *
 * Returns: the unique name of the sender. It
 * is up to the caller to free the returned string.
 */
gchar *
dbus_g_method_get_sender (DBusGMethodInvocation *context)
{
  const gchar *sender;

  g_return_val_if_fail (context != NULL, NULL);

  sender = dbus_message_get_sender (dbus_g_message_get_message (context->message));
  return g_strdup (sender);
}

/**
 * dbus_g_method_get_reply:
 * @context: the method context
 *
 * Get the reply message to append reply values
 * Used as a sidedoor when you can't generate dbus values
 * of the correct type due to glib binding limitations
 *
 * Returns: a #DBusMessage with the reply
 */
DBusMessage *
dbus_g_method_get_reply (DBusGMethodInvocation *context)
{
  g_return_val_if_fail (context != NULL, NULL);

  return reply_or_die (dbus_g_message_get_message (context->message));
}

/**
 * dbus_g_method_send_reply:
 * @context: the method context
 * @reply: the reply message, will be unreffed
 *
 * Send a manually created reply message.
 *
 * Used as a sidedoor when you can't generate dbus values
 * of the correct type due to glib binding limitations
 */
void
dbus_g_method_send_reply (DBusGMethodInvocation *context, DBusMessage *reply)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (reply != NULL);

  connection_send_or_die (dbus_g_connection_get_connection (context->connection),
      reply);
  dbus_message_unref (reply);

  dbus_g_connection_unref (context->connection);
  dbus_g_message_unref (context->message);
  g_free (context);
}


/**
 * dbus_g_method_return:
 * @context: the method context
 * @...: zero or more values to return from the method, with their number
 *    and types given by its #DBusGObjectInfo
 *
 * Send a return message for a given method invocation, with arguments.
 * This function also frees the sending context.
 */
void
dbus_g_method_return (DBusGMethodInvocation *context, ...)
{
  DBusMessage *reply;
  DBusMessageIter iter;
  va_list args;
  char *out_sig;
  GArray *argsig;
  guint i;

  g_return_if_fail (context != NULL);

  /* This field was initialized inside invoke_object_method; we
   * carry it over through the async invocation to here.
   */
  if (!context->send_reply)
    goto out;

  reply = dbus_g_method_get_reply (context);
  out_sig = method_output_signature_from_object_info (context->object, context->method);
  argsig = _dbus_gtypes_from_arg_signature (out_sig, FALSE);

  dbus_message_iter_init_append (reply, &iter);

  va_start (args, context);
  for (i = 0; i < argsig->len; i++)
    {
      GValue value = {0,};
      char *error;
      g_value_init (&value, g_array_index (argsig, GType, i));
      error = NULL;
      G_VALUE_COLLECT (&value, args, G_VALUE_NOCOPY_CONTENTS, &error);
      if (error)
	{
	  g_warning("%s", error);
	  g_free (error);
	}
      else
        {
          if (!_dbus_gvalue_marshal (&iter, &value))
            g_warning ("failed to marshal parameter %d for method %s",
                       i, dbus_message_get_member (
                         dbus_g_message_get_message (context->message)));
        }
    }
  va_end (args);

  connection_send_or_die (dbus_g_connection_get_connection (context->connection),
      reply);
  dbus_message_unref (reply);

  g_free (out_sig);
  g_array_free (argsig, TRUE);

out:
  dbus_g_connection_unref (context->connection);
  dbus_g_message_unref (context->message);
  g_free (context);
}

/**
 * dbus_g_method_return_error:
 * @context: the method context
 * @error: the error to send
 *
 * Send a error message for a given method invocation.
 * This function also frees the sending context.
 */
void
dbus_g_method_return_error (DBusGMethodInvocation *context, const GError *error)
{
  DBusMessage *reply;

  g_return_if_fail (context != NULL);
  g_return_if_fail (error != NULL);

  /* See comment in dbus_g_method_return */
  if (!context->send_reply)
    goto out;

  reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error);
  connection_send_or_die (
      dbus_g_connection_get_connection (context->connection), reply);
  dbus_message_unref (reply);

out:
  dbus_g_connection_unref (context->connection);
  dbus_g_message_unref (context->message);
  g_free (context);
}

/**
 * dbus_g_method_invocation_get_g_connection:
 * @context: the method context
 *
 * <!-- Returns: says it all -->
 *
 * Returns: (transfer none): the @DBusGConnection from which the method was called.
 */
DBusGConnection *
dbus_g_method_invocation_get_g_connection (DBusGMethodInvocation *context)
{
  g_return_val_if_fail (context != NULL, NULL);

  return context->connection;
}

const char *
_dbus_gobject_get_path (GObject *obj)
{
  ObjectExport *oe;
  ObjectRegistration *o;

  oe = g_object_get_data (obj, "dbus_glib_object_registrations");

  if (oe == NULL || oe->registrations == NULL)
    return NULL;

  /* First one to have been registered wins */
  o = oe->registrations->data;

  return o->object_path;
}

#ifdef DBUS_BUILD_TESTS
#include <stdlib.h>

static void
_dummy_function (void)
{
}

/* Data structures copied from one generated by current dbus-binding-tool;
 * we need to support this layout forever
 */
static const DBusGMethodInfo dbus_glib_internal_test_methods[] = {
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 0 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 49 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 117 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 191 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 270 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 320 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 391 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 495 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 623 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 693 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 765 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 838 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 911 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 988 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1064 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1140 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1204 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1278 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1347 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1408 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1460 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1533 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1588 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1647 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1730 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1784 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1833 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1895 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1947 },
  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1999 },
};

const DBusGObjectInfo dbus_glib_internal_test_object_info = {
  0,
  dbus_glib_internal_test_methods,
  30,
"org.freedesktop.DBus.Tests.MyObject\0DoNothing\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Increment\0S\0x\0I\0u\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementRetval\0S\0x\0I\0u\0arg1\0O\0F\0R\0u\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementRetvalError\0S\0x\0I\0u\0arg1\0O\0F\0E\0u\0\0org.freedesktop.DBus.Tests.MyObject\0ThrowError\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Uppercase\0S\0arg0\0I\0s\0arg1\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0ManyArgs\0S\0x\0I\0u\0str\0I\0s\0trouble\0I\0d\0d_ret\0O\0F\0N\0d\0str_ret\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0ManyReturn\0S\0arg0\0O\0F\0N\0u\0arg1\0O\0F\0N\0s\0arg2\0O\0F\0N\0i\0arg3\0O\0F\0N\0u\0arg4\0O\0F\0N\0u\0arg5\0O\0C\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0Stringify\0S\0val\0I\0v\0arg1\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0Unstringify\0S\0val\0I\0s\0arg1\0O\0F\0N\0v\0\0org.freedesktop.DBus.Tests.MyObject\0Recursive1\0S\0arg0\0I\0au\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0Recursive2\0S\0arg0\0I\0u\0arg1\0O\0F\0N\0au\0\0org.freedesktop.DBus.Tests.MyObject\0ManyUppercase\0S\0arg0\0I\0as\0arg1\0O\0F\0N\0as\0\0org.freedesktop.DBus.Tests.MyObject\0StrHashLen\0S\0arg0\0I\0a{ss}\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0SendCar\0S\0arg0\0I\0(suv)\0arg1\0O\0F\0N\0(uo)\0\0org.freedesktop.DBus.Tests.MyObject\0GetHash\0S\0arg0\0O\0F\0N\0a{ss}\0\0org.freedesktop.DBus.Tests.MyObject\0RecArrays\0S\0val\0I\0aas\0arg1\0O\0F\0N\0aau\0\0org.freedesktop.DBus.Tests.MyObject\0Objpath\0S\0arg0\0I\0o\0arg1\0O\0C\0N\0o\0\0org.freedesktop.DBus.Tests.MyObject\0GetObjs\0S\0arg0\0O\0F\0N\0ao\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementVal\0S\0\0org.freedesktop.DBus.Tests.MyObject\0AsyncIncrement\0A\0x\0I\0u\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0AsyncThrowError\0A\0\0org.freedesktop.DBus.Tests.MyObject\0GetVal\0S\0arg0\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0ManyStringify\0S\0arg0\0I\0a{sv}\0arg1\0O\0F\0N\0a{sv}\0\0org.freedesktop.DBus.Tests.MyObject\0EmitFrobnicate\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Terminate\0S\0\0org.freedesktop.DBus.Tests.FooObject\0GetValue\0S\0arg0\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.FooObject\0EmitSignals\0S\0\0org.freedesktop.DBus.Tests.FooObject\0EmitSignal2\0S\0\0org.freedesktop.DBus.Tests.FooObject\0Terminate\0S\0\0\0",
"org.freedesktop.DBus.Tests.MyObject\0Frobnicate\0org.freedesktop.DBus.Tests.FooObject\0Sig0\0org.freedesktop.DBus.Tests.FooObject\0Sig1\0org.freedesktop.DBus.Tests.FooObject\0Sig2\0\0",
"\0"
};


/*
 * Unit test for GLib GObject integration ("skeletons")
 * Returns: %TRUE on success.
 */
gboolean
_dbus_gobject_test (const char *test_data_dir)
{
  int i;
  const char *arg;
  const char *arg_name;
  gboolean arg_in;
  gboolean constval;
  RetvalType retval;
  const char *arg_signature;
  const char *sigdata;
  const char *iface;
  const char *signame;
  
  static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
    { "SetFoo", "set_foo" },
    { "Foo", "foo" },
    { "GetFooBar", "get_foo_bar" },
    { "Hello", "hello" }
    
    /* Impossible-to-handle cases */
    /* { "FrobateUIHandler", "frobate_ui_handler" } */
  };

  /* Test lookup in our hardcoded object info; if these tests fail
   * then it likely means you changed the generated object info in an
   * incompatible way and broke the lookup functions.  In that case
   * you need to bump the version and use a new structure instead. */
  /* DoNothing */
  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
					  &(dbus_glib_internal_test_methods[0]));
  g_assert (*arg == '\0');

  /* Increment */
  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
					  &(dbus_glib_internal_test_methods[1]));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (!strcmp (arg_name, "x"));
  g_assert (arg_in == TRUE);
  g_assert (!strcmp (arg_signature, "u"));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (arg_in == FALSE);
  g_assert (retval == RETVAL_NONE);
  g_assert (!strcmp (arg_signature, "u"));
  g_assert (*arg == '\0');

  /* IncrementRetval */
  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
					  &(dbus_glib_internal_test_methods[2]));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (!strcmp (arg_name, "x"));
  g_assert (arg_in == TRUE);
  g_assert (!strcmp (arg_signature, "u"));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (retval == RETVAL_NOERROR);
  g_assert (arg_in == FALSE);
  g_assert (!strcmp (arg_signature, "u"));
  g_assert (*arg == '\0');

  /* IncrementRetvalError */
  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
					  &(dbus_glib_internal_test_methods[3]));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (!strcmp (arg_name, "x"));
  g_assert (arg_in == TRUE);
  g_assert (!strcmp (arg_signature, "u"));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (retval == RETVAL_ERROR);
  g_assert (arg_in == FALSE);
  g_assert (!strcmp (arg_signature, "u"));
  g_assert (*arg == '\0');
  
  /* Stringify */
  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
					  &(dbus_glib_internal_test_methods[8]));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (!strcmp (arg_name, "val"));
  g_assert (arg_in == TRUE);
  g_assert (!strcmp (arg_signature, "v"));
  g_assert (*arg != '\0');
  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
  g_assert (retval == RETVAL_NONE);
  g_assert (arg_in == FALSE);
  g_assert (!strcmp (arg_signature, "s"));
  g_assert (*arg == '\0');

  sigdata = dbus_glib_internal_test_object_info.exported_signals;
  g_assert (*sigdata != '\0');
  sigdata = signal_iterate (sigdata, &iface, &signame);
  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.MyObject"));
  g_assert (!strcmp (signame, "Frobnicate"));
  g_assert (*sigdata != '\0');
  sigdata = signal_iterate (sigdata, &iface, &signame);
  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
  g_assert (!strcmp (signame, "Sig0"));
  g_assert (*sigdata != '\0');
  sigdata = signal_iterate (sigdata, &iface, &signame);
  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
  g_assert (!strcmp (signame, "Sig1"));
  g_assert (*sigdata != '\0');
  sigdata = signal_iterate (sigdata, &iface, &signame);
  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
  g_assert (!strcmp (signame, "Sig2"));
  g_assert (*sigdata == '\0');


  i = 0;
  while (i < (int) G_N_ELEMENTS (name_pairs))
    {
      char *uscore;
      char *wincaps;

      uscore = _dbus_gutils_wincaps_to_uscore (name_pairs[i].wincaps);
      wincaps = uscore_to_wincaps (name_pairs[i].uscore);

      if (strcmp (uscore, name_pairs[i].uscore) != 0)
        {
          g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
                      name_pairs[i].wincaps, name_pairs[i].uscore,
                      uscore);
          exit (1);
        }
      
      if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
        {
          g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
                      name_pairs[i].uscore, name_pairs[i].wincaps,
                      wincaps);
          exit (1);
        }
      
      g_free (uscore);
      g_free (wincaps);

      ++i;
    }
  
  return TRUE;
}

#endif /* DBUS_BUILD_TESTS */
