From 94b907134426e26393a86630dae5ce53baee6ae6 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 8 Apr 2011 15:14:47 -0400 Subject: [PATCH] Start merging gdbus-codegen code Signed-off-by: David Zeuthen --- gio/Makefile.am | 16 + gio/gdbusauthobserver.c | 17 +- gio/gdbusinterface.c | 410 +++++++++ gio/gdbusinterface.h | 79 ++ gio/gdbusinterfacestub.c | 693 ++++++++++++++ gio/gdbusinterfacestub.h | 105 +++ gio/gdbusobject.c | 191 ++++ gio/gdbusobject.h | 95 ++ gio/gdbusobjectmanager.c | 216 +++++ gio/gdbusobjectmanager.h | 89 ++ gio/gdbusobjectmanagerclient.c | 1575 ++++++++++++++++++++++++++++++++ gio/gdbusobjectmanagerclient.h | 129 +++ gio/gdbusobjectmanagerserver.c | 898 ++++++++++++++++++ gio/gdbusobjectmanagerserver.h | 80 ++ gio/gdbusobjectproxy.c | 315 +++++++ gio/gdbusobjectproxy.h | 72 ++ gio/gdbusobjectstub.c | 475 ++++++++++ gio/gdbusobjectstub.h | 87 ++ gio/gdbusprivate.c | 18 + gio/gdbusprivate.h | 20 +- gio/gdbusproxy.c | 36 + gio/gio-marshal.list | 3 + gio/gio.h | 8 + gio/gio.symbols | 96 ++ gio/gioenums.h | 36 + gio/giotypes.h | 33 + 26 files changed, 5774 insertions(+), 18 deletions(-) create mode 100644 gio/gdbusinterface.c create mode 100644 gio/gdbusinterface.h create mode 100644 gio/gdbusinterfacestub.c create mode 100644 gio/gdbusinterfacestub.h create mode 100644 gio/gdbusobject.c create mode 100644 gio/gdbusobject.h create mode 100644 gio/gdbusobjectmanager.c create mode 100644 gio/gdbusobjectmanager.h create mode 100644 gio/gdbusobjectmanagerclient.c create mode 100644 gio/gdbusobjectmanagerclient.h create mode 100644 gio/gdbusobjectmanagerserver.c create mode 100644 gio/gdbusobjectmanagerserver.h create mode 100644 gio/gdbusobjectproxy.c create mode 100644 gio/gdbusobjectproxy.h create mode 100644 gio/gdbusobjectstub.c create mode 100644 gio/gdbusobjectstub.h diff --git a/gio/Makefile.am b/gio/Makefile.am index e2fcd7eb7..318c22c4a 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -82,6 +82,14 @@ gdbus_headers = \ gdbusintrospection.h \ gdbusmethodinvocation.h \ gdbusserver.h \ + gdbusinterface.h \ + gdbusinterfacestub.h \ + gdbusobject.h \ + gdbusobjectstub.h \ + gdbusobjectproxy.h \ + gdbusobjectmanager.h \ + gdbusobjectmanagerclient.h \ + gdbusobjectmanagerserver.h \ $(NULL) gdbus_sources = \ @@ -103,6 +111,14 @@ gdbus_sources = \ gdbusintrospection.h gdbusintrospection.c \ gdbusmethodinvocation.h gdbusmethodinvocation.c \ gdbusserver.h gdbusserver.c \ + gdbusinterface.h gdbusinterface.c \ + gdbusinterfacestub.h gdbusinterfacestub.c \ + gdbusobject.h gdbusobject.c \ + gdbusobjectstub.h gdbusobjectstub.c \ + gdbusobjectproxy.h gdbusobjectproxy.c \ + gdbusobjectmanager.h gdbusobjectmanager.c \ + gdbusobjectmanagerclient.h gdbusobjectmanagerclient.c \ + gdbusobjectmanagerserver.h gdbusobjectmanagerserver.c \ $(NULL) settings_headers = \ diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index 46af595a5..ff9482113 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -27,6 +27,7 @@ #include "gcredentials.h" #include "gioenumtypes.h" #include "giostream.h" +#include "gdbusprivate.h" #include "glibintl.h" @@ -130,22 +131,6 @@ g_dbus_auth_observer_authorize_authenticated_peer_real (GDBusAuthObserver *obse return TRUE; } -gboolean -_g_signal_accumulator_false_handled (GSignalInvocationHint *ihint, - GValue *return_accu, - const GValue *handler_return, - gpointer dummy) -{ - gboolean continue_emission; - gboolean signal_handled; - - signal_handled = g_value_get_boolean (handler_return); - g_value_set_boolean (return_accu, signal_handled); - continue_emission = signal_handled; - - return continue_emission; -} - static void g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass) { diff --git a/gio/gdbusinterface.c b/gio/gdbusinterface.c new file mode 100644 index 000000000..d8f85943b --- /dev/null +++ b/gio/gdbusinterface.c @@ -0,0 +1,410 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobject.h" +#include "gdbusinterface.h" +#include "gio-marshal.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusinterface + * @short_description: Base type for D-Bus interfaces + * @include: gio/gio.h + * + * The #GDBusInterface type is the base type for D-Bus interfaces both + * on the service side (see #GDBusInterfaceStub) and client side (see + * #GDBusProxy). + */ + +typedef GDBusInterfaceIface GDBusInterfaceInterface; +G_DEFINE_INTERFACE (GDBusInterface, g_dbus_interface, G_TYPE_OBJECT) + +static void +g_dbus_interface_default_init (GDBusInterfaceIface *iface) +{ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_interface_get_info: + * @interface: An exported D-Bus interface. + * + * Gets D-Bus introspection information for the D-Bus interface + * implemented by @interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +g_dbus_interface_get_info (GDBusInterface *interface) +{ + g_return_val_if_fail (G_IS_DBUS_INTERFACE (interface), NULL); + return G_DBUS_INTERFACE_GET_IFACE (interface)->get_info (interface); +} + +/** + * g_dbus_interface_get_object: + * @interface: An exported D-Bus interface. + * + * Gets the #GDBusObject that @interface belongs to, if any. + * + * Returns: (transfer none): A #GDBusObject or %NULL. The returned + * reference belongs to @interface and should not be freed. + */ +GDBusObject * +g_dbus_interface_get_object (GDBusInterface *interface) +{ + g_return_val_if_fail (G_IS_DBUS_INTERFACE (interface), NULL); + return G_DBUS_INTERFACE_GET_IFACE (interface)->get_object (interface); +} + +/** + * g_dbus_interface_set_object: + * @interface: An exported D-Bus interface. + * @object: A #GDBusObject or %NULL. + * + * Sets the #GDBusObject for @interface to @object. + * + * Note that @interface will hold a weak reference to @object. + */ +void +g_dbus_interface_set_object (GDBusInterface *interface, + GDBusObject *object) +{ + g_return_if_fail (G_IS_DBUS_INTERFACE (interface)); + g_return_if_fail (object == NULL || G_IS_DBUS_OBJECT (object)); + G_DBUS_INTERFACE_GET_IFACE (interface)->set_object (interface, object); +} + +/* Keep it here for now. TODO: move */ + +#include + +/** + * g_dbus_gvariant_to_gvalue: + * @value: A #GVariant. + * @out_gvalue: Return location for the #GValue. + * + * Convert a #GVariant to a #GValue. If @value is floating, it is consumed. + * + * Note that the passed @out_gvalue does not have to have a #GType set. + * + * Returns: %TRUE if the conversion succeeded, %FALSE otherwise. + */ +gboolean +g_dbus_gvariant_to_gvalue (GVariant *value, + GValue *out_gvalue) +{ + gboolean ret; + const GVariantType *type; + gchar **array; + + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (out_gvalue != NULL, FALSE); + + ret = FALSE; + + memset (out_gvalue, '\0', sizeof (GValue)); + + switch (g_variant_classify (value)) + { + case G_VARIANT_CLASS_BOOLEAN: + g_value_init (out_gvalue, G_TYPE_BOOLEAN); + g_value_set_boolean (out_gvalue, g_variant_get_boolean (value)); + break; + + case G_VARIANT_CLASS_BYTE: + g_value_init (out_gvalue, G_TYPE_UCHAR); + g_value_set_uchar (out_gvalue, g_variant_get_byte (value)); + break; + + case G_VARIANT_CLASS_INT16: + g_value_init (out_gvalue, G_TYPE_INT); + g_value_set_int (out_gvalue, g_variant_get_int16 (value)); + break; + + case G_VARIANT_CLASS_UINT16: + g_value_init (out_gvalue, G_TYPE_UINT); + g_value_set_uint (out_gvalue, g_variant_get_uint16 (value)); + break; + + case G_VARIANT_CLASS_INT32: + g_value_init (out_gvalue, G_TYPE_INT); + g_value_set_int (out_gvalue, g_variant_get_int32 (value)); + break; + + case G_VARIANT_CLASS_UINT32: + g_value_init (out_gvalue, G_TYPE_UINT); + g_value_set_uint (out_gvalue, g_variant_get_uint32 (value)); + break; + + case G_VARIANT_CLASS_INT64: + g_value_init (out_gvalue, G_TYPE_INT64); + g_value_set_int64 (out_gvalue, g_variant_get_int64 (value)); + break; + + case G_VARIANT_CLASS_UINT64: + g_value_init (out_gvalue, G_TYPE_UINT64); + g_value_set_uint64 (out_gvalue, g_variant_get_uint64 (value)); + break; + + case G_VARIANT_CLASS_HANDLE: + g_value_init (out_gvalue, G_TYPE_INT); + g_value_set_int (out_gvalue, g_variant_get_int32 (value)); + break; + + case G_VARIANT_CLASS_DOUBLE: + g_value_init (out_gvalue, G_TYPE_DOUBLE); + g_value_set_double (out_gvalue, g_variant_get_double (value)); + break; + + case G_VARIANT_CLASS_STRING: + g_value_init (out_gvalue, G_TYPE_STRING); + g_value_set_string (out_gvalue, g_variant_get_string (value, NULL)); + break; + + case G_VARIANT_CLASS_OBJECT_PATH: + g_value_init (out_gvalue, G_TYPE_STRING); + g_value_set_string (out_gvalue, g_variant_get_string (value, NULL)); + break; + + case G_VARIANT_CLASS_SIGNATURE: + g_value_init (out_gvalue, G_TYPE_STRING); + g_value_set_string (out_gvalue, g_variant_get_string (value, NULL)); + break; + + case G_VARIANT_CLASS_ARRAY: + type = g_variant_get_type (value); + switch (g_variant_type_peek_string (type)[1]) + { + case G_VARIANT_CLASS_BYTE: + g_value_init (out_gvalue, G_TYPE_STRING); + g_value_set_string (out_gvalue, g_variant_get_bytestring (value)); + break; + + case G_VARIANT_CLASS_STRING: + g_value_init (out_gvalue, G_TYPE_STRV); + array = g_variant_dup_strv (value, NULL); + g_value_take_boxed (out_gvalue, array); + break; + + case G_VARIANT_CLASS_ARRAY: + switch (g_variant_type_peek_string (type)[2]) + { + case G_VARIANT_CLASS_BYTE: + g_value_init (out_gvalue, G_TYPE_STRV); + array = g_variant_dup_bytestring_array (value, NULL); + g_value_take_boxed (out_gvalue, array); + break; + + default: + g_value_init (out_gvalue, G_TYPE_VARIANT); + g_value_set_variant (out_gvalue, value); + break; + } + break; + + default: + g_value_init (out_gvalue, G_TYPE_VARIANT); + g_value_set_variant (out_gvalue, value); + break; + } + break; + + case G_VARIANT_CLASS_VARIANT: + case G_VARIANT_CLASS_MAYBE: + case G_VARIANT_CLASS_TUPLE: + case G_VARIANT_CLASS_DICT_ENTRY: + g_value_init (out_gvalue, G_TYPE_VARIANT); + g_value_set_variant (out_gvalue, value); + break; + } + + ret = TRUE; + + return ret; +} + + +/** + * g_dbus_gvalue_to_gvariant: + * @gvalue: A #GValue to convert to a #GVariant. + * @expected_type: The #GVariantType to create. + * + * Convert a #GValue to #GVariant. + * + * Returns: A #GVariant (never floating) holding the data from @gvalue + * or %NULL in case of error. Free with g_variant_unref(). + */ +GVariant * +g_dbus_gvalue_to_gvariant (const GValue *gvalue, + const GVariantType *expected_type) +{ + GVariant *ret; + const gchar *s; + const gchar * const *as; + const gchar *empty_strv[1] = {NULL}; + + g_return_val_if_fail (gvalue != NULL, NULL); + g_return_val_if_fail (expected_type != NULL, NULL); + + ret = NULL; + + /* The expected type could easily be e.g. "s" with the GValue holding a string. + * because of the UseGVariant annotation + */ + if (G_VALUE_TYPE (gvalue) == G_TYPE_VARIANT) + { + ret = g_value_dup_variant (gvalue); + } + else + { + switch (g_variant_type_peek_string (expected_type)[0]) + { + case G_VARIANT_CLASS_BOOLEAN: + ret = g_variant_ref_sink (g_variant_new_boolean (g_value_get_boolean (gvalue))); + break; + + case G_VARIANT_CLASS_BYTE: + ret = g_variant_ref_sink (g_variant_new_byte (g_value_get_uchar (gvalue))); + break; + + case G_VARIANT_CLASS_INT16: + ret = g_variant_ref_sink (g_variant_new_int16 (g_value_get_int (gvalue))); + break; + + case G_VARIANT_CLASS_UINT16: + ret = g_variant_ref_sink (g_variant_new_uint16 (g_value_get_uint (gvalue))); + break; + + case G_VARIANT_CLASS_INT32: + ret = g_variant_ref_sink (g_variant_new_int32 (g_value_get_int (gvalue))); + break; + + case G_VARIANT_CLASS_UINT32: + ret = g_variant_ref_sink (g_variant_new_uint32 (g_value_get_uint (gvalue))); + break; + + case G_VARIANT_CLASS_INT64: + ret = g_variant_ref_sink (g_variant_new_int64 (g_value_get_int64 (gvalue))); + break; + + case G_VARIANT_CLASS_UINT64: + ret = g_variant_ref_sink (g_variant_new_uint64 (g_value_get_uint64 (gvalue))); + break; + + case G_VARIANT_CLASS_HANDLE: + ret = g_variant_ref_sink (g_variant_new_handle (g_value_get_int (gvalue))); + break; + + case G_VARIANT_CLASS_DOUBLE: + ret = g_variant_ref_sink (g_variant_new_double (g_value_get_double (gvalue))); + break; + + case G_VARIANT_CLASS_STRING: + s = g_value_get_string (gvalue); + if (s == NULL) + s = ""; + ret = g_variant_ref_sink (g_variant_new_string (s)); + break; + + case G_VARIANT_CLASS_OBJECT_PATH: + s = g_value_get_string (gvalue); + if (s == NULL) + s = "/"; + ret = g_variant_ref_sink (g_variant_new_object_path (s)); + break; + + case G_VARIANT_CLASS_SIGNATURE: + s = g_value_get_string (gvalue); + if (s == NULL) + s = ""; + ret = g_variant_ref_sink (g_variant_new_signature (s)); + break; + + case G_VARIANT_CLASS_ARRAY: + switch (g_variant_type_peek_string (expected_type)[1]) + { + case G_VARIANT_CLASS_BYTE: + s = g_value_get_string (gvalue); + if (s == NULL) + s = ""; + ret = g_variant_ref_sink (g_variant_new_bytestring (s)); + break; + + case G_VARIANT_CLASS_STRING: + as = g_value_get_boxed (gvalue); + if (as == NULL) + as = empty_strv; + ret = g_variant_ref_sink (g_variant_new_strv (as, -1)); + break; + + case G_VARIANT_CLASS_ARRAY: + switch (g_variant_type_peek_string (expected_type)[2]) + { + case G_VARIANT_CLASS_BYTE: + as = g_value_get_boxed (gvalue); + if (as == NULL) + as = empty_strv; + ret = g_variant_ref_sink (g_variant_new_bytestring_array (as, -1)); + break; + + default: + ret = g_value_dup_variant (gvalue); + break; + } + break; + + default: + ret = g_value_dup_variant (gvalue); + break; + } + break; + + default: + case G_VARIANT_CLASS_VARIANT: + case G_VARIANT_CLASS_MAYBE: + case G_VARIANT_CLASS_TUPLE: + case G_VARIANT_CLASS_DICT_ENTRY: + ret = g_value_dup_variant (gvalue); + break; + } + } + + /* Could be that the GValue is holding a NULL GVariant - in that case, + * we return an "empty" GVariant instead of a NULL GVariant + */ + if (ret == NULL) + { + GVariant *untrusted_empty; + untrusted_empty = g_variant_new_from_data (expected_type, NULL, 0, FALSE, NULL, NULL); + ret = g_variant_ref_sink (g_variant_get_normal_form (untrusted_empty)); + g_variant_unref (untrusted_empty); + } + + g_assert (!g_variant_is_floating (ret)); + + return ret; +} diff --git a/gio/gdbusinterface.h b/gio/gdbusinterface.h new file mode 100644 index 000000000..275f43eca --- /dev/null +++ b/gio/gdbusinterface.h @@ -0,0 +1,79 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_INTERFACE_H__ +#define __G_DBUS_INTERFACE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_INTERFACE (g_dbus_interface_get_type()) +#define G_DBUS_INTERFACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_INTERFACE, GDBusInterface)) +#define G_IS_DBUS_INTERFACE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_INTERFACE)) +#define G_DBUS_INTERFACE_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), G_TYPE_DBUS_INTERFACE, GDBusInterfaceIface)) + +/** + * GDBusInterface: + * + * Base type for D-Bus interfaces. + */ +typedef struct _GDBusInterface GDBusInterface; /* Dummy typedef */ + +typedef struct _GDBusInterfaceIface GDBusInterfaceIface; + +/** + * GDBusInterfaceIface: + * @parent_iface: The parent interface. + * @get_info: Returns a #GDBusInterfaceInfo. See g_dbus_interface_get_info(). + * @get_object: Gets the enclosing #GDBusObject. See g_dbus_interface_get_object(). + * @set_object: Sets the enclosing #GDBusObject. See g_dbus_interface_set_object(). + * + * Base type for D-Bus interfaces. + */ +struct _GDBusInterfaceIface +{ + GTypeInterface parent_iface; + + /* Virtual Functions */ + GDBusInterfaceInfo *(*get_info) (GDBusInterface *interface); + GDBusObject *(*get_object) (GDBusInterface *interface); + void (*set_object) (GDBusInterface *interface, + GDBusObject *object); +}; + +GType g_dbus_interface_get_type (void) G_GNUC_CONST; +GDBusInterfaceInfo *g_dbus_interface_get_info (GDBusInterface *interface); +GDBusObject *g_dbus_interface_get_object (GDBusInterface *interface); +void g_dbus_interface_set_object (GDBusInterface *interface, + GDBusObject *object); + +/* Keep it here for now. TODO: move */ + +gboolean g_dbus_gvariant_to_gvalue (GVariant *value, + GValue *out_gvalue); +GVariant *g_dbus_gvalue_to_gvariant (const GValue *gvalue, + const GVariantType *expected_type); + +G_END_DECLS + +#endif /* __G_DBUS_INTERFACE_H__ */ diff --git a/gio/gdbusinterfacestub.c b/gio/gdbusinterfacestub.c new file mode 100644 index 000000000..4eee99b2e --- /dev/null +++ b/gio/gdbusinterfacestub.c @@ -0,0 +1,693 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusinterface.h" +#include "gdbusinterfacestub.h" +#include "gdbusobjectstub.h" +#include "gio-marshal.h" +#include "gioenumtypes.h" +#include "gdbusprivate.h" +#include "gdbusmethodinvocation.h" +#include "gdbusconnection.h" +#include "gioscheduler.h" +#include "gioerror.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusinterfacestub + * @short_description: Service-side D-Bus interface + * @include: gio/gio.h + * + * Abstract base class for D-Bus interfaces on the service side. + */ + +struct _GDBusInterfaceStubPrivate +{ + GDBusObject *object; + GDBusInterfaceStubFlags flags; + guint registration_id; + + GDBusConnection *connection; + gchar *object_path; + GDBusInterfaceVTable *hooked_vtable; +}; + +enum +{ + G_AUTHORIZE_METHOD_SIGNAL, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_G_FLAGS +}; + +static guint signals[LAST_SIGNAL] = {0}; + +static void dbus_interface_interface_init (GDBusInterfaceIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GDBusInterfaceStub, g_dbus_interface_stub, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_interface_init)); + +static void +g_dbus_interface_stub_finalize (GObject *object) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object); + /* unexport if already exported */ + if (stub->priv->registration_id > 0) + g_dbus_interface_stub_unexport (stub); + + g_assert (stub->priv->connection == NULL); + g_assert (stub->priv->object_path == NULL); + g_assert (stub->priv->hooked_vtable == NULL); + + if (stub->priv->object != NULL) + g_object_remove_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object); + G_OBJECT_CLASS (g_dbus_interface_stub_parent_class)->finalize (object); +} + +static void +g_dbus_interface_stub_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object); + + switch (prop_id) + { + case PROP_G_FLAGS: + g_value_set_flags (value, g_dbus_interface_stub_get_flags (stub)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_interface_stub_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object); + + switch (prop_id) + { + case PROP_G_FLAGS: + g_dbus_interface_stub_set_flags (stub, g_value_get_flags (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +g_dbus_interface_stub_g_authorize_method_default (GDBusInterfaceStub *stub, + GDBusMethodInvocation *invocation) +{ + return TRUE; +} + +static void +g_dbus_interface_stub_class_init (GDBusInterfaceStubClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = g_dbus_interface_stub_finalize; + gobject_class->set_property = g_dbus_interface_stub_set_property; + gobject_class->get_property = g_dbus_interface_stub_get_property; + + klass->g_authorize_method = g_dbus_interface_stub_g_authorize_method_default; + + /** + * GDBusInterfaceStub:g-flags: + * + * Flags from the #GDBusInterfaceStubFlags enumeration. + */ + g_object_class_install_property (gobject_class, + PROP_G_FLAGS, + g_param_spec_flags ("g-flags", + "g-flags", + "Flags for the interface stub", + G_TYPE_DBUS_INTERFACE_STUB_FLAGS, + G_DBUS_INTERFACE_STUB_FLAGS_NONE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusInterfaceStub::g-authorize-method: + * @interface: The #GDBusInterfaceStub emitting the signal. + * @invocation: A #GDBusMethodInvocation. + * + * Emitted when a method is invoked by a remote caller and used to + * determine if the method call is authorized. + * + * Note that this signal is emitted in a thread dedicated to + * handling the method call so handlers are allowed to perform + * blocking IO. This means that it is appropriate to call + * e.g. polkit_authority_check_authorization_sync() + * with the POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION flag set. + * + * If %FALSE is returned then no further handlers are run and the + * signal handler must take ownership of @invocation and finish + * handling the call (e.g. return an error via + * g_dbus_method_invocation_return_error()). + * + * Otherwise, if %TRUE is returned, signal emission continues. If no + * handlers return %FALSE, then the method is dispatched. If + * @interface has an enclosing #GDBusObjectStub, then the + * #GDBusObjectStub::authorize-method signal handlers run before the + * handlers for this signal. + * + * The default class handler just returns %TRUE. + * + * Please note that the common case is optimized: if no signals + * handlers are connected and the default class handler isn't + * overridden (for both @interface and the enclosing + * #GDBusObjectStub, if any) and #GDBusInterfaceStub:g-flags does + * not have the + * %G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD + * flags set, no dedicated thread is ever used and the call will be + * handled in the same thread as the object that @interface belongs + * to was exported in. + * + * Returns: %TRUE if the call is authorized, %FALSE otherwise. + */ + signals[G_AUTHORIZE_METHOD_SIGNAL] = + g_signal_new ("g-authorize-method", + G_TYPE_DBUS_INTERFACE_STUB, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusInterfaceStubClass, g_authorize_method), + _g_signal_accumulator_false_handled, + NULL, + _gio_marshal_BOOLEAN__OBJECT, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + g_type_class_add_private (klass, sizeof (GDBusInterfaceStubPrivate)); +} + +static void +g_dbus_interface_stub_init (GDBusInterfaceStub *stub) +{ + stub->priv = G_TYPE_INSTANCE_GET_PRIVATE (stub, G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStubPrivate); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_interface_stub_get_flags: + * @stub: A #GDBusInterfaceStub. + * + * Gets the #GDBusInterfaceStubFlags that describes what the behavior + * of @stub + * + * Returns: One or more flags from the #GDBusInterfaceStubFlags enumeration. + */ +GDBusInterfaceStubFlags +g_dbus_interface_stub_get_flags (GDBusInterfaceStub *stub) +{ + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), G_DBUS_INTERFACE_STUB_FLAGS_NONE); + return stub->priv->flags; +} + +/** + * g_dbus_interface_stub_set_flags: + * @stub: A #GDBusInterfaceStub. + * @flags: Flags from the #GDBusInterfaceStubFlags enumeration. + * + * Sets flags describing what the behavior of @stub should be. + */ +void +g_dbus_interface_stub_set_flags (GDBusInterfaceStub *stub, + GDBusInterfaceStubFlags flags) +{ + g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub)); + if (stub->priv->flags != flags) + { + stub->priv->flags = flags; + g_object_notify (G_OBJECT (stub), "g-flags"); + } +} + +/** + * g_dbus_interface_stub_get_info: + * @stub: A #GDBusInterfaceStub. + * + * Gets D-Bus introspection information for the D-Bus interface + * implemented by @interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo (never %NULL). Do not free. + */ +GDBusInterfaceInfo * +g_dbus_interface_stub_get_info (GDBusInterfaceStub *stub) +{ + GDBusInterfaceInfo *ret; + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL); + ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_info (stub); + g_warn_if_fail (ret != NULL); + return ret; +} + +/** + * g_dbus_interface_stub_get_vtable: + * @stub: A #GDBusInterfaceStub. + * + * Gets the interface vtable for the D-Bus interface implemented by + * @interface. The returned function pointers should expect @stub + * itself to be passed as @user_data. + * + * Returns: A #GDBusInterfaceVTable (never %NULL). + */ +GDBusInterfaceVTable * +g_dbus_interface_stub_get_vtable (GDBusInterfaceStub *stub) +{ + GDBusInterfaceVTable *ret; + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL); + ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_vtable (stub); + g_warn_if_fail (ret != NULL); + return ret; +} + +/** + * g_dbus_interface_stub_get_properties: + * @stub: A #GDBusInterfaceStub. + * + * Gets all D-Bus properties for @stub. + * + * Returns: A new, floating, #GVariant. Free with g_variant_unref(). + */ +GVariant * +g_dbus_interface_stub_get_properties (GDBusInterfaceStub *stub) +{ + GVariant *ret; + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL); + ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_properties (stub); + g_warn_if_fail (g_variant_is_floating (ret)); + return ret; +} + +/** + * g_dbus_interface_stub_flush: + * @stub: A #GDBusInterfaceStub. + * + * If @stub has outstanding changes, request for these changes to be + * emitted immediately. + * + * For example, an exported D-Bus interface may queue up property + * changes and emit the + * org.freedesktop.DBus.Properties::PropertiesChanged + * signal later (e.g. in an idle handler). This technique is useful + * for collapsing multiple property changes into one. + */ +void +g_dbus_interface_stub_flush (GDBusInterfaceStub *stub) +{ + g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub)); + G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->flush (stub); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusInterfaceInfo * +_g_dbus_interface_stub_get_info (GDBusInterface *interface) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (interface); + return g_dbus_interface_stub_get_info (stub); +} + +static GDBusObject * +g_dbus_interface_stub_get_object (GDBusInterface *interface) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (interface); + return stub->priv->object; +} + +static void +g_dbus_interface_stub_set_object (GDBusInterface *interface, + GDBusObject *object) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (interface); + if (stub->priv->object != NULL) + g_object_remove_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object); + stub->priv->object = object; + if (object != NULL) + g_object_add_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object); +} + +static void +dbus_interface_interface_init (GDBusInterfaceIface *iface) +{ + iface->get_info = _g_dbus_interface_stub_get_info; + iface->get_object = g_dbus_interface_stub_get_object; + iface->set_object = g_dbus_interface_stub_set_object; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + volatile gint ref_count; + GDBusInterfaceStub *stub; + GDBusInterfaceMethodCallFunc method_call_func; + GDBusMethodInvocation *invocation; + GMainContext *context; +} DispatchData; + +static void +dispatch_data_unref (DispatchData *data) +{ + if (g_atomic_int_dec_and_test (&data->ref_count)) + { + if (data->context != NULL) + g_main_context_unref (data->context); + g_free (data); + } +} + +static DispatchData * +dispatch_data_ref (DispatchData *data) +{ + g_atomic_int_inc (&data->ref_count); + return data; +} + +static gboolean +dispatch_invoke_in_context_func (gpointer user_data) +{ + DispatchData *data = user_data; + data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation), + g_dbus_method_invocation_get_sender (data->invocation), + g_dbus_method_invocation_get_object_path (data->invocation), + g_dbus_method_invocation_get_interface_name (data->invocation), + g_dbus_method_invocation_get_method_name (data->invocation), + g_dbus_method_invocation_get_parameters (data->invocation), + data->invocation, + g_dbus_method_invocation_get_user_data (data->invocation)); + return FALSE; +} + +static gboolean +dispatch_in_thread_func (GIOSchedulerJob *job, + GCancellable *cancellable, + gpointer user_data) +{ + DispatchData *data = user_data; + gboolean authorized; + + /* first check on the enclosing object (if any), then the interface */ + authorized = TRUE; + if (data->stub->priv->object != NULL) + { + g_signal_emit_by_name (data->stub->priv->object, + "authorize-method", + data->stub, + data->invocation, + &authorized); + } + if (authorized) + { + g_signal_emit (data->stub, + signals[G_AUTHORIZE_METHOD_SIGNAL], + 0, + data->invocation, + &authorized); + } + + if (authorized) + { + gboolean run_in_thread; + run_in_thread = (data->stub->priv->flags & G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); + if (run_in_thread) + { + /* might as well just re-use the existing thread */ + data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation), + g_dbus_method_invocation_get_sender (data->invocation), + g_dbus_method_invocation_get_object_path (data->invocation), + g_dbus_method_invocation_get_interface_name (data->invocation), + g_dbus_method_invocation_get_method_name (data->invocation), + g_dbus_method_invocation_get_parameters (data->invocation), + data->invocation, + g_dbus_method_invocation_get_user_data (data->invocation)); + } + else + { + /* bah, back to original context */ + g_main_context_invoke_full (data->context, + G_PRIORITY_DEFAULT, + dispatch_invoke_in_context_func, + dispatch_data_ref (data), + (GDestroyNotify) dispatch_data_unref); + } + } + else + { + /* do nothing */ + } + + return FALSE; +} + +static void +g_dbus_interface_method_dispatch_helper (GDBusInterfaceStub *stub, + GDBusInterfaceMethodCallFunc method_call_func, + GDBusMethodInvocation *invocation) +{ + gboolean has_handlers; + gboolean has_default_class_handler; + gboolean emit_authorized_signal; + gboolean run_in_thread; + + g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub)); + g_return_if_fail (method_call_func != NULL); + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + + /* optimization for the common case where + * + * a) no handler is connected and class handler is not overridden (both interface and object); and + * b) method calls are not dispatched in a thread + */ + has_handlers = g_signal_has_handler_pending (stub, + signals[G_AUTHORIZE_METHOD_SIGNAL], + 0, + TRUE); + has_default_class_handler = (G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->g_authorize_method == + g_dbus_interface_stub_g_authorize_method_default); + + emit_authorized_signal = (has_handlers || !has_default_class_handler); + if (!emit_authorized_signal) + { + if (stub->priv->object != NULL) + emit_authorized_signal = _g_dbus_object_stub_has_authorize_method_handlers (G_DBUS_OBJECT_STUB (stub->priv->object)); + } + + run_in_thread = (stub->priv->flags & G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); + if (!emit_authorized_signal && !run_in_thread) + { + method_call_func (g_dbus_method_invocation_get_connection (invocation), + g_dbus_method_invocation_get_sender (invocation), + g_dbus_method_invocation_get_object_path (invocation), + g_dbus_method_invocation_get_interface_name (invocation), + g_dbus_method_invocation_get_method_name (invocation), + g_dbus_method_invocation_get_parameters (invocation), + invocation, + g_dbus_method_invocation_get_user_data (invocation)); + } + else + { + DispatchData *data; + data = g_new0 (DispatchData, 1); + data->stub = stub; + data->method_call_func = method_call_func; + data->invocation = invocation; + data->context = g_main_context_get_thread_default (); + data->ref_count = 1; + if (data->context != NULL) + g_main_context_ref (data->context); + g_io_scheduler_push_job (dispatch_in_thread_func, + data, + (GDestroyNotify) dispatch_data_unref, + G_PRIORITY_DEFAULT, + NULL); /* GCancellable* */ + } +} + +static void +stub_intercept_handle_method_call(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (user_data); + g_dbus_interface_method_dispatch_helper (stub, + g_dbus_interface_stub_get_vtable (stub)->method_call, + invocation); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_interface_stub_get_connection: + * @stub: A #GDBusInterfaceStub. + * + * Gets the connection that @stub is exported on, if any. + * + * Returns: (transfer none): A #GDBusConnection or %NULL if @stub is + * not exported anywhere. Do not free, the object belongs to @stub. + */ +GDBusConnection * +g_dbus_interface_stub_get_connection (GDBusInterfaceStub *stub) +{ + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL); + return stub->priv->connection; +} + +/** + * g_dbus_interface_stub_get_object_path: + * @stub: A #GDBusInterfaceStub. + * + * Gets the object that that @stub is exported on, if any. + * + * Returns: A string owned by @stub or %NULL if stub is not exported + * anywhere. Do not free, the string belongs to @stub. + */ +const gchar * +g_dbus_interface_stub_get_object_path (GDBusInterfaceStub *stub) +{ + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL); + return stub->priv->object_path; +} + +/** + * g_dbus_interface_stub_export: + * @stub: The D-Bus interface to export. + * @connection: A #GDBusConnection to export @stub on. + * @object_path: The path to export the interface at. + * @error: Return location for error or %NULL. + * + * Exports @stubs at @object_path on @connection. + * + * Use g_dbus_interface_stub_unexport() to unexport the object. + * + * Returns: %TRUE if the interface was exported, other %FALSE with + * @error set. + */ +gboolean +g_dbus_interface_stub_export (GDBusInterfaceStub *stub, + GDBusConnection *connection, + const gchar *object_path, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), 0); + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (error == NULL || *error == NULL, 0); + + ret = FALSE; + if (stub->priv->registration_id > 0) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, /* TODO: new error code */ + "The object is already exported"); + goto out; + } + + g_assert (stub->priv->connection == NULL); + g_assert (stub->priv->object_path == NULL); + g_assert (stub->priv->hooked_vtable == NULL); + + /* Hook the vtable since we need to intercept method calls for + * ::g-authorize-method and for dispatching in thread vs + * context + */ + stub->priv->hooked_vtable = g_memdup (g_dbus_interface_stub_get_vtable (stub), sizeof (GDBusInterfaceVTable)); + stub->priv->hooked_vtable->method_call = stub_intercept_handle_method_call; + + stub->priv->connection = g_object_ref (connection); + stub->priv->object_path = g_strdup (object_path); + stub->priv->registration_id = g_dbus_connection_register_object (connection, + object_path, + g_dbus_interface_stub_get_info (stub), + stub->priv->hooked_vtable, + stub, + NULL, /* user_data_free_func */ + error); + if (stub->priv->registration_id == 0) + goto out; + + ret = TRUE; + + out: + return ret; +} + +/** + * g_dbus_interface_stub_unexport: + * @stub: A #GDBusInterfaceStub. + * + * Stops exporting an interface previously exported with + * g_dbus_interface_stub_export(). + */ +void +g_dbus_interface_stub_unexport (GDBusInterfaceStub *stub) +{ + g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub)); + g_return_if_fail (stub->priv->registration_id > 0); + + g_assert (stub->priv->connection != NULL); + g_assert (stub->priv->object_path != NULL); + g_assert (stub->priv->hooked_vtable != NULL); + + g_warn_if_fail (g_dbus_connection_unregister_object (stub->priv->connection, + stub->priv->registration_id)); + + g_object_unref (stub->priv->connection); + g_free (stub->priv->object_path); + stub->priv->connection = NULL; + stub->priv->object_path = NULL; + stub->priv->hooked_vtable = NULL; + stub->priv->registration_id = 0; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusinterfacestub.h b/gio/gdbusinterfacestub.h new file mode 100644 index 000000000..8e03d72d4 --- /dev/null +++ b/gio/gdbusinterfacestub.h @@ -0,0 +1,105 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_INTERFACE_STUB_H__ +#define __G_DBUS_INTERFACE_STUB_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_INTERFACE_STUB (g_dbus_interface_stub_get_type ()) +#define G_DBUS_INTERFACE_STUB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStub)) +#define G_DBUS_INTERFACE_STUB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStubClass)) +#define G_DBUS_INTERFACE_STUB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStubClass)) +#define G_IS_DBUS_INTERFACE_STUB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_INTERFACE_STUB)) +#define G_IS_DBUS_INTERFACE_STUB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_INTERFACE_STUB)) + +typedef struct _GDBusInterfaceStubClass GDBusInterfaceStubClass; +typedef struct _GDBusInterfaceStubPrivate GDBusInterfaceStubPrivate; + +/** + * GDBusInterfaceStub: + * + * The #GDBusInterfaceStub structure contains private data and should + * only be accessed using the provided API. + */ +struct _GDBusInterfaceStub +{ + /*< private >*/ + GObject parent_instance; + GDBusInterfaceStubPrivate *priv; +}; + +/** + * GDBusInterfaceStubClass: + * @parent_class: The parent class. + * @get_info: Returns a #GDBusInterfaceInfo. See g_dbus_interface_stub_get_info() for details. + * @get_vtable: Returns a #GDBusInterfaceVTable. See g_dbus_interface_stub_get_vtable() for details. + * @get_properties: Returns a new, floating, #GVariant with all properties. See g_dbus_interface_stub_get_properties(). + * @flush: Emits outstanding changes, if any. See g_dbus_interface_stub_flush(). + * @g_authorize_method: Signal class handler for the #GDBusInterfaceStub::g-authorize-method signal. + * + * Class structure for #GDBusInterfaceStub. + */ +struct _GDBusInterfaceStubClass +{ + GObjectClass parent_class; + + /* Virtual Functions */ + GDBusInterfaceInfo *(*get_info) (GDBusInterfaceStub *stub); + GDBusInterfaceVTable *(*get_vtable) (GDBusInterfaceStub *stub); + GVariant *(*get_properties) (GDBusInterfaceStub *stub); + void (*flush) (GDBusInterfaceStub *stub); + + /*< private >*/ + gpointer vfunc_padding[8]; + /*< public >*/ + + /* Signals */ + gboolean (*g_authorize_method) (GDBusInterfaceStub *stub, + GDBusMethodInvocation *invocation); + + /*< private >*/ + gpointer signal_padding[8]; +}; + +GType g_dbus_interface_stub_get_type (void) G_GNUC_CONST; +GDBusInterfaceStubFlags g_dbus_interface_stub_get_flags (GDBusInterfaceStub *stub); +void g_dbus_interface_stub_set_flags (GDBusInterfaceStub *stub, + GDBusInterfaceStubFlags flags); +GDBusInterfaceInfo *g_dbus_interface_stub_get_info (GDBusInterfaceStub *stub); +GDBusInterfaceVTable *g_dbus_interface_stub_get_vtable (GDBusInterfaceStub *stub); +GVariant *g_dbus_interface_stub_get_properties (GDBusInterfaceStub *stub); +void g_dbus_interface_stub_flush (GDBusInterfaceStub *stub); + +gboolean g_dbus_interface_stub_export (GDBusInterfaceStub *stub, + GDBusConnection *connection, + const gchar *object_path, + GError **error); +void g_dbus_interface_stub_unexport (GDBusInterfaceStub *stub); +GDBusConnection *g_dbus_interface_stub_get_connection (GDBusInterfaceStub *stub); +const gchar *g_dbus_interface_stub_get_object_path (GDBusInterfaceStub *stub); + +G_END_DECLS + +#endif /* __G_DBUS_INTERFACE_STUB_H */ diff --git a/gio/gdbusobject.c b/gio/gdbusobject.c new file mode 100644 index 000000000..2211d4c4f --- /dev/null +++ b/gio/gdbusobject.c @@ -0,0 +1,191 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobject.h" +#include "gdbusinterface.h" +#include "gdbusutils.h" +#include "gio-marshal.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusobject + * @short_description: Base type for D-Bus objects + * @include: gio/gio.h + * + * The #GDBusObject type is the base type for D-Bus objects on both + * the service side (see #GDBusObjectStub) and the client side (see + * #GDBusObjectProxy). It is essentially just a container of + * interfaces. + */ + +typedef GDBusObjectIface GDBusObjectInterface; +G_DEFINE_INTERFACE (GDBusObject, g_dbus_object, G_TYPE_OBJECT) + +static void +g_dbus_object_default_init (GDBusObjectIface *iface) +{ + /** + * GDBusObject::interface-added: + * @object: The #GDBusObject emitting the signal. + * @interface: The #GDBusInterface that was added. + * + * Emitted when @interface is added to @object. + */ + g_signal_new ("interface-added", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectIface, interface_added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_DBUS_INTERFACE); + + /** + * GDBusObject::interface-removed: + * @object: The #GDBusObject emitting the signal. + * @interface: The #GDBusInterface that was removed. + * + * Emitted when @interface is removed from @object. + */ + g_signal_new ("interface-removed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectIface, interface_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_DBUS_INTERFACE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_object_get_object_path: + * @object: A #GDBusObject. + * + * Gets the object path for @object. + * + * Returns: A string owned by @object. Do not free. + */ +const gchar * +g_dbus_object_get_object_path (GDBusObject *object) +{ + GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object); + return iface->get_object_path (object); +} + +/** + * g_dbus_object_get_interfaces: + * @object: A #GDBusObject. + * + * Gets the D-Bus interfaces associated with @object. + * + * Returns: (element-type GDBusInterface) (transfer full) : A list of #GDBusInterface instances. + * The returned list must be freed by g_list_free() after each element has been freed + * with g_object_unref(). + */ +GList * +g_dbus_object_get_interfaces (GDBusObject *object) +{ + GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object); + return iface->get_interfaces (object); +} + +/** + * g_dbus_object_get_interface: + * @object: A #GDBusObject. + * @interface_name: A D-Bus interface name. + * + * Gets the D-Bus interface with name @interface_name associated with + * @object, if any. + * + * Returns: %NULL if not found, otherwise a #GDBusInterface that must + * be freed with g_object_unref(). + */ +GDBusInterface * +g_dbus_object_get_interface (GDBusObject *object, + const gchar *interface_name) +{ + GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + return iface->get_interface (object, interface_name); +} + + +/** + * g_dbus_object_peek_with_typecheck: + * @object: A #GDBusObject. + * @interface_name: A D-Bus interface name. + * @type: The #GType that the returned object must conform to. + * + * Like g_dbus_object_lookup_with_typecheck() except that the caller + * does not own a reference to the returned object. + * + * This function is intended to only be used in type + * implementations. + * + * Returns: A #GDBusInterface implementing @type or %NULL if + * not found. Do not free the returned object, it is owned by + * @object. + */ +gpointer +g_dbus_object_peek_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type) +{ + GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + return iface->peek_with_typecheck (object, interface_name, type); +} + +/** + * g_dbus_object_lookup_with_typecheck: + * @object: A #GDBusObject. + * @interface_name: A D-Bus interface name. + * @type: The #GType that the returned object must conform to. + * + * Like g_dbus_object_get_interface() but warns on stderr if the + * returned object, if any, does not conform to @type. + * + * This function is intended to only be used in type + * implementations. + * + * Returns: A #GDBusInterface implementing @type or %NULL if + * not found. Free with g_object_unref(). + */ +gpointer +g_dbus_object_lookup_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type) +{ + GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + return iface->lookup_with_typecheck (object, interface_name, type); +} + diff --git a/gio/gdbusobject.h b/gio/gdbusobject.h new file mode 100644 index 000000000..e941ddd8a --- /dev/null +++ b/gio/gdbusobject.h @@ -0,0 +1,95 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_OBJECT_H__ +#define __G_DBUS_OBJECT_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_OBJECT (g_dbus_object_get_type()) +#define G_DBUS_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT, GDBusObject)) +#define G_IS_DBUS_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT)) +#define G_DBUS_OBJECT_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), G_TYPE_DBUS_OBJECT, GDBusObjectIface)) + +typedef struct _GDBusObjectIface GDBusObjectIface; + +/** + * GDBusObjectIface: + * @parent_iface: The parent interface. + * @get_object_path: Returns the object path. See g_dbus_object_get_object_path(). + * @get_interfaces: Returns all interfaces. See g_dbus_object_get_interfaces(). + * @get_interface: Returns an interface by name. See g_dbus_object_get_interface(). + * @lookup_with_typecheck: Like @get_interface but warns on stderr if the returned object, if any, + * does not conform to @type. Returned object must be freed by the caller. + * @peek_with_typecheck: Like @lookup_with_typecheck but does not transfer the reference. + * @interface_added: Signal handler for the #GDBusObject::interface-added signal. + * @interface_removed: Signal handler for the #GDBusObject::interface-removed signal. + * + * Base object type for D-Bus objects. + * + * The @lookup_with_typecheck and @peek_with_typecheck + * virtual functions should only be used by D-Bus interface + * implementations. + */ +struct _GDBusObjectIface +{ + GTypeInterface parent_iface; + + /* Virtual Functions */ + const gchar *(*get_object_path) (GDBusObject *object); + GList *(*get_interfaces) (GDBusObject *object); + GDBusInterface *(*get_interface) (GDBusObject *object, + const gchar *interface_name); + + gpointer (*lookup_with_typecheck) (GDBusObject *object, + const gchar *interface_name, + GType type); + gpointer (*peek_with_typecheck) (GDBusObject *object, + const gchar *interface_name, + GType type); + + /* Signals */ + void (*interface_added) (GDBusObject *object, + GDBusInterface *interface); + void (*interface_removed) (GDBusObject *object, + GDBusInterface *interface); + +}; + +GType g_dbus_object_get_type (void) G_GNUC_CONST; +const gchar *g_dbus_object_get_object_path (GDBusObject *object); +GList *g_dbus_object_get_interfaces (GDBusObject *object); +GDBusInterface *g_dbus_object_get_interface (GDBusObject *object, + const gchar *interface_name); + +gpointer g_dbus_object_peek_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type); +gpointer g_dbus_object_lookup_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type); + +G_END_DECLS + +#endif /* __G_DBUS_OBJECT_H__ */ diff --git a/gio/gdbusobjectmanager.c b/gio/gdbusobjectmanager.c new file mode 100644 index 000000000..6b3b29164 --- /dev/null +++ b/gio/gdbusobjectmanager.c @@ -0,0 +1,216 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobject.h" +#include "gdbusobjectmanager.h" +#include "gio-marshal.h" +#include "gdbusinterface.h" +#include "gdbusutils.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusobjectmanager + * @short_description: Base type for D-Bus object managers + * @include: gio/gio.h + * + * The #GDBusObjectManager type is the base type for service- and + * client-side implementations of the standardized org.freedesktop.DBus.ObjectManager + * interface. + * + * See #GDBusObjectManagerClient for the client-side implementation + * and #GDBusObjectManagerServer for the service-side implementation. + */ + +/** + * GDBusObjectManager: + * + * A D-Bus object. + */ + +typedef GDBusObjectManagerIface GDBusObjectManagerInterface; +G_DEFINE_INTERFACE (GDBusObjectManager, g_dbus_object_manager, G_TYPE_OBJECT) + +static void +g_dbus_object_manager_default_init (GDBusObjectManagerIface *iface) +{ + /** + * GDBusObjectManager::object-added: + * @manager: The #GDBusObjectManager emitting the signal. + * @object: The #GDBusObject that was added. + * + * Emitted when @object is added to @manager. + */ + g_signal_new ("object-added", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectManagerIface, object_added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_DBUS_OBJECT); + + /** + * GDBusObjectManager::object-removed: + * @manager: The #GDBusObjectManager emitting the signal. + * @object: The #GDBusObject that was removed. + * + * Emitted when @object is removed from @manager. + */ + g_signal_new ("object-removed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectManagerIface, object_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_DBUS_OBJECT); + + /** + * GDBusObjectManager::interface-added: + * @manager: The #GDBusObjectManager emitting the signal. + * @object: The #GDBusObject on which an interface was added. + * @interface: The #GDBusInterface that was added. + * + * Emitted when @interface is added to @object. + * + * This signal exists purely as a convenience to avoid having to + * connect signals to all objects managed by @manager. + */ + g_signal_new ("interface-added", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectManagerIface, interface_added), + NULL, + NULL, + _gio_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, + 2, + G_TYPE_DBUS_OBJECT, + G_TYPE_DBUS_INTERFACE); + + /** + * GDBusObjectManager::interface-removed: + * @manager: The #GDBusObjectManager emitting the signal. + * @object: The #GDBusObject on which an interface was removed. + * @interface: The #GDBusInterface that was removed. + * + * Emitted when @interface has been removed from @object. + * + * This signal exists purely as a convenience to avoid having to + * connect signals to all objects managed by @manager. + */ + g_signal_new ("interface-removed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectManagerIface, interface_removed), + NULL, + NULL, + _gio_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, + 2, + G_TYPE_DBUS_OBJECT, + G_TYPE_DBUS_INTERFACE); + +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_object_manager_get_object_path: + * @manager: A #GDBusObjectManager. + * + * Gets the object path that @manager is for. + * + * Returns: A string owned by @manager. Do not free. + */ +const gchar * +g_dbus_object_manager_get_object_path (GDBusObjectManager *manager) +{ + GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager); + return iface->get_object_path (manager); +} + +/** + * g_dbus_object_manager_get_objects: + * @manager: A #GDBusObjectManager. + * + * Gets all #GDBusObject objects known to @manager. + * + * Returns: (transfer full) (element-type GDBusObject): A list of + * #GDBusObject objects. The returned list should be freed with + * g_list_free() after each element has been freed with + * g_object_unref(). + */ +GList * +g_dbus_object_manager_get_objects (GDBusObjectManager *manager) +{ + GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager); + return iface->get_objects (manager); +} + +/** + * g_dbus_object_manager_get_object: + * @manager: A #GDBusObjectManager. + * @object_path: Object path to lookup. + * + * Gets the #GDBusObjectProxy at @object_path, if any. + * + * Returns: A #GDBusObject or %NULL. Free with g_object_unref(). + */ +GDBusObject * +g_dbus_object_manager_get_object (GDBusObjectManager *manager, + const gchar *object_path) +{ + GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + return iface->get_object (manager, object_path); +} + +/** + * g_dbus_object_manager_get_interface: + * @manager: A #GDBusObjectManager. + * @object_path: Object path to lookup. + * @interface_name: D-Bus interface name to lookup. + * + * Gets the interface proxy for @interface_name at @object_path, if + * any. + * + * Returns: A #GDBusInterface instance or %NULL. Free with g_object_unref(). + */ +GDBusInterface * +g_dbus_object_manager_get_interface (GDBusObjectManager *manager, + const gchar *object_path, + const gchar *interface_name) +{ + GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + return iface->get_interface (manager, object_path, interface_name); +} diff --git a/gio/gdbusobjectmanager.h b/gio/gdbusobjectmanager.h new file mode 100644 index 000000000..2b82bf47e --- /dev/null +++ b/gio/gdbusobjectmanager.h @@ -0,0 +1,89 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_OBJECT_MANAGER_H__ +#define __G_DBUS_OBJECT_MANAGER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_OBJECT_MANAGER (g_dbus_object_manager_get_type()) +#define G_DBUS_OBJECT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_MANAGER, GDBusObjectManager)) +#define G_IS_DBUS_OBJECT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_MANAGER)) +#define G_DBUS_OBJECT_MANAGER_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), G_TYPE_DBUS_OBJECT_MANAGER, GDBusObjectManagerIface)) + +typedef struct _GDBusObjectManagerIface GDBusObjectManagerIface; + +/** + * GDBusObjectManagerIface: + * @parent_iface: The parent interface. + * @get_object_path: Virtual function for g_dbus_object_manager_get_object_path(). + * @get_objects: Virtual function for g_dbus_object_manager_get_objects(). + * @get_object: Virtual function for g_dbus_object_manager_get_object(). + * @get_interface: Virtual function for g_dbus_object_manager_get_interface(). + * @object_added: Signal handler for the #GDBusObjectManager::object-added signal. + * @object_removed: Signal handler for the #GDBusObjectManager::object-removed signal. + * @interface_added: Signal handler for the #GDBusObjectManager::interface-added signal. + * @interface_removed: Signal handler for the #GDBusObjectManager::interface-removed signal. + * + * Base type for D-Bus object managers. + */ +struct _GDBusObjectManagerIface +{ + GTypeInterface parent_iface; + + /* Virtual Functions */ + const gchar *(*get_object_path) (GDBusObjectManager *manager); + GList *(*get_objects) (GDBusObjectManager *manager); + GDBusObject *(*get_object) (GDBusObjectManager *manager, + const gchar *object_path); + GDBusInterface *(*get_interface) (GDBusObjectManager *manager, + const gchar *object_path, + const gchar *interface_name); + + /* Signals */ + void (*object_added) (GDBusObjectManager *manager, + GDBusObject *object); + void (*object_removed) (GDBusObjectManager *manager, + GDBusObject *object); + + void (*interface_added) (GDBusObjectManager *manager, + GDBusObject *object, + GDBusInterface *interface); + void (*interface_removed) (GDBusObjectManager *manager, + GDBusObject *object, + GDBusInterface *interface); +}; + +GType g_dbus_object_manager_get_type (void) G_GNUC_CONST; +const gchar *g_dbus_object_manager_get_object_path (GDBusObjectManager *manager); +GList *g_dbus_object_manager_get_objects (GDBusObjectManager *manager); +GDBusObject *g_dbus_object_manager_get_object (GDBusObjectManager *manager, + const gchar *object_path); +GDBusInterface *g_dbus_object_manager_get_interface (GDBusObjectManager *manager, + const gchar *object_path, + const gchar *interface_name); + +G_END_DECLS + +#endif /* __G_DBUS_OBJECT_MANAGER_H__ */ diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c new file mode 100644 index 000000000..79eb5cc5e --- /dev/null +++ b/gio/gdbusobjectmanagerclient.c @@ -0,0 +1,1575 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobjectmanager.h" +#include "gdbusobjectmanagerclient.h" +#include "gdbusobject.h" +#include "gdbusprivate.h" +#include "gio-marshal.h" +#include "gioenumtypes.h" +#include "ginitable.h" +#include "gasyncresult.h" +#include "gsimpleasyncresult.h" +#include "gasyncinitable.h" +#include "gdbusconnection.h" +#include "gdbusutils.h" +#include "gdbusobject.h" +#include "gdbusobjectproxy.h" +#include "gdbusproxy.h" +#include "gdbusinterface.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusobjectmanagerclient + * @short_description: Client-side object manager + * @include: gio/gio.h + * + * #GDBusObjectManagerClient is used to create, monitor and delete object + * proxies for remote objects exported by a #GDBusObjectManagerServer (or any + * code implementing the org.freedesktop.DBus.ObjectManager + * interface). + * + * Once an instance of this type has been created, you can connect to + * the #GDBusObjectManager::object-added and + * #GDBusObjectManager::object-removed signals and inspect the + * #GDBusObjectProxy objects returned by + * g_dbus_object_manager_get_objects(). + * + * If the name for a #GDBusObjectManagerClient is not owned by anyone at + * object construction time, the default behavior is to request the + * message bus to launch an owner for the name. This behavior can be + * disabled using the %G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START + * flag. It's also worth noting that this only works if the name of + * interest is activatable in the first place. E.g. in some cases it + * is not possible to launch an owner for the requested name. In this + * case, #GDBusObjectManagerClient object construction still succeeds but + * there will be no object proxies + * (e.g. g_dbus_object_manager_get_objects() returns the empty list) and + * the #GDBusObjectManagerClient:name-owner property is %NULL. + * + * The owner of the requested name can come and go (for example + * consider a system service being restarted) – #GDBusObjectManagerClient + * handles this case too; simply connect to the #GObject::notify + * signal to watch for changes on the #GDBusObjectManagerClient:name-owner + * property. When the name owner vanishes, the behavior is that + * #GDBusObjectManagerClient:name-owner is set to %NULL (this includes + * emission of the #GObject::notify signal) and then + * #GDBusObjectManager::object-removed signals are synthesized + * for all currently existing object proxies. Since + * #GDBusObjectManagerClient:name-owner is %NULL when this happens, you can + * use this information to disambiguate a synthesized signal from a + * genuine signal caused by object removal on the remote + * #GDBusObjectManager. Similarly, when a new name owner appears, + * #GDBusObjectManager::object-added signals are synthesized + * while #GDBusObjectManagerClient:name-owner is still %NULL. Only when all + * object proxies have been added, the #GDBusObjectManagerClient:name-owner + * is set to the new name owner (this includes emission of the + * #GObject::notify signal). Furthermore, you are guaranteed that + * #GDBusObjectManagerClient:name-owner will alternate between a name owner + * (e.g. :1.42) and %NULL even in the case where + * the name of interest is atomically replaced + * + * Ultimately, #GDBusObjectManagerClient is used to obtain #GDBusProxy + * instances. All signals (including the + * org.freedesktop.DBus.Properties::PropertiesChanged + * signal) delivered to #GDBusProxy instances are guaranteed to + * originate from the name owner. This guarantee along with the + * behavior described above, means that certain race conditions + * including the half the proxy is from the old owner + * and the other half is from the new owner problem + * cannot happen. + * + * To avoid having the application connect to signals on the returned + * #GDBusObjectProxy and #GDBusProxy objects, the + * #GDBusObject::interface-added, + * #GDBusObject::interface-removed, + * #GDBusProxy::g-properties-changed and + * #GDBusProxy::g-signal signals + * are also emitted on the #GDBusObjectManagerClient instance managing these + * objects. The signals emitted are + * #GDBusObjectManager::interface-added, + * #GDBusObjectManager::interface-removed, + * #GDBusObjectManagerClient::interface-proxy-properties-changed and + * #GDBusObjectManagerClient::interface-proxy-signal. + * + * Note that all callbacks and signals are emitted in the + * thread-default main loop + * that the #GDBusObjectManagerClient object was constructed + * in. Additionally, the #GDBusObjectProxy and #GDBusProxy objects + * originating from the #GDBusObjectManagerClient object will be created in + * the same context and, consequently, will deliver signals in the + * same main loop. + */ + +struct _GDBusObjectManagerClientPrivate +{ + GBusType bus_type; + GDBusConnection *connection; + gchar *object_path; + gchar *name; + gchar *name_owner; + GDBusObjectManagerClientFlags flags; + + GDBusProxy *control_proxy; + + GHashTable *map_object_path_to_object_proxy; + + guint signal_subscription_id; + gchar *match_rule; + + GDBusProxyTypeFunc get_proxy_type_func; + gpointer get_proxy_type_user_data; +}; + +enum +{ + PROP_0, + PROP_BUS_TYPE, + PROP_CONNECTION, + PROP_FLAGS, + PROP_OBJECT_PATH, + PROP_NAME, + PROP_NAME_OWNER, + PROP_GET_PROXY_TYPE_FUNC, + PROP_GET_PROXY_TYPE_USER_DATA +}; + +enum +{ + INTERFACE_PROXY_SIGNAL_SIGNAL, + INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void initable_iface_init (GInitableIface *initable_iface); +static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); +static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerClient, g_dbus_object_manager_client, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init)); + +static void maybe_unsubscribe_signals (GDBusObjectManagerClient *manager); + +static void on_control_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + +static void process_get_all_result (GDBusObjectManagerClient *manager, + GVariant *value, + const gchar *name_owner); + +static void +g_dbus_object_manager_client_finalize (GObject *object) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object); + + maybe_unsubscribe_signals (manager); + + g_hash_table_unref (manager->priv->map_object_path_to_object_proxy); + + if (manager->priv->control_proxy != NULL) + { + g_warn_if_fail (g_signal_handlers_disconnect_by_func (manager->priv->control_proxy, + on_control_proxy_g_signal, + manager) == 1); + g_object_unref (manager->priv->control_proxy); + } + g_object_unref (manager->priv->connection); + g_free (manager->priv->object_path); + g_free (manager->priv->name); + g_free (manager->priv->name_owner); + + if (G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize (object); +} + +static void +g_dbus_object_manager_client_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object); + + switch (prop_id) + { + case PROP_CONNECTION: + g_value_set_object (value, g_dbus_object_manager_client_get_connection (manager)); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager))); + break; + + case PROP_NAME: + g_value_set_string (value, g_dbus_object_manager_client_get_name (manager)); + break; + + case PROP_FLAGS: + g_value_set_flags (value, g_dbus_object_manager_client_get_flags (manager)); + break; + + case PROP_NAME_OWNER: + g_value_take_string (value, g_dbus_object_manager_client_get_name_owner (manager)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_manager_client_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object); + + switch (prop_id) + { + case PROP_BUS_TYPE: + manager->priv->bus_type = g_value_get_enum (value); + break; + + case PROP_CONNECTION: + if (g_value_get_object (value) != NULL) + { + g_assert (manager->priv->connection == NULL); + g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value))); + manager->priv->connection = g_value_dup_object (value); + } + break; + + case PROP_OBJECT_PATH: + g_assert (manager->priv->object_path == NULL); + g_assert (g_variant_is_object_path (g_value_get_string (value))); + manager->priv->object_path = g_value_dup_string (value); + break; + + case PROP_NAME: + g_assert (manager->priv->name == NULL); + g_assert (g_dbus_is_name (g_value_get_string (value))); + manager->priv->name = g_value_dup_string (value); + break; + + case PROP_FLAGS: + manager->priv->flags = g_value_get_flags (value); + break; + + case PROP_GET_PROXY_TYPE_FUNC: + manager->priv->get_proxy_type_func = g_value_get_pointer (value); + break; + + case PROP_GET_PROXY_TYPE_USER_DATA: + manager->priv->get_proxy_type_user_data = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_object_manager_client_finalize; + gobject_class->set_property = g_dbus_object_manager_client_set_property; + gobject_class->get_property = g_dbus_object_manager_client_get_property; + + /** + * GDBusObjectManagerClient:connection: + * + * The #GDBusConnection to use. + */ + g_object_class_install_property (gobject_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "The connection to use", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerClient:bus-type: + * + * If this property is not %G_BUS_TYPE_NONE, then + * #GDBusObjectManagerClient:connection must be %NULL and will be set to the + * #GDBusConnection obtained by calling g_bus_get() with the value + * of this property. + */ + g_object_class_install_property (gobject_class, + PROP_BUS_TYPE, + g_param_spec_enum ("bus-type", + "Bus Type", + "The bus to connect to, if any", + G_TYPE_BUS_TYPE, + G_BUS_TYPE_NONE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusObjectManagerClient:flags: + * + * Flags from the #GDBusObjectManagerClientFlags enumeration. + */ + g_object_class_install_property (gobject_class, + PROP_FLAGS, + g_param_spec_flags ("flags", + "Flags", + "Flags for the proxy manager", + G_TYPE_DBUS_OBJECT_MANAGER_CLIENT_FLAGS, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusObjectManagerClient:object-path: + * + * The object path the manager is for. + */ + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path of the control object", + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerClient:name: + * + * The well-known name or unique name that the manager is for. + */ + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "Name that the manager is for", + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerClient:name-owner: + * + * The unique name that owns #GDBusObjectManagerClient:name or %NULL if + * no-one is currently owning the name. Connect to the + * #GObject::notify signal to track changes to this property. + */ + g_object_class_install_property (gobject_class, + PROP_NAME_OWNER, + g_param_spec_string ("name-owner", + "Name Owner", + "The owner of the name we are watching", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerClient:get-proxy-type-func: + * + * The #GDBusProxyTypeFunc to use when determining what #GType to + * use for interface proxies or %NULL. + */ + g_object_class_install_property (gobject_class, + PROP_GET_PROXY_TYPE_FUNC, + g_param_spec_pointer ("get-proxy-type-func", + "GDBusProxyTypeFunc Function Pointer", + "The GDBusProxyTypeFunc pointer to use", + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerClient:get-proxy-type-user-data: + * + * The #gpointer user_data to pass to #GDBusObjectManagerClient:get-proxy-type-func. + */ + g_object_class_install_property (gobject_class, + PROP_GET_PROXY_TYPE_USER_DATA, + g_param_spec_pointer ("get-proxy-type-user-data", + "GDBusProxyTypeFunc User Data", + "The GDBusProxyTypeFunc user_data", + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerClient::interface-proxy-signal: + * @manager: The #GDBusObjectManagerClient emitting the signal. + * @object_proxy: The #GDBusObjectProxy on which an interface is emitting a D-Bus signal. + * @interface_proxy: The #GDBusProxy that is emitting a D-Bus signal. + * @sender_name: The sender of the signal or NULL if the connection is not a bus connection. + * @signal_name: The signal name. + * @parameters: A #GVariant tuple with parameters for the signal. + * + * Emitted when a D-Bus signal is received on @interface_proxy. + * + * This signal exists purely as a convenience to avoid having to + * connect signals to all interface proxies managed by @manager. + * + * This signal is emitted in the + * thread-default main loop + * that @manager was constructed in. + */ + signals[INTERFACE_PROXY_SIGNAL_SIGNAL] = + g_signal_new ("interface-proxy-signal", + G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_signal), + NULL, + NULL, + _gio_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANT, + G_TYPE_NONE, + 5, + G_TYPE_DBUS_OBJECT_PROXY, + G_TYPE_DBUS_PROXY, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_VARIANT); + + /** + * GDBusObjectManagerClient::interface-proxy-properties-changed: + * @manager: The #GDBusObjectManagerClient emitting the signal. + * @object_proxy: The #GDBusObjectProxy on which an interface has properties that are changing. + * @interface_proxy: The #GDBusProxy that has properties that are changing. + * @changed_properties: A #GVariant containing the properties that changed. + * @invalidated_properties: A %NULL terminated array of properties that was invalidated. + * + * Emitted when one or more D-Bus properties on proxy changes. The + * local cache has already been updated when this signal fires. Note + * that both @changed_properties and @invalidated_properties are + * guaranteed to never be %NULL (either may be empty though). + * + * This signal exists purely as a convenience to avoid having to + * connect signals to all interface proxies managed by @manager. + * + * This signal is emitted in the + * thread-default main loop + * that @manager was constructed in. + */ + signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL] = + g_signal_new ("interface-proxy-properties-changed", + G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_properties_changed), + NULL, + NULL, + _gio_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXED, + G_TYPE_NONE, + 4, + G_TYPE_DBUS_OBJECT_PROXY, + G_TYPE_DBUS_PROXY, + G_TYPE_VARIANT, + G_TYPE_STRV); + + g_type_class_add_private (klass, sizeof (GDBusObjectManagerClientPrivate)); +} + +static void +g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager) +{ + manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, + G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + GDBusObjectManagerClientPrivate); + manager->priv->map_object_path_to_object_proxy = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_object_manager_client_new_sync: + * @connection: A #GDBusConnection. + * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. + * @name: The owner of the control object (unique or well-known name). + * @object_path: The object path of the control object. + * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. + * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. + * @cancellable: A #GCancellable or %NULL + * @error: Return location for error or %NULL. + * + * Creates a new #GDBusObjectManagerClient object. + * + * This is a synchronous failable constructor - the calling thread is + * blocked until a reply is received. See g_dbus_object_manager_client_new() + * for the asynchronous version. + * + * Returns: A #GDBusObjectManagerClient object or %NULL if @error is + * set. Free with g_object_unref(). + */ +GDBusObjectManager * +g_dbus_object_manager_client_new_sync (GDBusConnection *connection, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GError **error) +{ + GInitable *initable; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || + g_dbus_is_name (name), NULL); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + cancellable, + error, + "connection", connection, + "flags", flags, + "name", name, + "object-path", object_path, + "get-proxy-type-func", get_proxy_type_func, + "get-proxy-type-user-data", get_proxy_type_user_data, + NULL); + if (initable != NULL) + return G_DBUS_OBJECT_MANAGER (initable); + else + return NULL; +} + +/** + * g_dbus_object_manager_client_new: + * @connection: A #GDBusConnection. + * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. + * @name: The owner of the control object (unique or well-known name). + * @object_path: The object path of the control object. + * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. + * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. + * @cancellable: A #GCancellable or %NULL + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: The data to pass to @callback. + * + * Asynchronously creates a new #GDBusObjectManagerClient object. + * + * This is an asynchronous failable constructor. When the result is + * ready, @callback will be invoked in the + * thread-default main loop + * of the thread you are calling this method from. You can + * then call g_dbus_object_manager_client_new_finish() to get the result. See + * g_dbus_object_manager_client_new_sync() for the synchronous version. + */ +void +g_dbus_object_manager_client_new (GDBusConnection *connection, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + g_return_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || + g_dbus_is_name (name)); + g_return_if_fail (g_variant_is_object_path (object_path)); + + g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "connection", connection, + "flags", flags, + "name", name, + "object-path", object_path, + "get-proxy-type-func", get_proxy_type_func, + "get-proxy-type-user-data", get_proxy_type_user_data, + NULL); +} + +/** + * g_dbus_object_manager_client_new_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_object_manager_client_new(). + * + * Returns: A #GDBusObjectManagerClient object or %NULL if @error is + * set. Free with g_object_unref(). + */ +GDBusObjectManager * +g_dbus_object_manager_client_new_finish (GAsyncResult *res, + GError **error) +{ + GObject *object; + GObject *source_object; + + source_object = g_async_result_get_source_object (res); + g_assert (source_object != NULL); + + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + res, + error); + g_object_unref (source_object); + + if (object != NULL) + return G_DBUS_OBJECT_MANAGER (object); + else + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_object_manager_client_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. + * @name: The owner of the control object (unique or well-known name). + * @object_path: The object path of the control object. + * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. + * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. + * @cancellable: A #GCancellable or %NULL + * @error: Return location for error or %NULL. + * + * Like g_dbus_object_manager_client_new_sync() but takes a #GBusType instead + * of a #GDBusConnection. + * + * This is a synchronous failable constructor - the calling thread is + * blocked until a reply is received. See g_dbus_object_manager_client_new_for_bus() + * for the asynchronous version. + * + * Returns: A #GDBusObjectManagerClient object or %NULL if @error is + * set. Free with g_object_unref(). + */ +GDBusObjectManager * +g_dbus_object_manager_client_new_for_bus_sync (GBusType bus_type, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GError **error) +{ + GInitable *initable; + + g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, NULL); + g_return_val_if_fail (g_dbus_is_name (name), NULL); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + cancellable, + error, + "bus-type", bus_type, + "flags", flags, + "name", name, + "object-path", object_path, + "get-proxy-type-func", get_proxy_type_func, + "get-proxy-type-user-data", get_proxy_type_user_data, + NULL); + if (initable != NULL) + return G_DBUS_OBJECT_MANAGER (initable); + else + return NULL; +} + +/** + * g_dbus_object_manager_client_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. + * @name: The owner of the control object (unique or well-known name). + * @object_path: The object path of the control object. + * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. + * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. + * @cancellable: A #GCancellable or %NULL + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: The data to pass to @callback. + * + * Like g_dbus_object_manager_client_new() but takes a #GBusType instead of a + * #GDBusConnection. + * + * This is an asynchronous failable constructor. When the result is + * ready, @callback will be invoked in the + * thread-default main loop + * of the thread you are calling this method from. You can + * then call g_dbus_object_manager_client_new_for_bus_finish() to get the result. See + * g_dbus_object_manager_client_new_for_bus_sync() for the synchronous version. + */ +void +g_dbus_object_manager_client_new_for_bus (GBusType bus_type, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (bus_type != G_BUS_TYPE_NONE); + g_return_if_fail (g_dbus_is_name (name)); + g_return_if_fail (g_variant_is_object_path (object_path)); + + g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "bus-type", bus_type, + "flags", flags, + "name", name, + "object-path", object_path, + "get-proxy-type-func", get_proxy_type_func, + "get-proxy-type-user-data", get_proxy_type_user_data, + NULL); +} + +/** + * g_dbus_object_manager_client_new_for_bus_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new_for_bus(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_object_manager_client_new_for_bus(). + * + * Returns: A #GDBusObjectManagerClient object or %NULL if @error is + * set. Free with g_object_unref(). + */ +GDBusObjectManager * +g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult *res, + GError **error) +{ + GObject *object; + GObject *source_object; + + source_object = g_async_result_get_source_object (res); + g_assert (source_object != NULL); + + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + res, + error); + g_object_unref (source_object); + + if (object != NULL) + return G_DBUS_OBJECT_MANAGER (object); + else + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_object_manager_client_get_connection: + * @manager: A #GDBusObjectManagerClient + * + * Gets the #GDBusConnection used by @manager. + * + * Returns: A #GDBusConnection object. Do not free, the object belongs + * to @manager. + */ +GDBusConnection * +g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); + return manager->priv->connection; +} + +/** + * g_dbus_object_manager_client_get_name: + * @manager: A #GDBusObjectManagerClient + * + * Gets the name that @manager is for. + * + * Returns: A unique or well-known name. Do not free, the string + * belongs to @manager. + */ +const gchar * +g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); + return manager->priv->name; +} + +/** + * g_dbus_object_manager_client_get_flags: + * @manager: A #GDBusObjectManagerClient + * + * Gets the flags that @manager was constructed with. + * + * Returns: Zero of more flags from the #GDBusObjectManagerClientFlags + * enumeration. + */ +GDBusObjectManagerClientFlags +g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE); + return manager->priv->flags; +} + +/** + * g_dbus_object_manager_client_get_name_owner: + * @manager: A #GDBusObjectManagerClient. + * + * The unique name that owns the name that @manager is for or %NULL if + * no-one currently owns that name. You can connect to the + * #GObject::notify signal to track changes to the + * #GDBusObjectManagerClient:name-owner property. + * + * Returns: The name owner or %NULL if no name owner exists. Free with + * g_free(). + */ +gchar * +g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); + return g_strdup (manager->priv->name_owner); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* signal handler for all objects we manage - we dispatch signals + * from here to the objects + */ +static void +signal_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data); + GDBusObjectProxy *object_proxy; + GDBusInterface *interface; + + object_proxy = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); + if (object_proxy == NULL) + goto out; + + //g_debug ("yay, signal_cb %s %s: %s\n", signal_name, object_path, g_variant_print (parameters, TRUE)); + + if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0) + { + if (g_strcmp0 (signal_name, "PropertiesChanged") == 0) + { + const gchar *interface_name; + GVariant *changed_properties; + const gchar **invalidated_properties; + + g_variant_get (parameters, + "(&s@a{sv}^a&s)", + &interface_name, + &changed_properties, + &invalidated_properties); + + interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name); + if (interface != NULL) + { + GVariantIter property_iter; + const gchar *property_name; + GVariant *property_value; + guint n; + + /* update caches... */ + g_variant_iter_init (&property_iter, changed_properties); + while (g_variant_iter_next (&property_iter, + "{&sv}", + &property_name, + &property_value)) + { + g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface), + property_name, + property_value); + g_variant_unref (property_value); + } + + for (n = 0; invalidated_properties[n] != NULL; n++) + { + g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface), + invalidated_properties[n], + NULL); + } + /* ... and then synthesize the signal */ + g_signal_emit (manager, + signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL], + 0, + object_proxy, + interface, + changed_properties, + invalidated_properties); + g_signal_emit_by_name (interface, + "g-properties-changed", + changed_properties, + invalidated_properties); + g_object_unref (interface); + } + g_variant_unref (changed_properties); + g_free (invalidated_properties); + } + } + else + { + /* regular signal - just dispatch it */ + interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name); + if (interface != NULL) + { + g_signal_emit (manager, + signals[INTERFACE_PROXY_SIGNAL_SIGNAL], + 0, + object_proxy, + interface, + sender_name, + signal_name, + parameters); + g_signal_emit_by_name (interface, + "g-signal", + sender_name, + signal_name, + parameters); + g_object_unref (interface); + } + } + + out: + ; +} + +static void +subscribe_signals (GDBusObjectManagerClient *manager, + const gchar *name_owner) +{ + GError *error; + GVariant *ret; + + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager)); + g_return_if_fail (manager->priv->signal_subscription_id == 0); + g_return_if_fail (g_dbus_is_unique_name (name_owner)); + + /* the bus daemon may not implement path_prefix so gracefully + * handle this by using a fallback + */ + manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'", + name_owner, + manager->priv->object_path); + + error = NULL; + ret = g_dbus_connection_call_sync (manager->priv->connection, + "org.freedesktop.DBus", + "/org/freedeskop/DBus", + "org.freedesktop.DBus", + "AddMatch", + g_variant_new ("(s)", + manager->priv->match_rule), + NULL, /* reply_type */ + G_DBUS_CALL_FLAGS_NONE, + -1, /* default timeout */ + NULL, /* TODO: Cancellable */ + &error); + if (ret != NULL) + { + /* yay, bus daemon supports path_namespace */ + g_variant_unref (ret); + + /* still need to ask GDBusConnection for the callbacks */ + manager->priv->signal_subscription_id = + g_dbus_connection_signal_subscribe (manager->priv->connection, + name_owner, + NULL, /* interface */ + NULL, /* member */ + NULL, /* path - TODO: really want wilcard support here */ + NULL, /* arg0 */ + G_DBUS_SIGNAL_FLAGS_NONE | + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + signal_cb, + manager, + NULL); /* user_data_free_func */ + + } + else + { + /* TODO: we could report this to the user + g_warning ("Message bus daemon does not support path_namespace: %s (%s %d)", + error->message, + g_quark_to_string (error->domain), + error->code); + */ + + g_error_free (error); + + /* no need to call RemoveMatch when done since it didn't work */ + g_free (manager->priv->match_rule); + manager->priv->match_rule = NULL; + + /* Fallback is to subscribe to *all* signals from the name owner which + * is rather wasteful. It's probably not a big practical problem because + * users typically want all objects that the name owner supplies. + */ + manager->priv->signal_subscription_id = + g_dbus_connection_signal_subscribe (manager->priv->connection, + name_owner, + NULL, /* interface */ + NULL, /* member */ + NULL, /* path - TODO: really want wilcard support here */ + NULL, /* arg0 */ + G_DBUS_SIGNAL_FLAGS_NONE, + signal_cb, + manager, + NULL); /* user_data_free_func */ + } +} + +static void +maybe_unsubscribe_signals (GDBusObjectManagerClient *manager) +{ + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager)); + + if (manager->priv->signal_subscription_id > 0) + { + g_dbus_connection_signal_unsubscribe (manager->priv->connection, + manager->priv->signal_subscription_id); + manager->priv->signal_subscription_id = 0; + } + + if (manager->priv->match_rule != NULL) + { + /* Since the AddMatch call succeeded this is guaranteed to not + * fail - therefore, don't bother checking the return value + */ + g_dbus_connection_call (manager->priv->connection, + "org.freedesktop.DBus", + "/org/freedeskop/DBus", + "org.freedesktop.DBus", + "RemoveMatch", + g_variant_new ("(s)", + manager->priv->match_rule), + NULL, /* reply_type */ + G_DBUS_CALL_FLAGS_NONE, + -1, /* default timeout */ + NULL, /* GCancellable */ + NULL, /* GAsyncReadyCallback */ + NULL); /* user data */ + g_free (manager->priv->match_rule); + manager->priv->match_rule = NULL; + } + +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_notify_g_name_owner (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data); + gchar *old_name_owner; + gchar *new_name_owner; + + old_name_owner = manager->priv->name_owner; + new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); + manager->priv->name_owner = NULL; + + if (g_strcmp0 (old_name_owner, new_name_owner) != 0) + { + GList *l; + GList *proxies; + + /* do the :name-owner notify with a NULL name - this way the user knows + * the ::object-proxy-removed following is because the name owner went + * away + */ + g_object_notify (G_OBJECT (manager), "name-owner"); + + /* remote manager changed; nuke all local proxies */ + proxies = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy); + g_list_foreach (proxies, (GFunc) g_object_ref, NULL); + g_hash_table_remove_all (manager->priv->map_object_path_to_object_proxy); + for (l = proxies; l != NULL; l = l->next) + { + GDBusObjectProxy *object_proxy = G_DBUS_OBJECT_PROXY (l->data); + g_signal_emit_by_name (manager, "object-removed", object_proxy); + } + g_list_foreach (proxies, (GFunc) g_object_unref, NULL); + g_list_free (proxies); + + /* nuke local filter */ + maybe_unsubscribe_signals (manager); + } + + if (new_name_owner != NULL) + { + GError *error; + GVariant *value; + + //g_debug ("repopulating for %s", new_name_owner); + + /* TODO: do this async! */ + subscribe_signals (manager, + new_name_owner); + error = NULL; + value = g_dbus_proxy_call_sync (manager->priv->control_proxy, + "GetManagedObjects", + NULL, /* parameters */ + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (value == NULL) + { + maybe_unsubscribe_signals (manager); + g_warning ("Error calling GetManagedObjects() when name owner %s for name %s came back: %s", + new_name_owner, + manager->priv->name, + error->message); + g_error_free (error); + } + else + { + process_get_all_result (manager, value, new_name_owner); + g_variant_unref (value); + } + + /* do the :name-owner notify *AFTER* emitting ::object-proxy-added signals - this + * way the user knows that the signals were emitted because the name owner came back + */ + manager->priv->name_owner = new_name_owner; + g_object_notify (G_OBJECT (manager), "name-owner"); + + } + g_free (old_name_owner); +} + +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (initable); + gboolean ret; + GVariant *value; + GDBusProxyFlags proxy_flags; + + ret = FALSE; + + if (manager->priv->bus_type != G_BUS_TYPE_NONE) + { + g_assert (manager->priv->connection == NULL); + manager->priv->connection = g_bus_get_sync (manager->priv->bus_type, cancellable, error); + if (manager->priv->connection == NULL) + goto out; + } + + proxy_flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; + if (manager->priv->flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START) + proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;; + + manager->priv->control_proxy = g_dbus_proxy_new_sync (manager->priv->connection, + proxy_flags, + NULL, /* GDBusInterfaceInfo* */ + manager->priv->name, + manager->priv->object_path, + "org.freedesktop.DBus.ObjectManager", + cancellable, + error); + if (manager->priv->control_proxy == NULL) + goto out; + + g_signal_connect (G_OBJECT (manager->priv->control_proxy), + "notify::g-name-owner", + G_CALLBACK (on_notify_g_name_owner), + manager); + + manager->priv->name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); + if (manager->priv->name_owner == NULL) + { + /* it's perfectly fine if there's no name owner.. we're just going to + * wait until one is ready + */ + } + else + { + /* yay, we have a name owner */ + g_signal_connect (manager->priv->control_proxy, + "g-signal", + G_CALLBACK (on_control_proxy_g_signal), + manager); + subscribe_signals (manager, + manager->priv->name_owner); + value = g_dbus_proxy_call_sync (manager->priv->control_proxy, + "GetManagedObjects", + NULL, /* parameters */ + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (value == NULL) + { + maybe_unsubscribe_signals (manager); + g_warn_if_fail (g_signal_handlers_disconnect_by_func (manager->priv->control_proxy, + on_control_proxy_g_signal, + manager) == 1); + g_object_unref (manager->priv->control_proxy); + manager->priv->control_proxy = NULL; + goto out; + } + + process_get_all_result (manager, value, manager->priv->name_owner); + g_variant_unref (value); + } + + ret = TRUE; + + out: + return ret; +} + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = initable_init; +} + +static void +async_initable_iface_init (GAsyncInitableIface *async_initable_iface) +{ + /* for now, just use default: run GInitable code in thread */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +add_interfaces (GDBusObjectManagerClient *manager, + const gchar *object_path, + GVariant *ifaces_and_properties, + const gchar *name_owner) +{ + GDBusObjectProxy *op; + gboolean added; + GVariantIter iter; + const gchar *interface_name; + GVariant *properties; + + g_return_if_fail (g_dbus_is_unique_name (name_owner)); + + added = FALSE; + op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); + if (op == NULL) + { + op = _g_dbus_object_proxy_new (manager->priv->connection, object_path); + added = TRUE; + } + + g_variant_iter_init (&iter, ifaces_and_properties); + while (g_variant_iter_next (&iter, + "{&s@a{sv}}", + &interface_name, + &properties)) + { + GDBusProxy *interface_proxy; + GError *error; + GType interface_proxy_type; + + if (manager->priv->get_proxy_type_func != NULL) + { + interface_proxy_type = manager->priv->get_proxy_type_func (manager, + object_path, + interface_name, + manager->priv->get_proxy_type_user_data); + g_warn_if_fail (g_type_is_a (interface_proxy_type, G_TYPE_DBUS_PROXY)); + } + else + { + interface_proxy_type = G_TYPE_DBUS_PROXY; + } + + /* this is fine - there is no blocking IO because we pass DO_NOT_LOAD_PROPERTIES and + * DO_NOT_CONNECT_SIGNALS and use a unique name + */ + error = NULL; + interface_proxy = g_initable_new (interface_proxy_type, + NULL, /* GCancellable */ + &error, + "g-connection", manager->priv->connection, + "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + "g-name", name_owner, + "g-object-path", object_path, + "g-interface-name", interface_name, + NULL); + if (interface_proxy == NULL) + { + g_warning ("%s: Error constructing proxy for path %s and interface %s: %s", + G_STRLOC, + object_path, + interface_name, + error->message); + g_error_free (error); + } + else + { + GVariantIter property_iter; + const gchar *property_name; + GVariant *property_value; + + /* associate the interface proxy with the object */ + g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_proxy), + G_DBUS_OBJECT (op)); + + g_variant_iter_init (&property_iter, properties); + while (g_variant_iter_next (&property_iter, + "{&sv}", + &property_name, + &property_value)) + { + g_dbus_proxy_set_cached_property (interface_proxy, + property_name, + property_value); + g_variant_unref (property_value); + } + + _g_dbus_object_proxy_add_interface (op, interface_proxy); + if (!added) + g_signal_emit_by_name (manager, "interface-added", op, interface_proxy); + g_object_unref (interface_proxy); + } + g_variant_unref (properties); + } + + if (added) + { + g_hash_table_insert (manager->priv->map_object_path_to_object_proxy, + g_strdup (object_path), + op); + g_signal_emit_by_name (manager, "object-added", op); + } +} + +static void +remove_interfaces (GDBusObjectManagerClient *manager, + const gchar *object_path, + const gchar *const *interface_names) +{ + GDBusObjectProxy *op; + GList *interfaces; + guint n; + guint num_interfaces; + guint num_interfaces_to_remove; + + op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); + if (op == NULL) + { + g_warning ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists", + G_STRLOC, + object_path); + goto out; + } + + interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op)); + num_interfaces = g_list_length (interfaces); + g_list_foreach (interfaces, (GFunc) g_object_unref, NULL); + g_list_free (interfaces); + + num_interfaces_to_remove = g_strv_length ((gchar **) interface_names); + + /* see if we are going to completety remove the object */ + if (num_interfaces_to_remove == num_interfaces) + { + g_object_ref (op); + g_warn_if_fail (g_hash_table_remove (manager->priv->map_object_path_to_object_proxy, object_path)); + g_signal_emit_by_name (manager, "object-removed", op); + g_object_unref (op); + } + else + { + for (n = 0; interface_names != NULL && interface_names[n] != NULL; n++) + { + GDBusInterface *interface; + interface = g_dbus_object_get_interface (G_DBUS_OBJECT (op), interface_names[n]); + _g_dbus_object_proxy_remove_interface (op, interface_names[n]); + if (interface != NULL) + { + g_signal_emit_by_name (manager, "interface-removed", op, interface); + g_object_unref (interface); + } + } + } + out: + ; +} + +static void +process_get_all_result (GDBusObjectManagerClient *manager, + GVariant *value, + const gchar *name_owner) +{ + GVariant *arg0; + const gchar *object_path; + GVariant *ifaces_and_properties; + GVariantIter iter; + + g_return_if_fail (g_dbus_is_unique_name (name_owner)); + + arg0 = g_variant_get_child_value (value, 0); + g_variant_iter_init (&iter, arg0); + while (g_variant_iter_next (&iter, + "{&o@a{sa{sv}}}", + &object_path, + &ifaces_and_properties)) + { + add_interfaces (manager, object_path, ifaces_and_properties, name_owner); + g_variant_unref (ifaces_and_properties); + } + g_variant_unref (arg0); +} + +static void +on_control_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data); + const gchar *object_path; + + //g_debug ("yay, g_signal %s: %s\n", signal_name, g_variant_print (parameters, TRUE)); + + if (g_strcmp0 (signal_name, "InterfacesAdded") == 0) + { + GVariant *ifaces_and_properties; + g_variant_get (parameters, + "(&o@a{sa{sv}})", + &object_path, + &ifaces_and_properties); + add_interfaces (manager, object_path, ifaces_and_properties, manager->priv->name_owner); + g_variant_unref (ifaces_and_properties); + } + else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0) + { + const gchar **ifaces; + g_variant_get (parameters, + "(&o^a&s)", + &object_path, + &ifaces); + remove_interfaces (manager, object_path, ifaces); + g_free (ifaces); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static const gchar * +g_dbus_object_manager_client_get_object_path (GDBusObjectManager *_manager) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); + return manager->priv->object_path; +} + +static GDBusObject * +g_dbus_object_manager_client_get_object (GDBusObjectManager *_manager, + const gchar *object_path) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); + GDBusObject *ret; + + ret = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); + if (ret != NULL) + g_object_ref (ret); + return ret; +} + +static GDBusInterface * +g_dbus_object_manager_client_get_interface (GDBusObjectManager *_manager, + const gchar *object_path, + const gchar *interface_name) +{ + GDBusInterface *ret; + GDBusObject *object; + + ret = NULL; + + object = g_dbus_object_manager_get_object (_manager, object_path); + if (object == NULL) + goto out; + + ret = g_dbus_object_get_interface (object, interface_name); + g_object_unref (object); + + out: + return ret; +} + +static GList * +g_dbus_object_manager_client_get_objects (GDBusObjectManager *_manager) +{ + GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); + GList *ret; + + g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); + + ret = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; +} + + +static void +dbus_object_manager_interface_init (GDBusObjectManagerIface *iface) +{ + iface->get_object_path = g_dbus_object_manager_client_get_object_path; + iface->get_objects = g_dbus_object_manager_client_get_objects; + iface->get_object = g_dbus_object_manager_client_get_object; + iface->get_interface = g_dbus_object_manager_client_get_interface; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusobjectmanagerclient.h b/gio/gdbusobjectmanagerclient.h new file mode 100644 index 000000000..386c63dc9 --- /dev/null +++ b/gio/gdbusobjectmanagerclient.h @@ -0,0 +1,129 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_OBJECT_MANAGER_CLIENT_H__ +#define __G_DBUS_OBJECT_MANAGER_CLIENT_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_OBJECT_MANAGER_CLIENT (g_dbus_object_manager_client_get_type ()) +#define G_DBUS_OBJECT_MANAGER_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, GDBusObjectManagerClient)) +#define G_DBUS_OBJECT_MANAGER_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, GDBusObjectManagerClientClass)) +#define G_DBUS_OBJECT_MANAGER_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, GDBusObjectManagerClientClass)) +#define G_IS_DBUS_OBJECT_MANAGER_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT)) +#define G_IS_DBUS_OBJECT_MANAGER_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT)) + +typedef struct _GDBusObjectManagerClientClass GDBusObjectManagerClientClass; +typedef struct _GDBusObjectManagerClientPrivate GDBusObjectManagerClientPrivate; + +/** + * GDBusObjectManagerClient: + * + * The #GDBusObjectManagerClient structure contains private data and should + * only be accessed using the provided API. + */ +struct _GDBusObjectManagerClient +{ + /*< private >*/ + GObject parent_instance; + GDBusObjectManagerClientPrivate *priv; +}; + +/** + * GDBusObjectManagerClientClass: + * @parent_class: The parent class. + * @interface_proxy_signal: Signal class handler for the #GDBusObjectManagerClient::interface-proxy-signal signal. + * @interface_proxy_properties_changed: Signal class handler for the #GDBusObjectManagerClient::interface-proxy-properties-changed signal. + * + * Class structure for #GDBusObjectManagerClient. + */ +struct _GDBusObjectManagerClientClass +{ + GObjectClass parent_class; + + /* signals */ + void (*interface_proxy_signal) (GDBusObjectManagerClient *manager, + GDBusObjectProxy *object_proxy, + GDBusProxy *interface_proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters); + + void (*interface_proxy_properties_changed) (GDBusObjectManagerClient *manager, + GDBusObjectProxy *object_proxy, + GDBusProxy *interface_proxy, + GVariant *changed_properties, + const gchar* const *invalidated_properties); + + /*< private >*/ + gpointer padding[8]; +}; + +GType g_dbus_object_manager_client_get_type (void) G_GNUC_CONST; +void g_dbus_object_manager_client_new (GDBusConnection *connection, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusObjectManager *g_dbus_object_manager_client_new_finish (GAsyncResult *res, + GError **error); +GDBusObjectManager *g_dbus_object_manager_client_new_sync (GDBusConnection *connection, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GError **error); +void g_dbus_object_manager_client_new_for_bus (GBusType bus_type, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusObjectManager *g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult *res, + GError **error); +GDBusObjectManager *g_dbus_object_manager_client_new_for_bus_sync (GBusType bus_type, + GDBusObjectManagerClientFlags flags, + const gchar *name, + const gchar *object_path, + GDBusProxyTypeFunc get_proxy_type_func, + gpointer get_proxy_type_user_data, + GCancellable *cancellable, + GError **error); +GDBusConnection *g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager); +GDBusObjectManagerClientFlags g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager); +const gchar *g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager); +gchar *g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager); + +G_END_DECLS + +#endif /* __G_DBUS_OBJECT_MANAGER_CLIENT_H */ diff --git a/gio/gdbusobjectmanagerserver.c b/gio/gdbusobjectmanagerserver.c new file mode 100644 index 000000000..ed7c57514 --- /dev/null +++ b/gio/gdbusobjectmanagerserver.c @@ -0,0 +1,898 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobjectmanager.h" +#include "gdbusobjectmanagerserver.h" +#include "gdbusobject.h" +#include "gdbusobjectstub.h" +#include "gdbusinterfacestub.h" +#include "gdbusconnection.h" +#include "gdbusintrospection.h" +#include "gdbusmethodinvocation.h" +#include "gdbuserror.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusobjectmanagerserver + * @short_description: Service-side object manager + * @include: gio/gio.h + * + * #GDBusObjectManagerServer is used to export #GDBusObject instances using + * the standardized org.freedesktop.DBus.ObjectManager + * interface. For example, remote D-Bus clients can get all objects + * and properties in a single call. Additionally, any change in the + * object hierarchy is broadcast using signals. This means that D-Bus + * clients can keep caches up to date by only listening to D-Bus + * signals. + * + * See #GDBusObjectManagerClient for the client-side code that is intended to + * be used with #GDBusObjectManagerServer. + */ + +typedef struct +{ + GDBusObjectStub *object; + GDBusObjectManagerServer *manager; + GHashTable *map_iface_name_to_iface; + gboolean exported; +} RegistrationData; + +static void registration_data_free (RegistrationData *data); + +static void g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager, + RegistrationData *data, + const gchar *const *interfaces); + +static void g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager, + RegistrationData *data, + const gchar *const *interfaces); + +struct _GDBusObjectManagerServerPrivate +{ + GDBusConnection *connection; + gchar *object_path; + gchar *object_path_ending_in_slash; + GHashTable *map_object_path_to_data; + guint manager_reg_id; +}; + +enum +{ + PROP_0, + PROP_CONNECTION, + PROP_OBJECT_PATH +}; + +static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerServer, g_dbus_object_manager_server, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init)); + +static void g_dbus_object_manager_server_constructed (GObject *object); + +static void +g_dbus_object_manager_server_finalize (GObject *object) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object); + + g_hash_table_unref (manager->priv->map_object_path_to_data); + if (manager->priv->manager_reg_id > 0) + g_warn_if_fail (g_dbus_connection_unregister_object (manager->priv->connection, manager->priv->manager_reg_id)); + g_object_unref (manager->priv->connection); + g_free (manager->priv->object_path); + g_free (manager->priv->object_path_ending_in_slash); + + if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize (object); +} + +static void +g_dbus_object_manager_server_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_object); + + switch (prop_id) + { + case PROP_CONNECTION: + g_value_set_object (value, g_dbus_object_manager_server_get_connection (manager)); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_manager_server_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_object); + + switch (prop_id) + { + case PROP_CONNECTION: + g_assert (manager->priv->connection == NULL); + g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value))); + manager->priv->connection = g_value_dup_object (value); + break; + + case PROP_OBJECT_PATH: + g_assert (manager->priv->object_path == NULL); + g_assert (g_variant_is_object_path (g_value_get_string (value))); + manager->priv->object_path = g_value_dup_string (value); + manager->priv->object_path_ending_in_slash = g_strdup_printf ("%s/", manager->priv->object_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_manager_server_class_init (GDBusObjectManagerServerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_object_manager_server_finalize; + gobject_class->constructed = g_dbus_object_manager_server_constructed; + gobject_class->set_property = g_dbus_object_manager_server_set_property; + gobject_class->get_property = g_dbus_object_manager_server_get_property; + + /** + * GDBusObjectManagerServer:connection: + * + * The #GDBusConnection to export objects on. + */ + g_object_class_install_property (gobject_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "The connection to export objects on", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectManagerServer:object-path: + * + * The object path to register the manager object at. + */ + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path to register the manager object at", + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (klass, sizeof (GDBusObjectManagerServerPrivate)); +} + +static void +g_dbus_object_manager_server_init (GDBusObjectManagerServer *manager) +{ + manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, + G_TYPE_DBUS_OBJECT_MANAGER_SERVER, + GDBusObjectManagerServerPrivate); + manager->priv->map_object_path_to_data = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) registration_data_free); +} + +/** + * g_dbus_object_manager_server_new: + * @connection: A #GDBusConnection. + * @object_path: The object path to export the manager object at. + * + * Creates a new #GDBusObjectManagerServer object. + * + * TODO: make it so that the objects are not exported yet - + * e.g. start()/stop() semantics. + * + * Returns: A #GDBusObjectManagerServer object. Free with g_object_unref(). + */ +GDBusObjectManagerServer * +g_dbus_object_manager_server_new (GDBusConnection *connection, + const gchar *object_path) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + return G_DBUS_OBJECT_MANAGER_SERVER (g_object_new (G_TYPE_DBUS_OBJECT_MANAGER_SERVER, + "connection", connection, + "object-path", object_path, + NULL)); +} + +/** + * g_dbus_object_manager_server_get_connection: + * @manager: A #GDBusObjectManagerServer + * + * Gets the #GDBusConnection used by @manager. + * + * Returns: A #GDBusConnection object. Do not free, the object belongs + * to @manager. + */ +GDBusConnection * +g_dbus_object_manager_server_get_connection (GDBusObjectManagerServer *manager) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), NULL); + return manager->priv->connection; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +registration_data_export_interface (RegistrationData *data, + GDBusInterfaceStub *interface_stub) +{ + GDBusInterfaceInfo *info; + GError *error; + const gchar *object_path; + + object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); + + info = g_dbus_interface_stub_get_info (interface_stub); + error = NULL; + if (!g_dbus_interface_stub_export (interface_stub, + data->manager->priv->connection, + object_path, + &error)) + { + /* TODO: probably wrong to complain on stderr */ + g_warning ("%s: Error registering object at %s with interface %s: %s", + G_STRLOC, + object_path, + info->name, + error->message); + g_error_free (error); + goto out; + } + + g_assert (g_hash_table_lookup (data->map_iface_name_to_iface, info->name) == NULL); + g_hash_table_insert (data->map_iface_name_to_iface, + info->name, + g_object_ref (interface_stub)); + + /* if we are already exported, then... */ + if (data->exported) + { + const gchar *interfaces[2]; + /* emit InterfacesAdded on the ObjectManager object */ + interfaces[0] = info->name; + interfaces[1] = NULL; + g_dbus_object_manager_server_emit_interfaces_added (data->manager, data, interfaces); + } + + out: + ; +} + +static void +registration_data_unexport_interface (RegistrationData *data, + GDBusInterfaceStub *interface_stub) +{ + GDBusInterfaceInfo *info; + GDBusInterfaceStub *iface; + + info = g_dbus_interface_stub_get_info (interface_stub); + iface = g_hash_table_lookup (data->map_iface_name_to_iface, info->name); + g_assert (iface != NULL); + + g_dbus_interface_stub_unexport (iface); + + g_warn_if_fail (g_hash_table_remove (data->map_iface_name_to_iface, info->name)); + + /* if we are already exported, then... */ + if (data->exported) + { + const gchar *interfaces[2]; + /* emit InterfacesRemoved on the ObjectManager object */ + interfaces[0] = info->name; + interfaces[1] = NULL; + g_dbus_object_manager_server_emit_interfaces_removed (data->manager, data, interfaces); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_interface_added (GDBusObject *object, + GDBusInterface *interface, + gpointer user_data) +{ + RegistrationData *data = user_data; + registration_data_export_interface (data, G_DBUS_INTERFACE_STUB (interface)); +} + +static void +on_interface_removed (GDBusObject *object, + GDBusInterface *interface, + gpointer user_data) +{ + RegistrationData *data = user_data; + registration_data_unexport_interface (data, G_DBUS_INTERFACE_STUB (interface)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + + +static void +registration_data_free (RegistrationData *data) +{ + GHashTableIter iter; + GDBusInterfaceStub *iface; + + data->exported = FALSE; + + g_hash_table_iter_init (&iter, data->map_iface_name_to_iface); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface)) + g_dbus_interface_stub_unexport (iface); + + g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_added), data); + g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_removed), data); + g_object_unref (data->object); + g_hash_table_destroy (data->map_iface_name_to_iface); + g_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_object_manager_server_export: + * @manager: A #GDBusObjectManagerServer. + * @object: A #GDBusObjectStub. + * + * Exports @object on @manager. + * + * If there is already a #GDBusObject exported at the object path, + * then the old object is removed. + * + * The object path for @object must be in the hierarchy rooted by the + * object path for @manager. + * + * Note that @manager will take a reference on @object for as long as + * it is exported. + */ +void +g_dbus_object_manager_server_export (GDBusObjectManagerServer *manager, + GDBusObjectStub *object) +{ + RegistrationData *data; + GList *existing_interfaces; + GList *l; + GPtrArray *interface_names; + const gchar *object_path; + + object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); + + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); + g_return_if_fail (G_IS_DBUS_OBJECT (object)); + g_return_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash)); + + interface_names = g_ptr_array_new (); + + data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); + if (data != NULL) + g_dbus_object_manager_server_unexport (manager, object_path); + + data = g_new0 (RegistrationData, 1); + data->object = g_object_ref (object); + data->manager = manager; + data->map_iface_name_to_iface = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) g_object_unref); + + g_signal_connect (object, + "interface-added", + G_CALLBACK (on_interface_added), + data); + g_signal_connect (object, + "interface-removed", + G_CALLBACK (on_interface_removed), + data); + + /* Register all known interfaces - note that data->exported is FALSE so + * we don't emit any InterfacesAdded signals. + */ + existing_interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object)); + for (l = existing_interfaces; l != NULL; l = l->next) + { + GDBusInterfaceStub *interface_stub = G_DBUS_INTERFACE_STUB (l->data); + registration_data_export_interface (data, interface_stub); + g_ptr_array_add (interface_names, g_dbus_interface_stub_get_info (interface_stub)->name); + } + g_list_foreach (existing_interfaces, (GFunc) g_object_unref, NULL); + g_list_free (existing_interfaces); + g_ptr_array_add (interface_names, NULL); + + data->exported = TRUE; + + /* now emit InterfacesAdded() for all the interfaces */ + g_dbus_object_manager_server_emit_interfaces_added (manager, data, (const gchar *const *) interface_names->pdata); + g_ptr_array_unref (interface_names); + + g_hash_table_insert (manager->priv->map_object_path_to_data, + g_strdup (object_path), + data); +} + +/** + * g_dbus_object_manager_server_export_and_uniquify: + * @manager: A #GDBusObjectManagerServer. + * @object: An object. + * + * Like g_dbus_object_manager_server_export() but appends a string of + * the form _N (with N being a natural number) to + * @object's object path if an object with the given path + * already exists. As such, the #GDBusObjectProxy:object-path property + * of @object may be modified. + */ +void +g_dbus_object_manager_server_export_and_uniquify (GDBusObjectManagerServer *manager, + GDBusObjectStub *object) +{ + gchar *orig_object_path; + gchar *object_path; + guint count; + gboolean modified; + + orig_object_path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); + + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); + g_return_if_fail (G_IS_DBUS_OBJECT (object)); + g_return_if_fail (g_str_has_prefix (orig_object_path, manager->priv->object_path_ending_in_slash)); + + object_path = g_strdup (orig_object_path); + count = 1; + modified = FALSE; + while (TRUE) + { + RegistrationData *data; + data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); + if (data == NULL) + { + break; + } + g_free (object_path); + object_path = g_strdup_printf ("%s_%d", orig_object_path, count++); + modified = TRUE; + } + + if (modified) + g_dbus_object_stub_set_object_path (G_DBUS_OBJECT_STUB (object), object_path); + + g_dbus_object_manager_server_export (manager, object); + + g_free (object_path); + g_free (orig_object_path); +} + +/** + * g_dbus_object_manager_server_unexport: + * @manager: A #GDBusObjectManagerServer. + * @object_path: An object path. + * + * If @manager has an object at @path, removes the object. Otherwise + * does nothing. + * + * Note that @object_path must be in the hierarchy rooted by the + * object path for @manager. + */ +void +g_dbus_object_manager_server_unexport (GDBusObjectManagerServer *manager, + const gchar *object_path) +{ + RegistrationData *data; + + g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); + g_return_if_fail (g_variant_is_object_path (object_path)); + g_return_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash)); + + data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); + if (data != NULL) + { + GPtrArray *interface_names; + GHashTableIter iter; + const gchar *iface_name; + + interface_names = g_ptr_array_new (); + g_hash_table_iter_init (&iter, data->map_iface_name_to_iface); + while (g_hash_table_iter_next (&iter, (gpointer) &iface_name, NULL)) + g_ptr_array_add (interface_names, (gpointer) iface_name); + g_ptr_array_add (interface_names, NULL); + /* now emit InterfacesRemoved() for all the interfaces */ + g_dbus_object_manager_server_emit_interfaces_removed (manager, data, (const gchar *const *) interface_names->pdata); + g_ptr_array_unref (interface_names); + + g_hash_table_remove (manager->priv->map_object_path_to_data, object_path); + } +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +static const GDBusArgInfo manager_interfaces_added_signal_info_arg0 = +{ + -1, + "object_path", + "o", + (GDBusAnnotationInfo**) NULL, +}; + +static const GDBusArgInfo manager_interfaces_added_signal_info_arg1 = +{ + -1, + "interfaces_and_properties", + "a{sa{sv}}", + (GDBusAnnotationInfo**) NULL, +}; + +static const GDBusArgInfo * const manager_interfaces_added_signal_info_arg_pointers[] = +{ + &manager_interfaces_added_signal_info_arg0, + &manager_interfaces_added_signal_info_arg1, + NULL +}; + +static const GDBusSignalInfo manager_interfaces_added_signal_info = +{ + -1, + "InterfacesAdded", + (GDBusArgInfo**) &manager_interfaces_added_signal_info_arg_pointers, + (GDBusAnnotationInfo**) NULL +}; + +/* ---------- */ + +static const GDBusArgInfo manager_interfaces_removed_signal_info_arg0 = +{ + -1, + "object_path", + "o", + (GDBusAnnotationInfo**) NULL, +}; + +static const GDBusArgInfo manager_interfaces_removed_signal_info_arg1 = +{ + -1, + "interfaces", + "as", + (GDBusAnnotationInfo**) NULL, +}; + +static const GDBusArgInfo * const manager_interfaces_removed_signal_info_arg_pointers[] = +{ + &manager_interfaces_removed_signal_info_arg0, + &manager_interfaces_removed_signal_info_arg1, + NULL +}; + +static const GDBusSignalInfo manager_interfaces_removed_signal_info = +{ + -1, + "InterfacesRemoved", + (GDBusArgInfo**) &manager_interfaces_removed_signal_info_arg_pointers, + (GDBusAnnotationInfo**) NULL +}; + +/* ---------- */ + +static const GDBusSignalInfo * const manager_signal_info_pointers[] = +{ + &manager_interfaces_added_signal_info, + &manager_interfaces_removed_signal_info, + NULL +}; + +/* ---------- */ + +static const GDBusArgInfo manager_get_all_method_info_out_arg0 = +{ + -1, + "object_paths_interfaces_and_properties", + "a{oa{sa{sv}}}", + (GDBusAnnotationInfo**) NULL, +}; + +static const GDBusArgInfo * const manager_get_all_method_info_out_arg_pointers[] = +{ + &manager_get_all_method_info_out_arg0, + NULL +}; + +static const GDBusMethodInfo manager_get_all_method_info = +{ + -1, + "GetManagedObjects", + (GDBusArgInfo**) NULL, + (GDBusArgInfo**) &manager_get_all_method_info_out_arg_pointers, + (GDBusAnnotationInfo**) NULL +}; + +static const GDBusMethodInfo * const manager_method_info_pointers[] = +{ + &manager_get_all_method_info, + NULL +}; + +/* ---------- */ + +static const GDBusInterfaceInfo manager_interface_info = +{ + -1, + "org.freedesktop.DBus.ObjectManager", + (GDBusMethodInfo **) manager_method_info_pointers, + (GDBusSignalInfo **) manager_signal_info_pointers, + (GDBusPropertyInfo **) NULL, + (GDBusAnnotationInfo **) NULL +}; + +static void +manager_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (user_data); + GVariantBuilder array_builder; + GHashTableIter object_iter; + RegistrationData *data; + + if (g_strcmp0 (method_name, "GetManagedObjects") == 0) + { + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{oa{sa{sv}}}")); + g_hash_table_iter_init (&object_iter, manager->priv->map_object_path_to_data); + while (g_hash_table_iter_next (&object_iter, NULL, (gpointer) &data)) + { + GVariantBuilder interfaces_builder; + GHashTableIter interface_iter; + GDBusInterfaceStub *iface; + const gchar *iter_object_path; + + g_variant_builder_init (&interfaces_builder, G_VARIANT_TYPE ("a{sa{sv}}")); + g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface); + while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface)) + { + g_variant_builder_add_value (&interfaces_builder, + g_variant_new ("{s@a{sv}}", + g_dbus_interface_stub_get_info (iface)->name, + g_dbus_interface_stub_get_properties (iface))); + } + iter_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); + g_variant_builder_add (&array_builder, + "{oa{sa{sv}}}", + iter_object_path, + &interfaces_builder); + } + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(a{oa{sa{sv}}})", + &array_builder)); + } + else + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s - only GetManagedObjects() is supported", + method_name); + } +} + +static const GDBusInterfaceVTable manager_interface_vtable = +{ + manager_method_call, /* handle_method_call */ + NULL, /* get_property */ + NULL /* set_property */ +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_dbus_object_manager_server_constructed (GObject *object) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object); + GError *error; + + error = NULL; + manager->priv->manager_reg_id = g_dbus_connection_register_object (manager->priv->connection, + manager->priv->object_path, + (GDBusInterfaceInfo *) &manager_interface_info, + &manager_interface_vtable, + manager, + NULL, /* user_data_free_func */ + &error); + if (manager->priv->manager_reg_id == 0) + { + /* TODO: probably wrong to complain on stderr */ + g_warning ("%s: Error registering manager at %s: %s", + G_STRLOC, + manager->priv->object_path, + error->message); + g_error_free (error); + } + + if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed != NULL) + G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed (object); +} + +static void +g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager, + RegistrationData *data, + const gchar *const *interfaces) +{ + GVariantBuilder array_builder; + GError *error; + guint n; + const gchar *object_path; + + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{sa{sv}}")); + for (n = 0; interfaces[n] != NULL; n++) + { + GDBusInterfaceStub *iface; + iface = g_hash_table_lookup (data->map_iface_name_to_iface, interfaces[n]); + g_assert (iface != NULL); + g_variant_builder_add_value (&array_builder, + g_variant_new ("{s@a{sv}}", + interfaces[n], + g_dbus_interface_stub_get_properties (iface))); + } + + error = NULL; + object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); + g_dbus_connection_emit_signal (data->manager->priv->connection, + NULL, /* destination_bus_name */ + manager->priv->object_path, + manager_interface_info.name, + "InterfacesAdded", + g_variant_new ("(oa{sa{sv}})", + object_path, + &array_builder), + &error); + g_assert_no_error (error); +} + +static void +g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager, + RegistrationData *data, + const gchar *const *interfaces) +{ + GVariantBuilder array_builder; + GError *error; + guint n; + const gchar *object_path; + + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as")); + for (n = 0; interfaces[n] != NULL; n++) + g_variant_builder_add (&array_builder, "s", interfaces[n]); + + error = NULL; + object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); + g_dbus_connection_emit_signal (data->manager->priv->connection, + NULL, /* destination_bus_name */ + manager->priv->object_path, + manager_interface_info.name, + "InterfacesRemoved", + g_variant_new ("(oas)", + object_path, + &array_builder), + &error); + g_assert_no_error (error); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GList * +g_dbus_object_manager_server_get_objects (GDBusObjectManager *_manager) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager); + GList *ret; + GHashTableIter iter; + RegistrationData *data; + + ret = NULL; + g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &data)) + { + ret = g_list_prepend (ret, g_object_ref (data->object)); + } + + return ret; +} + +static const gchar * +g_dbus_object_manager_server_get_object_path (GDBusObjectManager *_manager) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager); + return manager->priv->object_path; +} + +static GDBusObject * +g_dbus_object_manager_server_get_object (GDBusObjectManager *_manager, + const gchar *object_path) +{ + GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager); + GDBusObject *ret; + RegistrationData *data; + + ret = NULL; + data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); + if (data != NULL) + ret = g_object_ref (data->object); + return ret; +} + +static GDBusInterface * +g_dbus_object_manager_server_get_interface (GDBusObjectManager *_manager, + const gchar *object_path, + const gchar *interface_name) +{ + GDBusInterface *ret; + GDBusObject *object; + + ret = NULL; + + object = g_dbus_object_manager_get_object (_manager, object_path); + if (object == NULL) + goto out; + + ret = g_dbus_object_get_interface (object, interface_name); + g_object_unref (object); + + out: + return ret; +} + +static void +dbus_object_manager_interface_init (GDBusObjectManagerIface *iface) +{ + iface->get_object_path = g_dbus_object_manager_server_get_object_path; + iface->get_objects = g_dbus_object_manager_server_get_objects; + iface->get_object = g_dbus_object_manager_server_get_object; + iface->get_interface = g_dbus_object_manager_server_get_interface; +} diff --git a/gio/gdbusobjectmanagerserver.h b/gio/gdbusobjectmanagerserver.h new file mode 100644 index 000000000..baf964ee3 --- /dev/null +++ b/gio/gdbusobjectmanagerserver.h @@ -0,0 +1,80 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_OBJECT_MANAGER_SERVER_H__ +#define __G_DBUS_OBJECT_MANAGER_SERVER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_OBJECT_MANAGER_SERVER (g_dbus_object_manager_server_get_type ()) +#define G_DBUS_OBJECT_MANAGER_SERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_MANAGER_SERVER, GDBusObjectManagerServer)) +#define G_DBUS_OBJECT_MANAGER_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_MANAGER_SERVER, GDBusObjectManagerServerClass)) +#define G_DBUS_OBJECT_MANAGER_SERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_MANAGER_SERVER, GDBusObjectManagerServerClass)) +#define G_IS_DBUS_OBJECT_MANAGER_SERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_MANAGER_SERVER)) +#define G_IS_DBUS_OBJECT_MANAGER_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_MANAGER_SERVER)) + +typedef struct _GDBusObjectManagerServerClass GDBusObjectManagerServerClass; +typedef struct _GDBusObjectManagerServerPrivate GDBusObjectManagerServerPrivate; + +/** + * GDBusObjectManagerServer: + * + * The #GDBusObjectManagerServer structure contains private data and should + * only be accessed using the provided API. + */ +struct _GDBusObjectManagerServer +{ + /*< private >*/ + GObject parent_instance; + GDBusObjectManagerServerPrivate *priv; +}; + +/** + * GDBusObjectManagerServerClass: + * @parent_class: The parent class. + * + * Class structure for #GDBusObjectManagerServer. + */ +struct _GDBusObjectManagerServerClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer padding[8]; +}; + +GType g_dbus_object_manager_server_get_type (void) G_GNUC_CONST; +GDBusObjectManagerServer *g_dbus_object_manager_server_new (GDBusConnection *connection, + const gchar *object_path); +GDBusConnection *g_dbus_object_manager_server_get_connection (GDBusObjectManagerServer *manager); +void g_dbus_object_manager_server_export (GDBusObjectManagerServer *manager, + GDBusObjectStub *object); +void g_dbus_object_manager_server_export_and_uniquify (GDBusObjectManagerServer *manager, + GDBusObjectStub *object); +void g_dbus_object_manager_server_unexport (GDBusObjectManagerServer *manager, + const gchar *object_path); + +G_END_DECLS + +#endif /* __G_DBUS_OBJECT_MANAGER_SERVER_H */ diff --git a/gio/gdbusobjectproxy.c b/gio/gdbusobjectproxy.c new file mode 100644 index 000000000..3c26495db --- /dev/null +++ b/gio/gdbusobjectproxy.c @@ -0,0 +1,315 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobject.h" +#include "gdbusobjectproxy.h" +#include "gdbusconnection.h" +#include "gdbusprivate.h" +#include "gdbusutils.h" +#include "gdbusproxy.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusobjectproxy + * @short_description: Client-side D-Bus object + * @include: gio/gio.h + * + * A #GDBusObjectProxy is an object used to represent a remote object + * with one or more D-Bus interfaces. You cannot instantiate a + * #GDBusObjectProxy yourself - you need to use a + * #GDBusObjectManagerClient to get one. + */ + +struct _GDBusObjectProxyPrivate +{ + GHashTable *map_name_to_iface; + gchar *object_path; + GDBusConnection *connection; +}; + +enum +{ + PROP_0, + PROP_OBJECT_PATH, + PROP_CONNECTION +}; + +static void dbus_object_interface_init (GDBusObjectIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GDBusObjectProxy, g_dbus_object_proxy, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT, dbus_object_interface_init)); + +static void +g_dbus_object_proxy_finalize (GObject *object) +{ + GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object); + + g_hash_table_unref (proxy->priv->map_name_to_iface); + + if (G_OBJECT_CLASS (g_dbus_object_proxy_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_object_proxy_parent_class)->finalize (object); +} + +static void +g_dbus_object_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + g_value_set_string (value, proxy->priv->object_path); + break; + + case PROP_CONNECTION: + g_value_set_object (value, g_dbus_object_proxy_get_connection (proxy)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_proxy_class_init (GDBusObjectProxyClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_object_proxy_finalize; + gobject_class->set_property = g_dbus_object_proxy_set_property; + gobject_class->get_property = g_dbus_object_proxy_get_property; + + /** + * GDBusObjectProxy:object-path: + * + * The object path of the proxy. + */ + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path of the proxy", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectProxy:connection: + * + * The connection of the proxy. + */ + g_object_class_install_property (gobject_class, + PROP_CONNECTION, + g_param_spec_string ("connection", + "Connection", + "The connection of the proxy", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (klass, sizeof (GDBusObjectProxyPrivate)); +} + +static void +g_dbus_object_proxy_init (GDBusObjectProxy *proxy) +{ + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, + G_TYPE_DBUS_OBJECT_PROXY, + GDBusObjectProxyPrivate); + proxy->priv->map_name_to_iface = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); +} + +static const gchar * +g_dbus_object_proxy_get_object_path (GDBusObject *object) +{ + GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object); + return proxy->priv->object_path; +} + +/** + * g_dbus_object_proxy_get_connection: + * @proxy: A #GDBusObjectProxy. + * + * Gets the connection that @proxy is for. + * + * Returns: A #GDBusConnection. Do not free, the object is owned by @proxy. + */ +GDBusConnection * +g_dbus_object_proxy_get_connection (GDBusObjectProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL); + return proxy->priv->connection; +} + +static GDBusInterface * +g_dbus_object_proxy_get_interface (GDBusObject *object, + const gchar *interface_name) +{ + GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object); + GDBusProxy *ret; + + g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + + ret = g_hash_table_lookup (proxy->priv->map_name_to_iface, interface_name); + if (ret != NULL) + g_object_ref (ret); + return (GDBusInterface *) ret; /* TODO: proper cast */ +} + +static GList * +g_dbus_object_proxy_get_interfaces (GDBusObject *object) +{ + GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object); + GList *ret; + GHashTableIter iter; + GDBusProxy *interface_proxy; + + g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL); + + ret = NULL; + + g_hash_table_iter_init (&iter, proxy->priv->map_name_to_iface); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &interface_proxy)) + ret = g_list_prepend (ret, g_object_ref (interface_proxy)); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +GDBusObjectProxy * +_g_dbus_object_proxy_new (GDBusConnection *connection, + const gchar *object_path) +{ + GDBusObjectProxy *proxy; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + + proxy = G_DBUS_OBJECT_PROXY (g_object_new (G_TYPE_DBUS_OBJECT_PROXY, NULL)); + proxy->priv->object_path = g_strdup (object_path); + proxy->priv->connection = g_object_ref (connection); + return proxy; +} + +void +_g_dbus_object_proxy_add_interface (GDBusObjectProxy *proxy, + GDBusProxy *interface_proxy) +{ + const gchar *interface_name; + + g_return_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy)); + g_return_if_fail (G_IS_DBUS_PROXY (interface_proxy)); + + interface_name = g_dbus_proxy_get_interface_name (interface_proxy); + _g_dbus_object_proxy_remove_interface (proxy, interface_name); + g_hash_table_insert (proxy->priv->map_name_to_iface, + g_strdup (interface_name), + g_object_ref (interface_proxy)); + g_signal_emit_by_name (proxy, "interface-added", interface_proxy); +} + +void +_g_dbus_object_proxy_remove_interface (GDBusObjectProxy *proxy, + const gchar *interface_name) +{ + GDBusProxy *interface_proxy; + + g_return_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy)); + g_return_if_fail (g_dbus_is_interface_name (interface_name)); + + interface_proxy = g_hash_table_lookup (proxy->priv->map_name_to_iface, interface_name); + if (interface_proxy != NULL) + { + g_object_ref (interface_proxy); + g_warn_if_fail (g_hash_table_remove (proxy->priv->map_name_to_iface, interface_name)); + g_signal_emit_by_name (proxy, "interface-removed", interface_proxy); + g_object_unref (interface_proxy); + } +} + +static gpointer +g_dbus_object_proxy_lookup_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type) +{ + GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object); + GDBusProxy *ret; + + g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + + ret = g_hash_table_lookup (proxy->priv->map_name_to_iface, interface_name); + if (ret != NULL) + { + g_warn_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (ret, type)); + g_object_ref (ret); + } + return ret; +} + +static gpointer +g_dbus_object_proxy_peek_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type) +{ + GDBusProxy *ret; + ret = g_dbus_object_proxy_lookup_with_typecheck (object, interface_name, type); + if (ret != NULL) + g_object_unref (ret); + return ret; +} + +static void +dbus_object_interface_init (GDBusObjectIface *iface) +{ + iface->get_object_path = g_dbus_object_proxy_get_object_path; + iface->get_interfaces = g_dbus_object_proxy_get_interfaces; + iface->get_interface = g_dbus_object_proxy_get_interface; + iface->peek_with_typecheck = g_dbus_object_proxy_peek_with_typecheck; + iface->lookup_with_typecheck = g_dbus_object_proxy_lookup_with_typecheck; +} diff --git a/gio/gdbusobjectproxy.h b/gio/gdbusobjectproxy.h new file mode 100644 index 000000000..d5cd619c5 --- /dev/null +++ b/gio/gdbusobjectproxy.h @@ -0,0 +1,72 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_OBJECT_PROXY_H__ +#define __G_DBUS_OBJECT_PROXY_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_OBJECT_PROXY (g_dbus_object_proxy_get_type ()) +#define G_DBUS_OBJECT_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_PROXY, GDBusObjectProxy)) +#define G_DBUS_OBJECT_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_PROXY, GDBusObjectProxyClass)) +#define G_DBUS_OBJECT_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_PROXY, GDBusObjectProxyClass)) +#define G_IS_DBUS_OBJECT_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_PROXY)) +#define G_IS_DBUS_OBJECT_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_PROXY)) + +typedef struct _GDBusObjectProxyClass GDBusObjectProxyClass; +typedef struct _GDBusObjectProxyPrivate GDBusObjectProxyPrivate; + +/** + * GDBusObjectProxy: + * + * The #GDBusObjectProxy structure contains private data and should + * only be accessed using the provided API. + */ +struct _GDBusObjectProxy +{ + /*< private >*/ + GObject parent_instance; + GDBusObjectProxyPrivate *priv; +}; + +/** + * GDBusObjectProxyClass: + * @parent_class: The parent class. + * + * Class structure for #GDBusObjectProxy. + */ +struct _GDBusObjectProxyClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer padding[8]; +}; + +GType g_dbus_object_proxy_get_type (void) G_GNUC_CONST; +GDBusConnection *g_dbus_object_proxy_get_connection (GDBusObjectProxy *proxy); + +G_END_DECLS + +#endif /* __G_DBUS_OBJECT_PROXY_H */ diff --git a/gio/gdbusobjectstub.c b/gio/gdbusobjectstub.c new file mode 100644 index 000000000..7b60db533 --- /dev/null +++ b/gio/gdbusobjectstub.c @@ -0,0 +1,475 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusobject.h" +#include "gdbusobjectstub.h" +#include "gdbusinterfacestub.h" +#include "gio-marshal.h" +#include "gdbusprivate.h" +#include "gdbusmethodinvocation.h" +#include "gdbusintrospection.h" +#include "gdbusinterface.h" +#include "gdbusutils.h" + +#include "glibintl.h" + +/** + * SECTION:gdbusobjectstub + * @short_description: Service-side D-Bus object + * @include: gio/gio.h + * + * A #GDBusObjectStub instance is essentially a group of D-Bus + * interfaces. The set of exported interfaces on the object may be + * dynamic and change at runtime. + * + * This type is intended to be used with #GDBusObjectManager. + */ + +struct _GDBusObjectStubPrivate +{ + gchar *object_path; + GHashTable *map_name_to_iface; +}; + +enum +{ + PROP_0, + PROP_OBJECT_PATH +}; + +enum +{ + AUTHORIZE_METHOD_SIGNAL, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +static void dbus_object_interface_init (GDBusObjectIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GDBusObjectStub, g_dbus_object_stub, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT, dbus_object_interface_init)); + + +static void +g_dbus_object_stub_finalize (GObject *_object) +{ + GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object); + + g_free (object->priv->object_path); + g_hash_table_unref (object->priv->map_name_to_iface); + + if (G_OBJECT_CLASS (g_dbus_object_stub_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_object_stub_parent_class)->finalize (_object); +} + +static void +g_dbus_object_stub_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + g_value_take_string (value, object->priv->object_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static void +g_dbus_object_stub_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + g_dbus_object_stub_set_object_path (object, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec); + break; + } +} + +static gboolean +g_dbus_object_stub_authorize_method_default (GDBusObjectStub *object, + GDBusInterfaceStub *interface, + GDBusMethodInvocation *invocation) +{ + return TRUE; +} + +static void +g_dbus_object_stub_class_init (GDBusObjectStubClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_object_stub_finalize; + gobject_class->set_property = g_dbus_object_stub_set_property; + gobject_class->get_property = g_dbus_object_stub_get_property; + + klass->authorize_method = g_dbus_object_stub_authorize_method_default; + + /** + * GDBusObjectStub:object-path: + * + * The object path where the object is exported. + */ + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path where the object is exported", + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * GDBusObjectStub::authorize-method: + * @object: The #GDBusObjectStub emitting the signal. + * @interface: The #GDBusInterfaceStub that @invocation is on. + * @invocation: A #GDBusMethodInvocation. + * + * Emitted when a method is invoked by a remote caller and used to + * determine if the method call is authorized. + * + * This signal is like #GDBusInterfaceStub's + * #GDBusInterfaceStub::g-authorize-method signal, except that it is + * for the enclosing object. + * + * The default class handler just returns %TRUE. + * + * Returns: %TRUE if the call is authorized, %FALSE otherwise. + */ + signals[AUTHORIZE_METHOD_SIGNAL] = + g_signal_new ("authorize-method", + G_TYPE_DBUS_OBJECT_STUB, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusObjectStubClass, authorize_method), + _g_signal_accumulator_false_handled, + NULL, + _gio_marshal_BOOLEAN__OBJECT_OBJECT, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_INTERFACE_STUB, + G_TYPE_DBUS_METHOD_INVOCATION); + + g_type_class_add_private (klass, sizeof (GDBusObjectStubPrivate)); +} + +static void +g_dbus_object_stub_init (GDBusObjectStub *object) +{ + object->priv = G_TYPE_INSTANCE_GET_PRIVATE (object, G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStubPrivate); + object->priv->map_name_to_iface = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); +} + +/** + * g_dbus_object_stub_new: + * @object_path: An object path. + * + * Creates a new #GDBusObjectStub. + * + * Returns: A #GDBusObjectStub. Free with g_object_unref(). + */ +GDBusObjectStub * +g_dbus_object_stub_new (const gchar *object_path) +{ + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + return G_DBUS_OBJECT_STUB (g_object_new (G_TYPE_DBUS_OBJECT_STUB, + "object-path", object_path, + NULL)); +} + +/** + * g_dbus_object_stub_set_object_path: + * @object: A #GDBusObjectStub. + * @object_path: A valid D-Bus object path. + * + * Sets the object path for @object. + */ +void +g_dbus_object_stub_set_object_path (GDBusObjectStub *object, + const gchar *object_path) +{ + g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object)); + g_return_if_fail (object_path == NULL || g_variant_is_object_path (object_path)); + /* TODO: fail if object is currently exported */ + if (g_strcmp0 (object->priv->object_path, object_path) != 0) + { + g_free (object->priv->object_path); + object->priv->object_path = g_strdup (object_path); + g_object_notify (G_OBJECT (object), "object-path"); + } +} + +static const gchar * +g_dbus_object_stub_get_object_path (GDBusObject *_object) +{ + GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object); + return object->priv->object_path; +} + +/** + * g_dbus_object_stub_add_interface: + * @object: A #GDBusObjectStub. + * @interface: A #GDBusInterfaceStub. + * + * Adds @interface to @object. + * + * If @object already contains a #GDBusInterfaceStub with the same + * interface name, it is removed before @interface is added. + * + * Note that @object takes its own reference on @interface and holds + * it until removed. + */ +void +g_dbus_object_stub_add_interface (GDBusObjectStub *object, + GDBusInterfaceStub *interface) +{ + GDBusInterfaceInfo *info; + + g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object)); + g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (interface)); + + info = g_dbus_interface_stub_get_info (interface); + g_object_ref (interface); + g_dbus_object_stub_remove_interface_by_name (object, info->name); + g_hash_table_insert (object->priv->map_name_to_iface, + g_strdup (info->name), + interface); + g_dbus_interface_set_object (G_DBUS_INTERFACE (interface), G_DBUS_OBJECT (object)); + g_signal_emit_by_name (object, + "interface-added", + interface); +} + +/** + * g_dbus_object_stub_remove_interface: + * @object: A #GDBusObjectStub. + * @interface: A #GDBusInterfaceStub. + * + * Removes @interface from @object. + */ +void +g_dbus_object_stub_remove_interface (GDBusObjectStub *object, + GDBusInterfaceStub *interface) +{ + GDBusInterfaceStub *other_interface; + GDBusInterfaceInfo *info; + + g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object)); + g_return_if_fail (G_IS_DBUS_INTERFACE (interface)); + + info = g_dbus_interface_stub_get_info (interface); + + other_interface = g_hash_table_lookup (object->priv->map_name_to_iface, info->name); + if (other_interface == NULL) + { + g_warning ("Tried to remove interface with name %s from object " + "at path %s but no such interface exists", + info->name, + object->priv->object_path); + } + else if (other_interface != interface) + { + g_warning ("Tried to remove interface %p with name %s from object " + "at path %s but the object has the interface %p", + interface, + info->name, + object->priv->object_path, + other_interface); + } + else + { + g_object_ref (interface); + g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, info->name)); + g_dbus_interface_set_object (G_DBUS_INTERFACE (interface), NULL); + g_signal_emit_by_name (object, + "interface-removed", + interface); + g_object_unref (interface); + } +} + + +/** + * g_dbus_object_stub_remove_interface_by_name: + * @object: A #GDBusObjectStub. + * @interface_name: A D-Bus interface name. + * + * Removes the #GDBusInterface with @interface_name from @object. + * + * If no D-Bus interface of the given interface exists, this function + * does nothing. + */ +void +g_dbus_object_stub_remove_interface_by_name (GDBusObjectStub *object, + const gchar *interface_name) +{ + GDBusInterface *interface; + + g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object)); + g_return_if_fail (g_dbus_is_interface_name (interface_name)); + + interface = g_hash_table_lookup (object->priv->map_name_to_iface, interface_name); + if (interface != NULL) + { + g_object_ref (interface); + g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, interface_name)); + g_dbus_interface_set_object (interface, NULL); + g_signal_emit_by_name (object, + "interface-removed", + interface); + g_object_unref (interface); + } +} + +static GDBusInterface * +g_dbus_object_stub_get_interface (GDBusObject *_object, + const gchar *interface_name) +{ + GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object); + GDBusInterface *ret; + + g_return_val_if_fail (G_IS_DBUS_OBJECT_STUB (object), NULL); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + + ret = g_hash_table_lookup (object->priv->map_name_to_iface, interface_name); + if (ret != NULL) + g_object_ref (ret); + return ret; +} + +static GList * +g_dbus_object_stub_get_interfaces (GDBusObject *_object) +{ + GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object); + GList *ret; + GHashTableIter iter; + GDBusInterface *interface; + + g_return_val_if_fail (G_IS_DBUS_OBJECT_STUB (object), NULL); + + ret = NULL; + + g_hash_table_iter_init (&iter, object->priv->map_name_to_iface); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &interface)) + ret = g_list_prepend (ret, g_object_ref (interface)); + + return ret; +} + +/** + * g_dbus_object_stub_flush: + * @object: A #GDBusObjectStub. + * + * This method simply calls g_dbus_interface_stub_flush() on all + * interfaces stubs belonging to @object. See that method for when + * flushing is useful. + */ +void +g_dbus_object_stub_flush (GDBusObjectStub *object) +{ + GHashTableIter iter; + GDBusInterfaceStub *interface_stub; + + g_hash_table_iter_init (&iter, object->priv->map_name_to_iface); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &interface_stub)) + { + g_dbus_interface_stub_flush (interface_stub); + } +} + +static gpointer +g_dbus_object_stub_lookup_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type) +{ + GDBusObjectStub *stub = G_DBUS_OBJECT_STUB (object); + GDBusProxy *ret; + + ret = g_hash_table_lookup (stub->priv->map_name_to_iface, interface_name); + if (ret != NULL) + { + g_warn_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (ret, type)); + g_object_ref (ret); + } + return ret; +} + +static gpointer +g_dbus_object_stub_peek_with_typecheck (GDBusObject *object, + const gchar *interface_name, + GType type) +{ + GDBusInterfaceStub *ret; + ret = g_dbus_object_stub_lookup_with_typecheck (object, interface_name, type); + if (ret != NULL) + g_object_unref (ret); + return ret; +} + +static void +dbus_object_interface_init (GDBusObjectIface *iface) +{ + iface->get_object_path = g_dbus_object_stub_get_object_path; + iface->get_interfaces = g_dbus_object_stub_get_interfaces; + iface->get_interface = g_dbus_object_stub_get_interface; + iface->lookup_with_typecheck = g_dbus_object_stub_lookup_with_typecheck; + iface->peek_with_typecheck = g_dbus_object_stub_peek_with_typecheck; +} + +gboolean +_g_dbus_object_stub_has_authorize_method_handlers (GDBusObjectStub *stub) +{ + gboolean has_handlers; + gboolean has_default_class_handler; + + has_handlers = g_signal_has_handler_pending (stub, + signals[AUTHORIZE_METHOD_SIGNAL], + 0, + TRUE); + has_default_class_handler = (G_DBUS_OBJECT_STUB_GET_CLASS (stub)->authorize_method == + g_dbus_object_stub_authorize_method_default); + + return has_handlers || !has_default_class_handler; +} diff --git a/gio/gdbusobjectstub.h b/gio/gdbusobjectstub.h new file mode 100644 index 000000000..e5ebfc48d --- /dev/null +++ b/gio/gdbusobjectstub.h @@ -0,0 +1,87 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_OBJECT_STUB_H__ +#define __G_DBUS_OBJECT_STUB_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_OBJECT_STUB (g_dbus_object_stub_get_type ()) +#define G_DBUS_OBJECT_STUB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStub)) +#define G_DBUS_OBJECT_STUB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStubClass)) +#define G_DBUS_OBJECT_STUB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStubClass)) +#define G_IS_DBUS_OBJECT_STUB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_STUB)) +#define G_IS_DBUS_OBJECT_STUB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_STUB)) + +typedef struct _GDBusObjectStubClass GDBusObjectStubClass; +typedef struct _GDBusObjectStubPrivate GDBusObjectStubPrivate; + +/** + * GDBusObjectStub: + * + * The #GDBusObjectStub structure contains private data and should only be + * accessed using the provided API. + */ +struct _GDBusObjectStub +{ + /*< private >*/ + GObject parent_instance; + GDBusObjectStubPrivate *priv; +}; + +/** + * GDBusObjectStubClass: + * @parent_class: The parent class. + * @authorize_method: Signal class handler for the #GDBusObjectStub::authorize-method signal. + * + * Class structure for #GDBusObjectStub. + */ +struct _GDBusObjectStubClass +{ + GObjectClass parent_class; + + /* Signals */ + gboolean (*authorize_method) (GDBusObjectStub *stub, + GDBusInterfaceStub *interface_stub, + GDBusMethodInvocation *invocation); + + /*< private >*/ + gpointer padding[8]; +}; + +GType g_dbus_object_stub_get_type (void) G_GNUC_CONST; +GDBusObjectStub *g_dbus_object_stub_new (const gchar *object_path); +void g_dbus_object_stub_flush (GDBusObjectStub *object); +void g_dbus_object_stub_add_interface (GDBusObjectStub *object, + GDBusInterfaceStub *interface); +void g_dbus_object_stub_remove_interface (GDBusObjectStub *object, + GDBusInterfaceStub *interface); +void g_dbus_object_stub_remove_interface_by_name (GDBusObjectStub *object, + const gchar *interface_name); +void g_dbus_object_stub_set_object_path (GDBusObjectStub *object, + const gchar *object_path); + +G_END_DECLS + +#endif /* __G_DBUS_OBJECT_STUB_H */ diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 9c2c58c15..1e5bcf853 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -1869,3 +1869,21 @@ read_message_print_transport_debug (gssize bytes_read, out: ; } + +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean +_g_signal_accumulator_false_handled (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + gboolean signal_return; + + signal_return = g_value_get_boolean (handler_return); + g_value_set_boolean (return_accu, signal_return); + continue_emission = signal_return; + + return continue_emission; +} diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h index 659e9d48b..a39c19274 100644 --- a/gio/gdbusprivate.h +++ b/gio/gdbusprivate.h @@ -112,8 +112,6 @@ gchar *_g_dbus_get_machine_id (GError **error); gchar *_g_dbus_enum_to_string (GType enum_type, gint value); -G_END_DECLS - /* ---------------------------------------------------------------------------------------------------- */ GDBusMethodInvocation *_g_dbus_method_invocation_new (const gchar *sender, @@ -126,4 +124,22 @@ GDBusMethodInvocation *_g_dbus_method_invocation_new (const gchar *sen GVariant *parameters, gpointer user_data); +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean _g_signal_accumulator_false_handled (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy); + +gboolean _g_dbus_object_stub_has_authorize_method_handlers (GDBusObjectStub *stub); + +GDBusObjectProxy *_g_dbus_object_proxy_new (GDBusConnection *connection, + const gchar *object_path); +void _g_dbus_object_proxy_add_interface (GDBusObjectProxy *proxy, + GDBusProxy *interface_proxy); +void _g_dbus_object_proxy_remove_interface (GDBusObjectProxy *proxy, + const gchar *interface_name); + +G_END_DECLS + #endif /* __G_DBUS_PRIVATE_H__ */ diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 67af1fb83..14cb2baef 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -38,6 +38,7 @@ #include "gasyncresult.h" #include "gsimpleasyncresult.h" #include "gcancellable.h" +#include "gdbusinterface.h" #include "glibintl.h" @@ -129,10 +130,12 @@ enum guint signals[LAST_SIGNAL] = {0}; +static void dbus_interface_iface_init (GDBusInterfaceIface *dbus_interface_iface); static void initable_iface_init (GInitableIface *initable_iface); static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); G_DEFINE_TYPE_WITH_CODE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) ); @@ -2548,3 +2551,36 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy, } /* ---------------------------------------------------------------------------------------------------- */ +/* Hack until this is merged into libgio (extending types at run-time isn't really safe in any way) */ + +static GDBusInterfaceInfo * +_g_dbus_proxy_get_info (GDBusInterface *interface) +{ + GDBusProxy *proxy = G_DBUS_PROXY (interface); + return g_dbus_proxy_get_interface_info (proxy); +} + +static GDBusObject * +_g_dbus_proxy_get_object (GDBusInterface *interface) +{ + /* TODO */ + return g_object_get_data (G_OBJECT (interface), "-x-gdbus-binding-tool-object"); +} + +static void +_g_dbus_proxy_set_object (GDBusInterface *interface, + GDBusObject *object) +{ + /* TODO */ + g_object_set_data (G_OBJECT (interface), "-x-gdbus-binding-tool-object", object); +} + +static void +dbus_interface_iface_init (GDBusInterfaceIface *dbus_interface_iface) +{ + dbus_interface_iface->get_info = _g_dbus_proxy_get_info; + dbus_interface_iface->get_object = _g_dbus_proxy_get_object; + dbus_interface_iface->set_object = _g_dbus_proxy_set_object; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list index 8a42ad75b..ad6d18520 100644 --- a/gio/gio-marshal.list +++ b/gio/gio-marshal.list @@ -29,3 +29,6 @@ VOID:UINT64 BOOLEAN:FLAGS BOOLEAN:OBJECT,FLAGS OBJECT:VOID +VOID:OBJECT,OBJECT +VOID:OBJECT,OBJECT,STRING,STRING,VARIANT +VOID:OBJECT,OBJECT,VARIANT,BOXED diff --git a/gio/gio.h b/gio/gio.h index 288da4406..70cfe9ffd 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -130,6 +130,14 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #undef __GIO_GIO_H_INSIDE__ diff --git a/gio/gio.symbols b/gio/gio.symbols index 64275aebc..dcc5ac849 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -2086,3 +2086,99 @@ g_time_zone_monitor_get_type G_GNUC_CONST g_time_zone_monitor_get #endif #endif + +#if IN_HEADER(__G_DBUS_INTERFACE_H__) +#if IN_FILE(__G_DBUS_INTERFACE_C__) +g_dbus_interface_get_info +g_dbus_interface_get_object +g_dbus_interface_get_type +g_dbus_interface_set_object +g_dbus_gvalue_to_gvariant +g_dbus_gvariant_to_gvalue +#endif +#endif + +#if IN_HEADER(__G_DBUS_INTERFACE_STUB_H__) +#if IN_FILE(__G_DBUS_INTERFACE_STUB_C__) +g_dbus_interface_stub_export +g_dbus_interface_stub_flags_get_type +g_dbus_interface_stub_flush +g_dbus_interface_stub_get_connection +g_dbus_interface_stub_get_flags +g_dbus_interface_stub_get_info +g_dbus_interface_stub_get_object_path +g_dbus_interface_stub_get_properties +g_dbus_interface_stub_get_type +g_dbus_interface_stub_get_vtable +g_dbus_interface_stub_set_flags +g_dbus_interface_stub_unexport +#endif +#endif + +#if IN_HEADER(__G_DBUS_OBJECT_H__) +#if IN_FILE(__G_DBUS_OBJECT_C__) +g_dbus_object_get_interface +g_dbus_object_get_interfaces +g_dbus_object_get_object_path +g_dbus_object_get_type +g_dbus_object_lookup_with_typecheck +g_dbus_object_peek_with_typecheck +#endif +#endif + +#if IN_HEADER(__G_DBUS_OBJECT_PROXY_H__) +#if IN_FILE(__G_DBUS_OBJECT_PROXY_C__) +g_dbus_object_proxy_get_connection +g_dbus_object_proxy_get_type +#endif +#endif + +#if IN_HEADER(__G_DBUS_OBJECT_STUB_H__) +#if IN_FILE(__G_DBUS_OBJECT_STUB_C__) +g_dbus_object_stub_add_interface +g_dbus_object_stub_flush +g_dbus_object_stub_get_type +g_dbus_object_stub_new +g_dbus_object_stub_remove_interface +g_dbus_object_stub_remove_interface_by_name +g_dbus_object_stub_set_object_path +#endif +#endif + +#if IN_HEADER(__G_DBUS_OBJECT_MANAGER_H__) +#if IN_FILE(__G_DBUS_OBJECT_MANAGER_C__) +g_dbus_object_manager_get_interface +g_dbus_object_manager_get_object +g_dbus_object_manager_get_object_path +g_dbus_object_manager_get_objects +g_dbus_object_manager_get_type +#endif +#endif + +#if IN_HEADER(__G_DBUS_OBJECT_MANAGER_CLIENT_H__) +#if IN_FILE(__G_DBUS_OBJECT_MANAGER_CLIENT_C__) +g_dbus_object_manager_client_flags_get_type +g_dbus_object_manager_client_get_connection +g_dbus_object_manager_client_get_flags +g_dbus_object_manager_client_get_name +g_dbus_object_manager_client_get_name_owner +g_dbus_object_manager_client_get_type +g_dbus_object_manager_client_new +g_dbus_object_manager_client_new_finish +g_dbus_object_manager_client_new_for_bus +g_dbus_object_manager_client_new_for_bus_finish +g_dbus_object_manager_client_new_for_bus_sync +g_dbus_object_manager_client_new_sync +#endif +#endif + +#if IN_HEADER(__G_DBUS_OBJECT_MANAGER_SERVER_H__) +#if IN_FILE(__G_DBUS_OBJECT_MANAGER_SERVER_C__) +g_dbus_object_manager_server_export +g_dbus_object_manager_server_export_and_uniquify +g_dbus_object_manager_server_get_connection +g_dbus_object_manager_server_get_type +g_dbus_object_manager_server_new +g_dbus_object_manager_server_unexport +#endif +#endif diff --git a/gio/gioenums.h b/gio/gioenums.h index 0989799a5..89619147b 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1383,6 +1383,42 @@ typedef enum { G_TLS_REHANDSHAKE_UNSAFELY } GTlsRehandshakeMode; +/** + * GDBusInterfaceStubFlags: + * @G_DBUS_INTERFACE_STUB_FLAGS_NONE: No flags set. + * @G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD: Each method invocation is handled in + * a thread dedicated to the invocation. This means that the method implementation can use blocking IO + * without blocking any other part of the process. It also means that the method implementation must + * use locking to access data structures used by other threads. + * + * Flags describing the behavior of a #GDBusInterfaceStub class. + * + * Since: 2.30 + */ +typedef enum +{ + G_DBUS_INTERFACE_STUB_FLAGS_NONE = 0, + G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD = (1<<0) +} GDBusInterfaceStubFlags; + +/** + * GDBusObjectManagerClientFlags: + * @G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE: No flags set. + * @G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START: If not set and the + * manager is for a well-known name, then request the bus to launch + * an owner for the name if no-one owns the name. This flag can only + * be used in managers for well-known names. + * + * Flags used when constructing a #GDBusObjectManagerClient. + * + * Since: 2.30 + */ +typedef enum +{ + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE = 0, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START = (1<<0), +} GDBusObjectManagerClientFlags; + G_END_DECLS #endif /* __GIO_ENUMS_H__ */ diff --git a/gio/giotypes.h b/gio/giotypes.h index 1c350837a..848336aca 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -418,6 +418,39 @@ typedef gboolean (*GCancellableSourceFunc) (GCancellable *cancellable, typedef gboolean (*GPollableSourceFunc) (GObject *pollable_stream, gpointer user_data); +typedef struct _GDBusInterface GDBusInterface; /* Dummy typedef */ +typedef struct _GDBusInterfaceStub GDBusInterfaceStub; +typedef struct _GDBusObject GDBusObject; /* Dummy typedef */ +typedef struct _GDBusObjectStub GDBusObjectStub; +typedef struct _GDBusObjectProxy GDBusObjectProxy; +typedef struct _GDBusObjectManager GDBusObjectManager; /* Dummy typedef */ +typedef struct _GDBusObjectManagerClient GDBusObjectManagerClient; +typedef struct _GDBusObjectManagerServer GDBusObjectManagerServer; + +/** + * GDBusProxyTypeFunc: + * @manager: A #GDBusObjectManagerClient. + * @object_path: The object path of the remote object. + * @interface_name: The interface name of the remote object. + * @user_data: User data. + * + * Function signature for a function used to determine the #GType to + * use for an interface proxy. + * + * This function is called in the + * thread-default main loop + * that @manager was constructed in. + * + * Returns: A #GType to use for the remote object. The returned type + * must be a #GDBusProxy derived type. + * + * Since: 2.30 + */ +typedef GType (*GDBusProxyTypeFunc) (GDBusObjectManagerClient *manager, + const gchar *object_path, + const gchar *interface_name, + gpointer user_data); + G_END_DECLS #endif /* __GIO_TYPES_H__ */