[glib/gdbus-daemon: 2/6] Initial version of GDbusDaemon internal object



commit d7ff9b25f94134da1657ec4a9a178d8aabcbd361
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Apr 16 10:16:04 2012 +0200

    Initial version of GDbusDaemon internal object

 gio/.gitignore      |    1 +
 gio/Makefile.am     |   17 +-
 gio/dbus-daemon.xml |   73 +++
 gio/gdbusdaemon.c   | 1623 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gdbusdaemon.h   |   19 +
 5 files changed, 1732 insertions(+), 1 deletions(-)
---
diff --git a/gio/.gitignore b/gio/.gitignore
index 5646f7d..4a7c510 100644
--- a/gio/.gitignore
+++ b/gio/.gitignore
@@ -3,6 +3,7 @@ gdbus
 gdbus-example-objectmanager-generated-org.gtk.GDBus.Example.ObjectManager.Animal.xml
 gdbus-example-objectmanager-generated-org.gtk.GDBus.Example.ObjectManager.Cat.xml
 gdbus-example-objectmanager-generated.[ch]
+gdbus-daemon-generated.[ch]
 gio-marshal.[ch]
 gio-public-headers.txt
 gio-querymodules
diff --git a/gio/Makefile.am b/gio/Makefile.am
index edb534a..1ca7837 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -96,6 +96,19 @@ gdbus_sources = 							\
 	gdbusobjectmanagerserver.h	gdbusobjectmanagerserver.c	\
 	$(NULL)
 
+# These are not built into the library yet
+EXTRA_DIST += gdbusdaemon.c gdbusdaemon.h
+
+gdbus-daemon-generated.h gdbus-daemon-generated.c : dbus-daemon.xml Makefile $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen
+	$(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \
+		UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \
+		$(PYTHON) $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen \
+		--interface-prefix org. \
+		--generate-c-code gdbus-daemon-generated \
+		--c-namespace _G \
+		$(srcdir)/dbus-daemon.xml \
+		$(NULL)
+
 settings_headers = \
 	gsettingsbackend.h		\
 	gsettingsschema.h		\
@@ -609,6 +622,8 @@ BUILT_SOURCES = 		\
 	gconstructor_as_data.h	\
 	gioenumtypes.h		\
 	gioenumtypes.c		\
+	gdbus-daemon-generated.c \
+	gdbus-daemon-generated.h \
 	$(NULL)
 
 EXTRA_DIST += 			\
@@ -630,7 +645,7 @@ BUILT_EXTRA_DIST = 		\
 gio-public-headers.txt: Makefile
 	echo $(gioinclude_HEADERS) $(giowin32include_HEADERS) $(giounixinclude_HEADERS) > $  tmp && mv $  tmp $@
 
-CLEANFILES = gio-public-headers.txt gconstructor_as_data.h
+CLEANFILES = gdbus-daemon-generated.c gdbus-daemon-generated.h gio-public-headers.txt gconstructor_as_data.h
 
 all-local: gio-public-headers.txt
 
diff --git a/gio/dbus-daemon.xml b/gio/dbus-daemon.xml
new file mode 100644
index 0000000..2a5a414
--- /dev/null
+++ b/gio/dbus-daemon.xml
@@ -0,0 +1,73 @@
+<node>
+  <interface name="org.freedesktop.DBus">
+    <method name="Hello">
+      <arg direction="out" type="s" name="assigned_name"/>
+    </method>
+    <method name="RequestName">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="in" type="u" name="flags"/>
+      <arg direction="out" type="u" name="value"/>
+    </method>
+    <method name="ReleaseName">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="u" name="value"/>
+    </method>
+    <method name="StartServiceByName">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="in" type="u" name="flags"/>
+      <arg direction="out" type="u" name="value"/>
+    </method>
+    <method name="NameHasOwner">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="b" name="has_owner"/>
+    </method>
+    <method name="ListNames">
+      <arg direction="out" type="as" name="names"/>
+    </method>
+    <method name="ListActivatableNames">
+      <arg direction="out" type="as" name="activatable_names"/>
+    </method>
+    <method name="AddMatch">
+      <arg direction="in" type="s" name="rule"/>
+    </method>
+    <method name="RemoveMatch">
+      <arg direction="in" type="s" name="rule"/>
+    </method>
+    <method name="GetNameOwner">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="s" name="unique_name"/>
+    </method>
+    <method name="ListQueuedOwners">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="as" name="queued_owners"/>
+    </method>
+    <method name="GetConnectionUnixUser">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="u" name="uid"/>
+    </method>
+    <method name="GetConnectionUnixProcessID">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="u" name="pid"/>
+    </method>
+    <method name="GetConnectionSELinuxSecurityContext">
+      <arg direction="in" type="s" name="name"/>
+      <arg direction="out" type="ay" name="security_context"/>
+    </method>
+    <method name="ReloadConfig">
+    </method>
+    <method name="GetId">
+      <arg direction="out" type="s" name="unique_id"/>
+    </method>
+    <signal name="NameOwnerChanged">
+      <arg type="s" name="name"/>
+      <arg type="s" name="old_owner"/>
+      <arg type="s" name="new_owner"/>
+    </signal>
+    <signal name="NameLost">
+      <arg type="s" name="name"/>
+    </signal>
+    <signal name="NameAcquired">
+      <arg type="s" name="name"/>
+    </signal>
+  </interface>
+</node>
diff --git a/gio/gdbusdaemon.c b/gio/gdbusdaemon.c
new file mode 100644
index 0000000..6d1bd70
--- /dev/null
+++ b/gio/gdbusdaemon.c
@@ -0,0 +1,1623 @@
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gio/gio.h>
+#include <gio/gunixsocketaddress.h>
+#include "gdbusdaemon.h"
+
+#include "gdbus-daemon-generated.h"
+
+#define DBUS_SERVICE_NAME  "org.freedesktop.DBus"
+
+/* Owner flags */
+#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */
+#define DBUS_NAME_FLAG_REPLACE_EXISTING  0x2 /**< Request to replace the current primary owner */
+#define DBUS_NAME_FLAG_DO_NOT_QUEUE      0x4 /**< If we can not become the primary owner do not place us in the queue */
+
+/* Replies to request for a name */
+#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER  1 /**< Service has become the primary owner of the requested name */
+#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE       2 /**< Service could not become the primary owner and has been placed in the queue */
+#define DBUS_REQUEST_NAME_REPLY_EXISTS         3 /**< Service is already in the queue */
+#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER  4 /**< Service is already the primary owner */
+
+/* Replies to releasing a name */
+#define DBUS_RELEASE_NAME_REPLY_RELEASED        1 /**< Service was released from the given name */
+#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT    2 /**< The given name does not exist on the bus */
+#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER       3 /**< Service is not an owner of the given name */
+
+/* Replies to service starts */
+#define DBUS_START_REPLY_SUCCESS         1 /**< Service was auto started */
+#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */
+
+struct _GDBusDaemon
+{
+  _GFreedesktopDBusSkeleton parent_instance;
+
+  gchar *address;
+  gchar *tmpdir;
+  GDBusServer *server;
+  gchar *guid;
+  GHashTable *clients;
+  GHashTable *names;
+  guint32 next_major_id;
+  guint32 next_minor_id;
+};
+
+struct _GDBusDaemonClass
+{
+  _GFreedesktopDBusSkeletonClass parent_class;
+};
+
+enum {
+  PROP_0,
+  PROP_ADDRESS
+};
+
+static void initable_iface_init       (GInitableIface      *initable_iface);
+static void g_dbus_daemon_iface_init (_GFreedesktopDBusIface *iface);
+
+#define g_dbus_daemon_get_type _g_dbus_daemon_get_type
+G_DEFINE_TYPE_WITH_CODE (GDBusDaemon, g_dbus_daemon, _G_TYPE_FREEDESKTOP_DBUS_SKELETON,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+			 G_IMPLEMENT_INTERFACE (_G_TYPE_FREEDESKTOP_DBUS, g_dbus_daemon_iface_init));
+
+typedef struct {
+  GDBusDaemon *daemon;
+  char *id;
+  GDBusConnection *connection;
+  GList *matches;
+} Client;
+
+typedef struct {
+  Client *client;
+  guint32 flags;
+} NameOwner;
+
+typedef struct {
+  int refcount;
+
+  char *name;
+  GDBusDaemon *daemon;
+
+  NameOwner *owner;
+  GList *queue;
+} Name;
+
+enum {
+  MATCH_ELEMENT_TYPE,
+  MATCH_ELEMENT_SENDER,
+  MATCH_ELEMENT_INTERFACE,
+  MATCH_ELEMENT_MEMBER,
+  MATCH_ELEMENT_PATH,
+  MATCH_ELEMENT_PATH_NAMESPACE,
+  MATCH_ELEMENT_DESTINATION,
+  MATCH_ELEMENT_ARG0NAMESPACE,
+  MATCH_ELEMENT_EAVESDROP,
+  MATCH_ELEMENT_ARGN,
+  MATCH_ELEMENT_ARGNPATH,
+};
+
+typedef struct {
+  guint16 type;
+  guint16 arg;
+  char *value;
+} MatchElement;
+
+typedef struct {
+  gboolean eavesdrop;
+  GDBusMessageType type;
+  int n_elements;
+  MatchElement *elements;
+} Match;
+
+
+static NameOwner *
+name_owner_new (Client *client, guint32 flags)
+{
+  NameOwner *owner;
+
+  owner = g_new0 (NameOwner, 1);
+  owner->client = client;
+  owner->flags = flags;
+  return owner;
+}
+
+static void
+name_owner_free (NameOwner *owner)
+{
+  g_free (owner);
+}
+
+static Name *
+name_new (GDBusDaemon *daemon, const char *str)
+{
+  Name *name;
+
+  name = g_new0 (Name, 1);
+  name->refcount = 1;
+  name->daemon = daemon;
+  name->name = g_strdup (str);
+
+  g_hash_table_insert (daemon->names, name->name, name);
+
+  return name;
+}
+
+static Name *
+name_ref (Name *name)
+{
+  name->refcount++;
+  return name;
+}
+
+static void
+name_unref (Name *name)
+{
+  if (--name->refcount == 0)
+    {
+      g_hash_table_remove (name->daemon->names, name->name);
+      g_free (name->name);
+      g_free (name);
+    }
+}
+
+static Name *
+name_ensure (GDBusDaemon *daemon, const char *str)
+{
+  Name *name;
+
+  name = g_hash_table_lookup (daemon->names, str);
+
+  if (name != NULL)
+    return name_ref (name);
+  return name_new (daemon, str);
+}
+
+static Name *
+name_lookup (GDBusDaemon *daemon, const char *str)
+{
+  return g_hash_table_lookup (daemon->names, str);
+}
+
+static gboolean
+is_key (const char *key_start, const char *key_end, char *value)
+{
+  gsize len = strlen (value);
+
+  if (len != key_end - key_start)
+    return FALSE;
+
+  return strncmp (key_start, value, len) == 0;
+}
+
+static gboolean
+parse_key (MatchElement *element, const char *key_start, const char *key_end)
+{
+  gboolean res = TRUE;
+
+  if (is_key (key_start, key_end, "type"))
+    {
+      element->type = MATCH_ELEMENT_TYPE;
+    }
+  else if (is_key (key_start, key_end, "sender"))
+    {
+      element->type = MATCH_ELEMENT_SENDER;
+    }
+  else if (is_key (key_start, key_end, "interface"))
+    {
+      element->type = MATCH_ELEMENT_INTERFACE;
+    }
+  else if (is_key (key_start, key_end, "member"))
+    {
+      element->type = MATCH_ELEMENT_MEMBER;
+    }
+  else if (is_key (key_start, key_end, "path"))
+    {
+      element->type = MATCH_ELEMENT_PATH;
+    }
+  else if (is_key (key_start, key_end, "path_namespace"))
+    {
+      element->type = MATCH_ELEMENT_PATH_NAMESPACE;
+    }
+  else if (is_key (key_start, key_end, "destination"))
+    {
+      element->type = MATCH_ELEMENT_DESTINATION;
+    }
+  else if (is_key (key_start, key_end, "arg0namespace"))
+    {
+      element->type = MATCH_ELEMENT_ARG0NAMESPACE;
+    }
+  else if (is_key (key_start, key_end, "eavesdrop"))
+    {
+      element->type = MATCH_ELEMENT_EAVESDROP;
+    }
+  else if (key_end - key_start > 3 && is_key (key_start, key_start + 3, "arg"))
+    {
+      const char *digits = key_start + 3;
+      const char *end_digits = digits;
+
+      while (end_digits < key_end && g_ascii_isdigit (*end_digits))
+	end_digits++;
+
+      if (end_digits == key_end) /* argN */
+	{
+	  element->type = MATCH_ELEMENT_ARGN;
+	  element->arg = atoi (digits);
+	}
+      else if (is_key (end_digits, key_end, "path")) /* argNpath */
+	{
+	  element->type = MATCH_ELEMENT_ARGNPATH;
+	  element->arg = atoi (digits);
+	}
+      else
+	res = FALSE;
+    }
+  else
+    res = FALSE;
+
+  return res;
+}
+
+static const char *
+parse_value (MatchElement *element, const char *s)
+{
+  char quote_char;
+  GString *value;
+
+  value = g_string_new ("");
+
+  quote_char = 0;
+
+  for (;*s; s++)
+    {
+      if (quote_char == 0)
+	{
+	  switch (*s)
+	    {
+	    case '\'':
+	      quote_char = '\'';
+	      break;
+
+	    case ',':
+	      s++;
+	      goto out;
+
+	    case '\\':
+	      quote_char = '\\';
+	      break;
+
+	    default:
+	      g_string_append_c (value, *s);
+	      break;
+	    }
+	}
+      else if (quote_char == '\\')
+	{
+	  /* \ only counts as an escape if escaping a quote mark */
+	  if (*s != '\'')
+	    g_string_append_c (value, '\\');
+
+	  g_string_append_c (value, *s);
+	  quote_char = 0;
+	}
+      else /* quote_char == ' */
+	{
+	  if (*s == '\'')
+	    quote_char = 0;
+	  else
+	    g_string_append_c (value, *s);
+	}
+    }
+
+ out:
+
+  if (quote_char == '\\')
+    g_string_append_c (value, '\\');
+  else if (quote_char == '\'')
+    {
+      g_string_free (value, TRUE);
+      return NULL;
+    }
+
+  element->value = g_string_free (value, FALSE);
+  return s;
+}
+
+static Match *
+match_new (const char *str)
+{
+  Match *match;
+  GArray *elements;
+  const char *p;
+  const char *key_start;
+  const char *key_end;
+  MatchElement element;
+  gboolean eavesdrop;
+  GDBusMessageType type;
+  int i;
+
+  eavesdrop = FALSE;
+  type = G_DBUS_MESSAGE_TYPE_INVALID;
+  elements = g_array_new (TRUE, TRUE, sizeof (MatchElement));
+
+  p = str;
+
+  while (*p != 0)
+    {
+      memset (&element, 0, sizeof (element));
+
+      /* Skip initial whitespace */
+      while (*p && g_ascii_isspace (*p))
+	p++;
+
+      key_start = p;
+
+      /* Read non-whitespace non-equals chars */
+      while (*p && *p != '=' && !g_ascii_isspace (*p))
+	p++;
+
+      key_end = p;
+
+      /* Skip any whitespace after key */
+      while (*p && g_ascii_isspace (*p))
+	p++;
+
+      if (key_start == key_end)
+	continue; /* Allow trailing whitespace */
+
+      if (*p != '=')
+	goto error;
+
+      ++p;
+
+      if (!parse_key (&element, key_start, key_end))
+	goto error;
+
+      p = parse_value (&element, p);
+      if (p == NULL)
+	goto error;
+
+      if (element.type == MATCH_ELEMENT_EAVESDROP)
+	{
+	  if (strcmp (element.value, "true") == 0)
+	    eavesdrop = TRUE;
+	  else if (strcmp (element.value, "false") == 0)
+	    eavesdrop = FALSE;
+	  else
+	    {
+	      g_free (element.value);
+	      goto error;
+	    }
+	  g_free (element.value);
+	}
+      else if (element.type == MATCH_ELEMENT_TYPE)
+	{
+	  if (strcmp (element.value, "signal") == 0)
+	    type = G_DBUS_MESSAGE_TYPE_SIGNAL;
+	  else if (strcmp (element.value, "method_call") == 0)
+	    type = G_DBUS_MESSAGE_TYPE_METHOD_CALL;
+	  else if (strcmp (element.value, "method_return") == 0)
+	    type = G_DBUS_MESSAGE_TYPE_METHOD_RETURN;
+	  else if (strcmp (element.value, "error") == 0)
+	    type = G_DBUS_MESSAGE_TYPE_ERROR;
+	  else
+	    {
+	      g_free (element.value);
+	      goto error;
+	    }
+	  g_free (element.value);
+	}
+      else
+	g_array_append_val (elements, element);
+    }
+
+  match = g_new0 (Match, 1);
+  match->n_elements = elements->len;
+  match->elements = (MatchElement *)g_array_free (elements, FALSE);
+  match->eavesdrop = eavesdrop;
+  match->type = type;
+
+  return match;
+
+ error:
+  for (i = 0; i < elements->len; i++)
+    g_free (g_array_index (elements, MatchElement, i).value);
+  g_array_free (elements, TRUE);
+  return NULL;
+}
+
+static void
+match_free (Match *match)
+{
+  int i;
+  for (i = 0; i < match->n_elements; i++)
+    g_free (match->elements[i].value);
+  g_free (match->elements);
+  g_free (match);
+}
+
+static gboolean
+match_equal (Match *a, Match *b)
+{
+  int i;
+
+  if (a->eavesdrop != b->eavesdrop)
+    return FALSE;
+  if (a->type != b->type)
+    return FALSE;
+ if (a->n_elements != b->n_elements)
+    return FALSE;
+  for (i = 0; i < a->n_elements; i++)
+    {
+      if (a->elements[i].type != b->elements[i].type ||
+	  a->elements[i].arg != b->elements[i].arg ||
+	  strcmp (a->elements[i].value, b->elements[i].value) != 0)
+	return FALSE;
+    }
+  return TRUE;
+}
+
+static const gchar *
+message_get_argN (GDBusMessage *message, int n, gboolean allow_path)
+{
+  const gchar *ret;
+  GVariant *body;
+
+  ret = NULL;
+
+  body = g_dbus_message_get_body (message);
+
+  if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE_TUPLE))
+    {
+      GVariant *item;
+      item = g_variant_get_child_value (body, n);
+      if (g_variant_is_of_type (item, G_VARIANT_TYPE_STRING) ||
+	  (allow_path && g_variant_is_of_type (item, G_VARIANT_TYPE_OBJECT_PATH)))
+	ret = g_variant_get_string (item, NULL);
+      g_variant_unref (item);
+    }
+
+  return ret;
+}
+
+enum {
+  CHECK_TYPE_STRING,
+  CHECK_TYPE_NAME,
+  CHECK_TYPE_PATH_PREFIX,
+  CHECK_TYPE_PATH_RELATED,
+  CHECK_TYPE_NAMESPACE_PREFIX
+};
+
+static gboolean
+match_matches (GDBusDaemon *daemon,
+	       Match *match, GDBusMessage *message,
+	       gboolean has_destination)
+{
+  MatchElement *element;
+  Name *name;
+  int i, len, len2;
+  const char *value;
+  int check_type;
+
+  if (has_destination && !match->eavesdrop)
+    return FALSE;
+
+  if (match->type != G_DBUS_MESSAGE_TYPE_INVALID &&
+      g_dbus_message_get_message_type (message) != match->type)
+    return FALSE;
+
+  for (i = 0; i < match->n_elements; i++)
+    {
+      element = &match->elements[i];
+      check_type = CHECK_TYPE_STRING;
+      switch (element->type)
+	{
+	case MATCH_ELEMENT_SENDER:
+	  check_type = CHECK_TYPE_NAME;
+	  value = g_dbus_message_get_sender (message);
+	  if (value == NULL)
+	    value = DBUS_SERVICE_NAME;
+	  break;
+	case MATCH_ELEMENT_DESTINATION:
+	  check_type = CHECK_TYPE_NAME;
+	  value = g_dbus_message_get_destination (message);
+	  break;
+	case MATCH_ELEMENT_INTERFACE:
+	  value = g_dbus_message_get_interface (message);
+	  break;
+	case MATCH_ELEMENT_MEMBER:
+	  value = g_dbus_message_get_member (message);
+	  break;
+	case MATCH_ELEMENT_PATH:
+	  value = g_dbus_message_get_path (message);
+	  break;
+	case MATCH_ELEMENT_PATH_NAMESPACE:
+	  check_type = CHECK_TYPE_PATH_PREFIX;
+	  value = g_dbus_message_get_path (message);
+	  break;
+	case MATCH_ELEMENT_ARG0NAMESPACE:
+	  check_type = CHECK_TYPE_NAMESPACE_PREFIX;
+	  value = message_get_argN (message, 0, FALSE);
+	  break;
+	case MATCH_ELEMENT_ARGN:
+	  value = message_get_argN (message, element->arg, FALSE);
+	  break;
+	case MATCH_ELEMENT_ARGNPATH:
+	  check_type = CHECK_TYPE_PATH_RELATED;
+	  value = message_get_argN (message, element->arg, TRUE);
+	  break;
+	default:
+	case MATCH_ELEMENT_TYPE:
+	case MATCH_ELEMENT_EAVESDROP:
+	  g_assert_not_reached ();
+	}
+
+      if (value == NULL)
+	return FALSE;
+
+      switch (check_type)
+	{
+	case CHECK_TYPE_STRING:
+	  if (strcmp (element->value, value) != 0)
+	    return FALSE;
+	  break;
+	case CHECK_TYPE_NAME:
+	  name = name_lookup (daemon, element->value);
+	  if (name != NULL)
+	    {
+	      if (strcmp (name->owner->client->id, value) != 0)
+		return FALSE;
+	    }
+	  else if (strcmp (element->value, value) != 0)
+	    return FALSE;
+	  break;
+	case CHECK_TYPE_PATH_PREFIX:
+	  len = strlen (element->value);
+	  if (!(g_str_has_prefix (value, element->value) &&
+		(value[len] == 0 || value[len] == '/')))
+	    return FALSE;
+	  break;
+	case CHECK_TYPE_PATH_RELATED:
+	  len = strlen (element->value);
+	  len2 = strlen (value);
+
+	  if (!(strcmp (value, element->value) == 0 ||
+		(len2 > 0 && value[len2-1] == '/' && g_str_has_prefix (element->value, value)) ||
+		(len > 0 && element->value[len-1] == '/' && g_str_has_prefix (value, element->value))))
+	    return FALSE;
+	  break;
+	case CHECK_TYPE_NAMESPACE_PREFIX:
+	  len = strlen (element->value);
+	  if (!(g_str_has_prefix (value, element->value) &&
+		(value[len] == 0 || value[len] == '.')))
+	    return FALSE;
+	  break;
+	default:
+	  g_assert_not_reached ();
+	}
+    }
+
+  return TRUE;
+}
+
+static void
+broadcast_message (GDBusDaemon *daemon,
+		   GDBusMessage *message,
+		   gboolean has_destination,
+		   gboolean preserve_serial,
+		   Client *not_to)
+{
+  GList *clients, *l, *ll;
+  GDBusMessage *copy;
+
+  clients = g_hash_table_get_values (daemon->clients);
+  for (l = clients; l != NULL; l = l->next)
+    {
+      Client *client = l->data;
+
+      if (client == not_to)
+	continue;
+
+      for (ll = client->matches; ll != NULL; ll = ll->next)
+	{
+	  Match *match = ll->data;
+
+	  if (match_matches (daemon, match, message, has_destination))
+	    break;
+	}
+
+      if (ll != NULL)
+	{
+	  copy = g_dbus_message_copy (message, NULL);
+	  if (copy)
+	    {
+	      g_dbus_connection_send_message (client->connection, copy,
+					      preserve_serial?G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL:0, NULL, NULL);
+	      g_object_unref (copy);
+	    }
+	}
+    }
+
+  g_list_free (clients);
+}
+
+static void
+send_name_owner_changed (GDBusDaemon *daemon,
+			 const char *name,
+			 const char *old_owner,
+			 const char *new_owner)
+{
+  GDBusMessage *signal_message;
+
+  signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
+					      "org.freedesktop.DBus",
+					      "NameOwnerChanged");
+  g_dbus_message_set_body (signal_message,
+			   g_variant_new ("(sss)",
+					  name,
+					  old_owner ? old_owner : "",
+					  new_owner ? new_owner : ""));
+
+  broadcast_message (daemon, signal_message, FALSE, FALSE, NULL);
+  g_object_unref (signal_message);
+
+}
+
+static gboolean
+name_unqueue_owner (Name *name, Client *client)
+{
+  GList *l;
+
+  for (l = name->queue; l != NULL; l = l->next)
+    {
+      NameOwner *other = l->data;
+
+      if (other->client == client)
+	{
+	  name->queue = g_list_delete_link (name->queue, l);
+	  name_unref (name);
+	  name_owner_free (other);
+	  return TRUE;
+	}
+    }
+
+  return FALSE;
+}
+
+static void
+name_replace_owner (Name *name, NameOwner *owner)
+{
+  GDBusDaemon *daemon = name->daemon;
+  NameOwner *old_owner;
+  char *old_name = NULL, *new_name = NULL;
+  Client *new_client = NULL;
+
+  if (owner)
+    new_client = owner->client;
+
+  name_ref (name);
+
+  old_owner = name->owner;
+  if (old_owner)
+    {
+      Client *old_client = old_owner->client;
+
+      g_assert (old_owner->client != new_client);
+
+      g_dbus_connection_emit_signal (old_client->connection,
+				     NULL, "/org/freedesktop/DBus",
+				     "org.freedesktop.DBus", "NameLost",
+				     g_variant_new ("(s)",
+						    name->name), NULL);
+
+      old_name = g_strdup (old_client->id);
+      if (old_owner->flags & DBUS_NAME_FLAG_DO_NOT_QUEUE)
+	{
+	  name_unref (name);
+	  name_owner_free (old_owner);
+	}
+      else
+	name->queue = g_list_prepend (name->queue, old_owner);
+    }
+
+  name->owner = owner;
+  if (owner)
+    {
+      name_unqueue_owner (name, owner->client);
+      name_ref (name);
+      new_name = new_client->id;
+
+      g_dbus_connection_emit_signal (new_client->connection,
+				     NULL, "/org/freedesktop/DBus",
+				     "org.freedesktop.DBus", "NameAcquired",
+				     g_variant_new ("(s)",
+						    name->name), NULL);
+    }
+
+  send_name_owner_changed (daemon, name->name, old_name, new_name);
+
+  g_free (old_name);
+
+  name_unref (name);
+}
+
+static void
+name_release_owner (Name *name)
+{
+  NameOwner *next_owner = NULL;
+
+  name_ref (name);
+
+  /* Will someone else take over? */
+  if (name->queue)
+    {
+      next_owner = name->queue->data;
+      name_unref (name);
+      name->queue = g_list_delete_link (name->queue, name->queue);
+    }
+
+  name->owner->flags |= DBUS_NAME_FLAG_DO_NOT_QUEUE;
+  name_replace_owner (name, next_owner);
+
+  name_unref (name);
+}
+
+static void
+name_queue_owner (Name *name, NameOwner *owner)
+{
+  GList *l;
+
+  for (l = name->queue; l != NULL; l = l->next)
+    {
+      NameOwner *other = l->data;
+
+      if (other->client == owner->client)
+	{
+	  other->flags = owner->flags;
+	  name_owner_free (owner);
+	  return;
+	}
+    }
+
+  name->queue = g_list_append (name->queue, owner);
+  name_ref (name);
+}
+
+static Client *
+client_new (GDBusDaemon *daemon, GDBusConnection *connection)
+{
+  Client *client;
+
+  client = g_new0 (Client, 1);
+  client->daemon = daemon;
+  client->id = g_strdup_printf (":%d.%d", daemon->next_major_id, daemon->next_minor_id);
+  client->connection = connection;
+
+  if (daemon->next_minor_id == G_MAXUINT32)
+    {
+      daemon->next_minor_id = 0;
+      daemon->next_major_id++;
+    }
+  else
+    daemon->next_minor_id++;
+
+  g_object_set_data (G_OBJECT (connection), "client", client);
+  g_hash_table_insert (daemon->clients, client->id, client);
+
+  send_name_owner_changed (daemon, client->id, NULL, client->id);
+
+  return client;
+}
+
+static void
+client_free (Client *client)
+{
+  GDBusDaemon *daemon = client->daemon;
+  GList *l, *names;
+
+  g_hash_table_remove (daemon->clients, client->id);
+
+  names = g_hash_table_get_values (daemon->names);
+  for (l = names; l != NULL; l = l->next)
+    {
+      Name *name = l->data;
+
+      name_ref (name);
+
+      if (name->owner->client == client)
+	name_release_owner (name);
+
+      name_unqueue_owner (name, client);
+
+      name_unref (name);
+    }
+  g_list_free (names);
+
+  send_name_owner_changed (daemon, client->id, client->id, NULL);
+
+  g_object_unref (client->connection);
+
+  for (l = client->matches; l != NULL; l = l->next)
+    match_free (l->data);
+  g_list_free (client->matches);
+
+  g_free (client->id);
+  g_free (client);
+}
+
+static void
+connection_closed (GDBusConnection *connection,
+		   gboolean remote_peer_vanished,
+		   GError *error,
+		   Client *client)
+{
+  g_print ("connection %s closed %d %s\n", client->id, remote_peer_vanished, error->message);
+  client_free (client);
+}
+
+static gboolean
+handle_add_match (_GFreedesktopDBus *object,
+		  GDBusMethodInvocation *invocation,
+		  const gchar *arg_rule)
+{
+  Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client");
+  Match *match;
+
+  match = match_new (arg_rule);
+
+  if (match == NULL)
+    g_dbus_method_invocation_return_error (invocation,
+					   G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_INVALID,
+					   "Invalid rule: %s", arg_rule);
+  else
+    {
+      client->matches = g_list_prepend (client->matches, match);
+      _g_freedesktop_dbus_complete_add_match (object, invocation);
+    }
+  return TRUE;
+}
+
+static gboolean
+handle_get_connection_selinux_security_context (_GFreedesktopDBus *object,
+						GDBusMethodInvocation *invocation,
+						const gchar *arg_name)
+{
+  _g_freedesktop_dbus_complete_get_connection_selinux_security_context (object, invocation, "");
+  return TRUE;
+}
+
+static gboolean
+handle_get_connection_unix_process_id (_GFreedesktopDBus *object,
+				       GDBusMethodInvocation *invocation,
+				       const gchar *arg_name)
+{
+  /* TODO: Implement */
+  return FALSE;
+}
+
+static gboolean
+handle_get_connection_unix_user (_GFreedesktopDBus *object,
+				 GDBusMethodInvocation *invocation,
+				 const gchar *arg_name)
+{
+  /* TODO: Implement */
+  return FALSE;
+}
+
+static gboolean
+handle_get_id (_GFreedesktopDBus *object,
+	       GDBusMethodInvocation *invocation)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  _g_freedesktop_dbus_complete_get_id (object, invocation,
+				       daemon->guid);
+  return TRUE;
+}
+
+static gboolean
+handle_get_name_owner (_GFreedesktopDBus *object,
+		       GDBusMethodInvocation *invocation,
+		       const gchar *arg_name)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  Name *name;
+
+  if (strcmp (arg_name, DBUS_SERVICE_NAME) == 0)
+    {
+      _g_freedesktop_dbus_complete_get_name_owner (object, invocation, DBUS_SERVICE_NAME);
+      return TRUE;
+    }
+
+  name = name_lookup (daemon, arg_name);
+  if (name == NULL)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
+					     "Could not get owner of name '%s': no such name", arg_name);
+      return TRUE;
+    }
+
+  g_assert (name->owner != NULL);
+
+  _g_freedesktop_dbus_complete_get_name_owner (object, invocation, name->owner->client->id);
+  return TRUE;
+}
+
+static gboolean
+handle_hello (_GFreedesktopDBus *object,
+	      GDBusMethodInvocation *invocation)
+{
+  Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client");
+  _g_freedesktop_dbus_complete_hello (object, invocation, client->id);
+
+  g_dbus_connection_emit_signal (client->connection,
+				 NULL, "/org/freedesktop/DBus",
+				 "org.freedesktop.DBus", "NameAcquired",
+				 g_variant_new ("(s)",
+						client->id), NULL);
+
+  return TRUE;
+}
+
+static gboolean
+handle_list_activatable_names (_GFreedesktopDBus *object,
+			       GDBusMethodInvocation *invocation)
+{
+  const char *names[] = { NULL };
+
+  /* TODO: Implement */
+  _g_freedesktop_dbus_complete_list_activatable_names (object,
+						       invocation,
+						       names);
+  return TRUE;
+}
+
+static gboolean
+handle_list_names (_GFreedesktopDBus *object,
+		   GDBusMethodInvocation *invocation)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  GPtrArray *array;
+  GList *clients, *names, *l;
+
+  array = g_ptr_array_new ();
+
+  clients = g_hash_table_get_values (daemon->clients);
+  for (l = clients; l != NULL; l = l->next)
+    {
+      Client *client = l->data;
+
+      g_ptr_array_add (array, client->id);
+    }
+
+  g_list_free (clients);
+
+  names = g_hash_table_get_values (daemon->names);
+  for (l = names; l != NULL; l = l->next)
+    {
+      Name *name = l->data;
+
+      g_ptr_array_add (array, name->name);
+    }
+
+  g_list_free (names);
+
+  g_ptr_array_add (array, NULL);
+
+  _g_freedesktop_dbus_complete_list_names (object,
+					   invocation,
+					   (const gchar * const*)array->pdata);
+  g_ptr_array_free (array, TRUE);
+  return TRUE;
+}
+
+static gboolean
+handle_list_queued_owners (_GFreedesktopDBus *object,
+			   GDBusMethodInvocation *invocation,
+			   const gchar *arg_name)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  GPtrArray *array;
+  Name *name;
+  GList *l;
+
+  array = g_ptr_array_new ();
+
+  name = name_lookup (daemon, arg_name);
+  if (name)
+    {
+      g_assert (name->owner != NULL);
+
+      for (l = name->queue; l != NULL; l = l->next)
+	{
+	  Client *client = l->data;
+
+	  g_ptr_array_add (array, client->id);
+	}
+    }
+
+  g_ptr_array_add (array, NULL);
+
+  _g_freedesktop_dbus_complete_list_queued_owners (object,
+						   invocation,
+						   (const gchar * const*)array->pdata);
+  g_ptr_array_free (array, TRUE);
+  return TRUE;
+}
+
+static gboolean handle_name_has_owner (_GFreedesktopDBus *object,
+				       GDBusMethodInvocation *invocation,
+				       const gchar *arg_name)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  Name *name;
+
+  name = name_lookup (daemon, arg_name);
+
+  _g_freedesktop_dbus_complete_name_has_owner (object, invocation,
+					       name != NULL);
+  return TRUE;
+}
+
+static gboolean
+handle_release_name (_GFreedesktopDBus *object,
+		     GDBusMethodInvocation *invocation,
+		     const gchar *arg_name)
+{
+  Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client");
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  Name *name;
+  guint32 result;
+
+  if (!g_dbus_is_name (arg_name))
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+					     "Given bus name \"%s\" is not valid", arg_name);
+      return TRUE;
+    }
+
+  if (*arg_name == ':')
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+					     "Cannot release a service starting with ':' such as \"%s\"", arg_name);
+      return TRUE;
+    }
+
+  if (strcmp (arg_name, DBUS_SERVICE_NAME) == 0)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+					     "Cannot release a service named " DBUS_SERVICE_NAME ", because that is owned by the bus");
+      return TRUE;
+    }
+
+  name = name_lookup (daemon, arg_name);
+
+  if (name == NULL)
+    result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT;
+  else if (name->owner->client == client)
+    {
+      name_release_owner (name);
+      result = DBUS_RELEASE_NAME_REPLY_RELEASED;
+    }
+  else if (name_unqueue_owner (name, client))
+    result = DBUS_RELEASE_NAME_REPLY_RELEASED;
+  else
+    result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER;
+
+  _g_freedesktop_dbus_complete_release_name (object, invocation, result);
+  return TRUE;
+}
+
+static gboolean
+handle_reload_config (_GFreedesktopDBus *object,
+		      GDBusMethodInvocation *invocation)
+{
+  _g_freedesktop_dbus_complete_reload_config (object, invocation);
+  return TRUE;
+}
+
+static gboolean
+handle_remove_match (_GFreedesktopDBus *object,
+		     GDBusMethodInvocation *invocation,
+		     const gchar *arg_rule)
+{
+  Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client");
+  Match *match, *other_match;
+  GList *l;
+
+  match = match_new (arg_rule);
+
+  if (match == NULL)
+    g_dbus_method_invocation_return_error (invocation,
+					   G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_INVALID,
+					   "Invalid rule: %s", arg_rule);
+  else
+    {
+      for (l = client->matches; l != NULL; l = l->next)
+	{
+	  other_match = l->data;
+	  if (match_equal (match, other_match))
+	    {
+	      match_free (other_match);
+	      client->matches = g_list_delete_link (client->matches, l);
+	      break;
+	    }
+	}
+
+      if (l == NULL)
+	g_dbus_method_invocation_return_error (invocation,
+					       G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
+					       "The given match rule wasn't found and can't be removed");
+      else
+	_g_freedesktop_dbus_complete_remove_match (object, invocation);
+    }
+
+  match_free (match);
+
+  return TRUE;
+}
+
+static gboolean
+handle_request_name (_GFreedesktopDBus *object,
+		     GDBusMethodInvocation *invocation,
+		     const gchar *arg_name,
+		     guint flags)
+{
+  Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client");
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  Name *name;
+  NameOwner *owner;
+  guint32 result;
+
+  if (!g_dbus_is_name (arg_name))
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+					     "Requested bus name \"%s\" is not valid", arg_name);
+      return TRUE;
+    }
+
+  if (*arg_name == ':')
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+					     "Cannot acquire a service starting with ':' such as \"%s\"", arg_name);
+      return TRUE;
+    }
+
+  if (strcmp (arg_name, DBUS_SERVICE_NAME) == 0)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+					     G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+					     "Cannot acquire a service named " DBUS_SERVICE_NAME ", because that is reserved");
+      return TRUE;
+    }
+
+  name = name_ensure (daemon, arg_name);
+  if (name->owner == NULL)
+    {
+      owner = name_owner_new (client, flags);
+      name_replace_owner (name, owner);
+
+      result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+    }
+  else if (name->owner->client == client)
+    {
+      name->owner->flags = flags;
+      result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+    }
+  else if ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
+	   (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
+	    !(name->owner->flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT)))
+    {
+      /* Unqueue if queued */
+      name_unqueue_owner (name, client);
+      result = DBUS_REQUEST_NAME_REPLY_EXISTS;
+    }
+  else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
+	   (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
+	    !(name->owner->flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT)))
+    {
+      /* Queue the connection */
+      owner = name_owner_new (client, flags);
+      name_queue_owner (name, owner);
+      result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
+    }
+  else
+    {
+      /* Replace the current owner */
+
+      owner = name_owner_new (client, flags);
+      name_replace_owner (name, owner);
+
+      result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+    }
+
+  name_unref (name);
+
+  _g_freedesktop_dbus_complete_request_name (object, invocation, result);
+  return TRUE;
+}
+
+static gboolean
+handle_start_service_by_name (_GFreedesktopDBus *object,
+			      GDBusMethodInvocation *invocation,
+			      const gchar *arg_name,
+			      guint arg_flags)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+  Name *name;
+
+  name = name_lookup (daemon, arg_name);
+  if (name)
+    _g_freedesktop_dbus_complete_start_service_by_name (object, invocation,
+							DBUS_START_REPLY_ALREADY_RUNNING);
+  else
+    g_dbus_method_invocation_return_error (invocation,
+					   G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
+					   "No support for activation for name: %s", arg_name);
+
+  return TRUE;
+}
+
+static void
+return_error (Client *client, GDBusMessage *message,
+	      GQuark                 domain,
+	      gint                   code,
+	      const gchar           *format,
+	      ...)
+{
+  GDBusMessage *reply;
+  va_list var_args;
+  char *error_message;
+  GError *error;
+  gchar *dbus_error_name;
+
+  va_start (var_args, format);
+  error_message = g_strdup_vprintf (format, var_args);
+  va_end (var_args);
+
+  error = g_error_new_literal (domain, code, "");
+  dbus_error_name = g_dbus_error_encode_gerror (error);
+
+  reply = g_dbus_message_new_method_error_literal (message,
+						   dbus_error_name,
+						   error_message);
+
+  g_error_free (error);
+  g_free (dbus_error_name);
+  g_free (error_message);
+
+  if (!g_dbus_connection_send_message (client->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL))
+      g_warning ("Error sending reply");
+  g_object_unref (reply);
+}
+
+GDBusMessage *
+route_message (Client *source_client, GDBusMessage *message)
+{
+  const char *dest;
+  Client *dest_client;
+  GDBusDaemon *daemon;
+
+  daemon = source_client->daemon;
+
+  dest_client = NULL;
+  dest = g_dbus_message_get_destination (message);
+  if (dest != NULL && strcmp (dest, DBUS_SERVICE_NAME) != 0)
+    {
+      dest_client = g_hash_table_lookup (daemon->clients, dest);
+
+      if (dest_client == NULL)
+	{
+	  Name *name;
+	  name = name_lookup (daemon, dest);
+	  if (name)
+	    {
+	      g_assert (name->owner != NULL);
+	      dest_client = name->owner->client;
+	    }
+	}
+
+      if (dest_client == NULL)
+	{
+	  if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL)
+	    return_error (source_client, message,
+			  G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
+			  "The name %s is unknown", dest);
+	}
+      else
+	{
+	  GError *error = NULL;
+
+	  if (!g_dbus_connection_send_message (dest_client->connection, message, G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL, NULL, &error))
+	    {
+	      g_warning ("Error forwarding message: %s", error->message);
+	      g_error_free (error);
+	    }
+	}
+    }
+
+  broadcast_message (daemon, message, dest_client != NULL, TRUE, dest_client);
+
+  /* Swallow messages not for the bus */
+  if (dest == NULL || strcmp (dest, DBUS_SERVICE_NAME) != 0)
+    {
+      g_object_unref (message);
+      message = NULL;
+    }
+
+  return message;
+}
+
+GDBusMessage *
+copy_if_locked (GDBusMessage *message)
+{
+  if (g_dbus_message_get_locked (message))
+    {
+      GDBusMessage *copy = g_dbus_message_copy (message, NULL);
+      g_object_unref (message);
+      message = copy;
+    }
+  return message;
+}
+
+GDBusMessage *
+filter_function (GDBusConnection *connection,
+		 GDBusMessage    *message,
+		 gboolean         incoming,
+		 gpointer         user_data)
+{
+  Client *client = user_data;
+  char *types[] = {"invalid", "method_call", "method_return", "error", "signal" };
+
+  if (0)
+    g_printerr ("%s%s %s %d(%d) sender: %s destination: %s %s %s.%s\n",
+		client->id,
+		incoming? "->" : "<-",
+		types[g_dbus_message_get_message_type (message)],
+		g_dbus_message_get_serial (message),
+		g_dbus_message_get_reply_serial (message),
+		g_dbus_message_get_sender (message),
+		g_dbus_message_get_destination (message),
+		g_dbus_message_get_path (message),
+		g_dbus_message_get_interface (message),
+		g_dbus_message_get_member (message));
+
+  if (incoming)
+    {
+      /* Ensure its not locked so we can set the sender */
+      message = copy_if_locked (message);
+      if (message == NULL)
+	{
+	  g_warning ("Failed to copy incoming message");
+	  return NULL;
+	}
+      g_dbus_message_set_sender (message, client->id);
+
+      return route_message (client, message);
+    }
+  else
+    {
+      if (g_dbus_message_get_sender (message) == NULL)
+	{
+	  message = copy_if_locked (message);
+	  g_dbus_message_set_sender (message, DBUS_SERVICE_NAME);
+	}
+      if (g_dbus_message_get_destination (message) == NULL)
+	{
+	  message = copy_if_locked (message);
+	  g_dbus_message_set_destination (message, client->id);
+	}
+    }
+
+  return message;
+}
+
+static gboolean
+on_new_connection (GDBusServer *server,
+		   GDBusConnection *connection,
+		   gpointer user_data)
+{
+  GDBusDaemon *daemon = user_data;
+  GError        *error = NULL;
+  Client *client;
+
+  client = client_new (daemon, connection);
+
+  g_object_ref (connection);
+  g_dbus_connection_set_exit_on_close (connection, FALSE);
+  g_signal_connect (connection, "closed", G_CALLBACK (connection_closed), client);
+
+  g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon), connection,
+				    "/org/freedesktop/DBus", &error);
+  g_assert_no_error (error);
+
+  g_dbus_connection_add_filter (connection,
+				filter_function,
+				client, NULL);
+
+  return TRUE;
+}
+
+static void
+g_dbus_daemon_finalize (GObject *object)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+
+  g_object_unref (daemon->server);
+
+  if (daemon->tmpdir)
+    {
+      rmdir (daemon->tmpdir);
+      g_free (daemon->tmpdir);
+    }
+
+  g_free (daemon->guid);
+  g_free (daemon->address);
+
+  /* TODO: Destroy all clients */
+
+  g_hash_table_destroy (daemon->clients);
+  g_hash_table_destroy (daemon->names);
+
+  G_OBJECT_CLASS (g_dbus_daemon_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_daemon_init (GDBusDaemon *daemon)
+{
+  daemon->next_major_id = 1;
+  daemon->clients = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+  daemon->names = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+  daemon->guid = g_dbus_generate_guid ();
+}
+
+static gboolean
+initable_init (GInitable     *initable,
+	       GCancellable  *cancellable,
+	       GError       **error)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (initable);
+
+  if (daemon->address == NULL)
+    {
+#ifdef G_OS_UNIX
+      if (g_unix_socket_address_abstract_names_supported ())
+	daemon->address = g_strdup ("unix:tmpdir=/tmp/gdbus-daemon");
+      else
+	{
+	  daemon->tmpdir = g_dir_make_tmp ("gdbus-daemon-XXXXXX", NULL);
+	  daemon->address = g_strdup_printf ("unix:tmpdir=%s", daemon->tmpdir);
+	}
+#else
+      daemon->address = g_strdup ("nonce-tcp:");
+#endif
+    }
+
+  daemon->server = g_dbus_server_new_sync (daemon->address,
+					 G_DBUS_SERVER_FLAGS_NONE,
+					 daemon->guid,
+					 NULL, /* observer */
+					 cancellable,
+					 error);
+  if (daemon->server == NULL)
+    return FALSE;
+
+  g_dbus_server_start (daemon->server);
+
+  g_signal_connect (daemon->server, "new-connection",
+		    G_CALLBACK (on_new_connection),
+		    daemon);
+  return TRUE;
+}
+
+static void
+g_dbus_daemon_set_property (GObject      *object,
+			    guint         prop_id,
+			    const GValue *value,
+			    GParamSpec   *pspec)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+
+  switch (prop_id)
+    {
+    case PROP_ADDRESS:
+      g_free (daemon->address);
+      daemon->address = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_dbus_daemon_get_property (GObject    *object,
+			    guint       prop_id,
+			    GValue     *value,
+			    GParamSpec *pspec)
+{
+  GDBusDaemon *daemon = G_DBUS_DAEMON (object);
+
+  switch (prop_id)
+    {
+      case PROP_ADDRESS:
+	g_value_set_string (value, daemon->address);
+	break;
+
+    default:
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_dbus_daemon_class_init (GDBusDaemonClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = g_dbus_daemon_finalize;
+  gobject_class->set_property = g_dbus_daemon_set_property;
+  gobject_class->get_property = g_dbus_daemon_get_property;
+
+  g_object_class_install_property (gobject_class,
+				   PROP_ADDRESS,
+				   g_param_spec_string ("address",
+							"Bus Address",
+							"The address the bus should use",
+							NULL,
+							G_PARAM_READWRITE |
+							G_PARAM_CONSTRUCT_ONLY |
+							G_PARAM_STATIC_STRINGS));
+}
+
+static void
+g_dbus_daemon_iface_init (_GFreedesktopDBusIface *iface)
+{
+  iface->handle_add_match = handle_add_match;
+  iface->handle_get_connection_selinux_security_context = handle_get_connection_selinux_security_context;
+  iface->handle_get_connection_unix_process_id = handle_get_connection_unix_process_id;
+  iface->handle_get_connection_unix_user = handle_get_connection_unix_user;
+  iface->handle_get_id = handle_get_id;
+  iface->handle_get_name_owner = handle_get_name_owner;
+  iface->handle_hello = handle_hello;
+  iface->handle_list_activatable_names = handle_list_activatable_names;
+  iface->handle_list_names = handle_list_names;
+  iface->handle_list_queued_owners = handle_list_queued_owners;
+  iface->handle_name_has_owner = handle_name_has_owner;
+  iface->handle_release_name = handle_release_name;
+  iface->handle_reload_config = handle_reload_config;
+  iface->handle_remove_match = handle_remove_match;
+  iface->handle_request_name = handle_request_name;
+  iface->handle_start_service_by_name = handle_start_service_by_name;
+}
+
+static void
+initable_iface_init (GInitableIface *initable_iface)
+{
+  initable_iface->init = initable_init;
+}
+
+GDBusDaemon *
+_g_dbus_daemon_new (const char *address,
+		    GCancellable *cancellable,
+		    GError **error)
+{
+  return g_initable_new (G_TYPE_DBUS_DAEMON,
+			 cancellable,
+			 error,
+			 "address", address,
+			 NULL);
+}
+
+const char *
+_g_dbus_daemon_get_address (GDBusDaemon *daemon)
+{
+  return g_dbus_server_get_client_address (daemon->server);
+}
diff --git a/gio/gdbusdaemon.h b/gio/gdbusdaemon.h
new file mode 100644
index 0000000..183b70e
--- /dev/null
+++ b/gio/gdbusdaemon.h
@@ -0,0 +1,19 @@
+#include <gio/gio.h>
+
+#define G_TYPE_DBUS_DAEMON (_g_dbus_daemon_get_type ())
+#define G_DBUS_DAEMON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_DAEMON, GDBusDaemon))
+#define G_DBUS_DAEMON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_DBUS_DAEMON, GDBusDaemonClass))
+#define G_DBUS_DAEMON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_DAEMON, GDBusDaemonClass))
+#define G_IS_DBUS_DAEMON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_DAEMON))
+#define G_IS_DBUS_DAEMON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_DAEMON))
+
+typedef struct _GDBusDaemon GDBusDaemon;
+typedef struct _GDBusDaemonClass GDBusDaemonClass;
+
+GType _g_dbus_daemon_get_type (void) G_GNUC_CONST;
+
+GDBusDaemon *_g_dbus_daemon_new (const char *address,
+				 GCancellable *cancellable,
+				 GError **error);
+
+const char *_g_dbus_daemon_get_address (GDBusDaemon *daemon);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]