From d0a14469d09d5fe23de219ba293fd4a266b02ced Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 14:13:59 -0400 Subject: [PATCH 01/76] Initial GDBus code-drop from GDBus-standalone repo Things compile and the test-suite passes. Still need to hook up gio.symbols and docs. There are still a bunch of TODOs left in the sources that needs to be addressed. Signed-off-by: David Zeuthen --- configure.in | 10 + gio/Makefile.am | 58 +- gio/gcredentials.c | 427 ++ gio/gcredentials.h | 104 + gio/gdbus-bash-completion.sh | 33 + gio/gdbus-tool.c | 1491 ++++++ gio/gdbusaddress.c | 1004 ++++ gio/gdbusaddress.h | 54 + gio/gdbusauth.c | 1538 +++++++ gio/gdbusauth.h | 86 + gio/gdbusauthmechanism.c | 342 ++ gio/gdbusauthmechanism.h | 174 + gio/gdbusauthmechanismanon.c | 327 ++ gio/gdbusauthmechanismanon.h | 82 + gio/gdbusauthmechanismexternal.c | 416 ++ gio/gdbusauthmechanismexternal.h | 82 + gio/gdbusauthmechanismsha1.c | 1216 +++++ gio/gdbusauthmechanismsha1.h | 82 + gio/gdbusauthobserver.c | 218 + gio/gdbusauthobserver.h | 100 + gio/gdbusconnection.c | 5280 ++++++++++++++++++++++ gio/gdbusconnection.h | 467 ++ gio/gdbuserror.c | 847 ++++ gio/gdbuserror.h | 92 + gio/gdbusintrospection.c | 2009 ++++++++ gio/gdbusintrospection.h | 255 ++ gio/gdbusmessage.c | 2421 ++++++++++ gio/gdbusmessage.h | 172 + gio/gdbusmethodinvocation.c | 795 ++++ gio/gdbusmethodinvocation.h | 119 + gio/gdbusnameowning.c | 713 +++ gio/gdbusnameowning.h | 88 + gio/gdbusnamewatching.c | 620 +++ gio/gdbusnamewatching.h | 68 + gio/gdbusprivate.c | 1040 +++++ gio/gdbusprivate.h | 83 + gio/gdbusproxy.c | 1542 +++++++ gio/gdbusproxy.h | 146 + gio/gdbusproxywatching.c | 397 ++ gio/gdbusproxywatching.h | 77 + gio/gdbusserver.c | 1043 +++++ gio/gdbusserver.h | 97 + gio/gdbusutils.c | 364 ++ gio/gdbusutils.h | 42 + gio/gio-marshal.list | 2 + gio/gio.h | 16 + gio/gioenums.h | 372 +- gio/giotypes.h | 19 + gio/gunixcredentialsmessage.c | 341 ++ gio/gunixcredentialsmessage.h | 68 + gio/tests/Makefile.am | 98 +- gio/tests/gdbus-addresses.c | 77 + gio/tests/gdbus-connection.c | 653 +++ gio/tests/gdbus-error.c | 198 + gio/tests/gdbus-example-own-name.c | 99 + gio/tests/gdbus-example-peer.c | 318 ++ gio/tests/gdbus-example-server.c | 388 ++ gio/tests/gdbus-example-subtree.c | 410 ++ gio/tests/gdbus-example-unix-fd-client.c | 145 + gio/tests/gdbus-example-watch-name.c | 101 + gio/tests/gdbus-example-watch-proxy.c | 205 + gio/tests/gdbus-exit-on-close.c | 82 + gio/tests/gdbus-export.c | 1410 ++++++ gio/tests/gdbus-introspection.c | 169 + gio/tests/gdbus-names.c | 749 +++ gio/tests/gdbus-peer.c | 746 +++ gio/tests/gdbus-proxy.c | 455 ++ gio/tests/gdbus-serialization.c | 650 +++ gio/tests/gdbus-sessionbus.c | 342 ++ gio/tests/gdbus-sessionbus.h | 38 + gio/tests/gdbus-tests.c | 218 + gio/tests/gdbus-tests.h | 146 + gio/tests/gdbus-testserver.py | 270 ++ gio/tests/gdbus-threading.c | 532 +++ 74 files changed, 35927 insertions(+), 11 deletions(-) create mode 100644 gio/gcredentials.c create mode 100644 gio/gcredentials.h create mode 100644 gio/gdbus-bash-completion.sh create mode 100644 gio/gdbus-tool.c create mode 100644 gio/gdbusaddress.c create mode 100644 gio/gdbusaddress.h create mode 100644 gio/gdbusauth.c create mode 100644 gio/gdbusauth.h create mode 100644 gio/gdbusauthmechanism.c create mode 100644 gio/gdbusauthmechanism.h create mode 100644 gio/gdbusauthmechanismanon.c create mode 100644 gio/gdbusauthmechanismanon.h create mode 100644 gio/gdbusauthmechanismexternal.c create mode 100644 gio/gdbusauthmechanismexternal.h create mode 100644 gio/gdbusauthmechanismsha1.c create mode 100644 gio/gdbusauthmechanismsha1.h create mode 100644 gio/gdbusauthobserver.c create mode 100644 gio/gdbusauthobserver.h create mode 100644 gio/gdbusconnection.c create mode 100644 gio/gdbusconnection.h create mode 100644 gio/gdbuserror.c create mode 100644 gio/gdbuserror.h create mode 100644 gio/gdbusintrospection.c create mode 100644 gio/gdbusintrospection.h create mode 100644 gio/gdbusmessage.c create mode 100644 gio/gdbusmessage.h create mode 100644 gio/gdbusmethodinvocation.c create mode 100644 gio/gdbusmethodinvocation.h create mode 100644 gio/gdbusnameowning.c create mode 100644 gio/gdbusnameowning.h create mode 100644 gio/gdbusnamewatching.c create mode 100644 gio/gdbusnamewatching.h create mode 100644 gio/gdbusprivate.c create mode 100644 gio/gdbusprivate.h create mode 100644 gio/gdbusproxy.c create mode 100644 gio/gdbusproxy.h create mode 100644 gio/gdbusproxywatching.c create mode 100644 gio/gdbusproxywatching.h create mode 100644 gio/gdbusserver.c create mode 100644 gio/gdbusserver.h create mode 100644 gio/gdbusutils.c create mode 100644 gio/gdbusutils.h create mode 100644 gio/gunixcredentialsmessage.c create mode 100644 gio/gunixcredentialsmessage.h create mode 100644 gio/tests/gdbus-addresses.c create mode 100644 gio/tests/gdbus-connection.c create mode 100644 gio/tests/gdbus-error.c create mode 100644 gio/tests/gdbus-example-own-name.c create mode 100644 gio/tests/gdbus-example-peer.c create mode 100644 gio/tests/gdbus-example-server.c create mode 100644 gio/tests/gdbus-example-subtree.c create mode 100644 gio/tests/gdbus-example-unix-fd-client.c create mode 100644 gio/tests/gdbus-example-watch-name.c create mode 100644 gio/tests/gdbus-example-watch-proxy.c create mode 100644 gio/tests/gdbus-exit-on-close.c create mode 100644 gio/tests/gdbus-export.c create mode 100644 gio/tests/gdbus-introspection.c create mode 100644 gio/tests/gdbus-names.c create mode 100644 gio/tests/gdbus-peer.c create mode 100644 gio/tests/gdbus-proxy.c create mode 100644 gio/tests/gdbus-serialization.c create mode 100644 gio/tests/gdbus-sessionbus.c create mode 100644 gio/tests/gdbus-sessionbus.h create mode 100644 gio/tests/gdbus-tests.c create mode 100644 gio/tests/gdbus-tests.h create mode 100755 gio/tests/gdbus-testserver.py create mode 100644 gio/tests/gdbus-threading.c diff --git a/configure.in b/configure.in index 05bfad7a4..882bbc0ed 100644 --- a/configure.in +++ b/configure.in @@ -3496,6 +3496,16 @@ if test x$glib_win32_static_compilation = xyes; then fi ]) +# Check for libdbus1 - Optional - is only used in the GDBus test cases +# +PKG_CHECK_MODULES(DBUS1, + dbus-1, + [AC_DEFINE(HAVE_DBUS1, 1, [Define if dbus-1 is available]) have_dbus1=yes], + have_dbus1=no) +AC_SUBST(DBUS1_CFLAGS) +AC_SUBST(DBUS1_LIBS) +AM_CONDITIONAL(HAVE_DBUS1, [test "x$have_dbus1" = "xyes"]) + AC_CONFIG_FILES([ glib-2.0.pc glib-2.0-uninstalled.pc diff --git a/gio/Makefile.am b/gio/Makefile.am index bdc897d01..147974923 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -79,6 +79,48 @@ gio-marshal.c: gio-marshal.h gio-marshal.list $(glib_genmarshal) --prefix=_gio_marshal $(srcdir)/gio-marshal.list --body --internal) > $@.tmp && \ mv $@.tmp $@ +gdbus_headers = \ + gdbusauthobserver.h \ + gcredentials.h \ + gunixcredentialsmessage.h \ + gdbusutils.h \ + gdbuserror.h \ + gdbusaddress.h \ + gdbusconnection.h \ + gdbusmessage.h \ + gdbusnameowning.h \ + gdbusnamewatching.h \ + gdbusproxywatching.h \ + gdbusproxy.h \ + gdbusintrospection.h \ + gdbusmethodinvocation.h \ + gdbusserver.h \ + $(NULL) + +gdbus_sources = \ + gdbusutils.h gdbusutils.c \ + gcredentials.h gcredentials.c \ + gunixcredentialsmessage.h gunixcredentialsmessage.c \ + gdbusaddress.h gdbusaddress.c \ + gdbusauthobserver.h gdbusauthobserver.c \ + gdbusauth.h gdbusauth.c \ + gdbusauthmechanism.h gdbusauthmechanism.c \ + gdbusauthmechanismanon.h gdbusauthmechanismanon.c \ + gdbusauthmechanismexternal.h gdbusauthmechanismexternal.c \ + gdbusauthmechanismsha1.h gdbusauthmechanismsha1.c \ + gdbuserror.h gdbuserror.c \ + gdbusconnection.h gdbusconnection.c \ + gdbusmessage.h gdbusmessage.c \ + gdbusnameowning.h gdbusnameowning.c \ + gdbusnamewatching.h gdbusnamewatching.c \ + gdbusproxywatching.h gdbusproxywatching.c \ + gdbusproxy.h gdbusproxy.c \ + gdbusprivate.h gdbusprivate.c \ + gdbusintrospection.h gdbusintrospection.c \ + gdbusmethodinvocation.h gdbusmethodinvocation.c \ + gdbusserver.h gdbusserver.c \ + $(NULL) + settings_headers = \ gsettingsbackend.h \ gsettings.h @@ -327,6 +369,7 @@ libgio_2_0_la_SOURCES = \ $(unix_sources) \ $(win32_sources) \ $(settings_sources) \ + $(gdbus_sources) \ $(local_sources) \ $(marshal_sources) \ $(NULL) @@ -454,6 +497,7 @@ gio_headers = \ gzlibcompressor.h \ gzlibdecompressor.h \ $(settings_headers) \ + $(gdbus_headers) \ $(NULL) gioincludedir=$(includedir)/glib-2.0/gio/ @@ -527,10 +571,22 @@ gsettings_LDADD = \ libgio-2.0.la gsettings_SOURCES = gsettings-tool.c - schemadir = $(datadir)/glib-2.0/schemas dist_schema_DATA = gschema.dtd +# ------------------------------------------------------------------------ +# gdbus(1) tool + +bin_PROGRAMS += gdbus +gdbus_SOURCES = gdbus-tool.c +gdbus_LDADD = libgio-2.0.la + +completiondir = $(sysconfdir)/bash_completion.d +completion_SCRIPTS = gdbus-bash-completion.sh +EXTRA_DIST += $(completion_SCRIPTS) + +# ------------------------------------------------------------------------ + dist-hook: $(BUILT_EXTRA_DIST) ../build/win32/vs9/gio.vcproj files='$(BUILT_EXTRA_DIST)'; \ for f in $$files; do \ diff --git a/gio/gcredentials.c b/gio/gcredentials.c new file mode 100644 index 000000000..e1712fe61 --- /dev/null +++ b/gio/gcredentials.c @@ -0,0 +1,427 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gcredentials.h" +#include "gioerror.h" + +#ifdef G_OS_UNIX +#include +#include +#endif + +/** + * SECTION:gcredentials + * @short_description: Credentials + * @include: gdbus/gdbus.h + * + * The #GCredentials type is used for storing information that can be + * used for identifying, authenticating and authorizing processes. + * + * Most UNIX and UNIX-like operating systems support a secure exchange + * of credentials over a Unix Domain Socket, see + * #GUnixCredentialsMessage, g_unix_connection_send_credentials() and + * g_unix_connection_receive_credentials() for details. + */ + +struct _GCredentialsPrivate +{ + gint64 unix_user; + gint64 unix_group; + gint64 unix_process; + gchar *windows_user; +}; + +G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT); + +static void +g_credentials_finalize (GObject *object) +{ + GCredentials *credentials = G_CREDENTIALS (object); + + g_free (credentials->priv->windows_user); + + if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object); +} + + +static void +g_credentials_class_init (GCredentialsClass *klass) +{ + GObjectClass *gobject_class; + + g_type_class_add_private (klass, sizeof (GCredentialsPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = g_credentials_finalize; +} + +static void +g_credentials_init (GCredentials *credentials) +{ + credentials->priv = G_TYPE_INSTANCE_GET_PRIVATE (credentials, G_TYPE_CREDENTIALS, GCredentialsPrivate); + + credentials->priv->unix_user = -1; + credentials->priv->unix_group = -1; + credentials->priv->unix_process = -1; + credentials->priv->windows_user = NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_new: + * + * Creates a new empty credentials object. + * + * Returns: A #GCredentials. Free with g_object_unref(). + */ +GCredentials * +g_credentials_new (void) +{ + return g_object_new (G_TYPE_CREDENTIALS, NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_UNIX +static GCredentials * +g_credentials_new_for_unix_process (void) +{ + GCredentials *credentials; + credentials = g_credentials_new (); + credentials->priv->unix_user = getuid (); + credentials->priv->unix_group = getgid (); + credentials->priv->unix_process = getpid (); + return credentials; +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_new_for_process: + * + * Gets the credentials for the current process. Note that the exact + * set of credentials in the returned object vary according to + * platform. + * + * Returns: A #GCredentials. Free with g_object_unref(). + */ +GCredentials * +g_credentials_new_for_process (void) +{ +#ifdef G_OS_UNIX + return g_credentials_new_for_unix_process (); +#elif G_OS_WIN32 + return g_credentials_new_for_win32_process (); +#else +#warning Please implement g_credentials_new_for_process() for your OS. For now g_credentials_new_for_process() will return empty credentials. + return g_credentials_new (); +#endif +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_new_for_string: + * @str: A string returned from g_credentials_to_string(). + * @error: Return location for error. + * + * Constructs a #GCredentials instance from @str. + * + * Returns: A #GCredentials or %NULL if @error is set. The return + * object must be freed with g_object_unref(). + */ +GCredentials * +g_credentials_new_for_string (const gchar *str, + GError **error) +{ + GCredentials *credentials; + gchar **tokens; + guint n; + + g_return_val_if_fail (str != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + tokens = NULL; + credentials = g_credentials_new (); + + if (!g_str_has_prefix (str, "GCredentials:")) + goto fail; + + tokens = g_strsplit (str + sizeof "GCredentials:" - 1, ",", 0); + for (n = 0; tokens[n] != NULL; n++) + { + const gchar *token = tokens[n]; + if (g_str_has_prefix (token, "unix-user:")) + g_credentials_set_unix_user (credentials, atoi (token + sizeof ("unix-user:") - 1)); + else if (g_str_has_prefix (token, "unix-group:")) + g_credentials_set_unix_group (credentials, atoi (token + sizeof ("unix-group:") - 1)); + else if (g_str_has_prefix (token, "unix-process:")) + g_credentials_set_unix_process (credentials, atoi (token + sizeof ("unix-process:") - 1)); + else if (g_str_has_prefix (token, "windows-user:")) + g_credentials_set_windows_user (credentials, token + sizeof ("windows-user:")); + else + goto fail; + } + g_strfreev (tokens); + return credentials; + + fail: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("The string `%s' is not a valid credentials string"), + str); + g_object_unref (credentials); + g_strfreev (tokens); + return NULL; +} + +/** + * g_credentials_to_string: + * @credentials: A #GCredentials object. + * + * Serializes @credentials to a string that can be used with + * g_credentials_new_for_string(). + * + * Returns: A string that should be freed with g_free(). + */ +gchar * +g_credentials_to_string (GCredentials *credentials) +{ + GString *ret; + + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL); + + ret = g_string_new ("GCredentials:"); + if (credentials->priv->unix_user != -1) + g_string_append_printf (ret, "unix-user=%" G_GINT64_FORMAT ",", credentials->priv->unix_user); + if (credentials->priv->unix_group != -1) + g_string_append_printf (ret, "unix-group=%" G_GINT64_FORMAT ",", credentials->priv->unix_group); + if (credentials->priv->unix_process != -1) + g_string_append_printf (ret, "unix-process=%" G_GINT64_FORMAT ",", credentials->priv->unix_process); + if (credentials->priv->windows_user != NULL) + g_string_append_printf (ret, "windows-user=%s,", credentials->priv->windows_user); + if (ret->str[ret->len - 1] == ',') + ret->str[ret->len - 1] = '\0'; + + return g_string_free (ret, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_has_unix_user: + * @credentials: A #GCredentials. + * + * Checks if @credentials has a UNIX user credential. + * + * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + */ +gboolean +g_credentials_has_unix_user (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); + return credentials->priv->unix_user != -1; +} + +/** + * g_credentials_get_unix_user: + * @credentials: A #GCredentials. + * + * Gets the UNIX user identifier from @credentials. + * + * Returns: The identifier or -1 if unset. + */ +gint64 +g_credentials_get_unix_user (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1); + return credentials->priv->unix_user; +} + +/** + * g_credentials_set_unix_user: + * @credentials: A #GCredentials. + * @user_id: A UNIX user identifier (typically type #uid_t) or -1 to unset it. + * + * Sets the UNIX user identifier. + */ +void +g_credentials_set_unix_user (GCredentials *credentials, + gint64 user_id) +{ + g_return_if_fail (G_IS_CREDENTIALS (credentials)); + credentials->priv->unix_user = user_id; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_has_unix_group: + * @credentials: A #GCredentials. + * + * Checks if @credentials has a UNIX group credential. + * + * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + */ +gboolean +g_credentials_has_unix_group (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); + return credentials->priv->unix_group != -1; +} + +/** + * g_credentials_get_unix_group: + * @credentials: A #GCredentials. + * + * Gets the UNIX group identifier from @credentials. + * + * Returns: The identifier or -1 if unset. + */ +gint64 +g_credentials_get_unix_group (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1); + return credentials->priv->unix_group; +} + +/** + * g_credentials_set_unix_group: + * @credentials: A #GCredentials. + * @group_id: A UNIX group identifier (typically type #gid_t) or -1 to unset. + * + * Sets the UNIX group identifier. + */ +void +g_credentials_set_unix_group (GCredentials *credentials, + gint64 group_id) +{ + g_return_if_fail (G_IS_CREDENTIALS (credentials)); + credentials->priv->unix_group = group_id; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_has_unix_process: + * @credentials: A #GCredentials. + * + * Checks if @credentials has a UNIX process credential. + * + * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + */ +gboolean +g_credentials_has_unix_process (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); + return credentials->priv->unix_process != -1; +} + +/** + * g_credentials_get_unix_process: + * @credentials: A #GCredentials. + * + * Gets the UNIX process identifier from @credentials. + * + * Returns: The identifier or -1 if unset. + */ +gint64 +g_credentials_get_unix_process (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1); + return credentials->priv->unix_process; +} + +/** + * g_credentials_set_unix_process: + * @credentials: A #GCredentials. + * @process_id: A UNIX process identifier (typically type #pid_t/#GPid) or -1 to unset. + * + * Sets the UNIX process identifier. + */ +void +g_credentials_set_unix_process (GCredentials *credentials, + gint64 process_id) +{ + g_return_if_fail (G_IS_CREDENTIALS (credentials)); + credentials->priv->unix_process = process_id; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_credentials_has_windows_user: + * @credentials: A #GCredentials. + * + * Checks if @credentials has a Windows user SID (Security Identifier). + * + * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + */ +gboolean +g_credentials_has_windows_user (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); + return credentials->priv->windows_user != NULL; +} + +/** + * g_credentials_get_windows_user: + * @credentials: A #GCredentials. + * + * Gets the Windows User SID from @credentials. + * + * Returns: A string or %NULL if unset. Do not free, the string is owned by @credentials. + */ +const gchar * +g_credentials_get_windows_user (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL); + return credentials->priv->windows_user; +} + +/** + * g_credentials_set_windows_user: + * @credentials: A #GCredentials. + * @user_sid: The Windows User SID or %NULL to unset. + * + * Sets the Windows User SID. + */ +void +g_credentials_set_windows_user (GCredentials *credentials, + const gchar *user_sid) +{ + g_return_if_fail (G_IS_CREDENTIALS (credentials)); + g_free (credentials->priv->windows_user); + credentials->priv->windows_user = g_strdup (user_sid); +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gcredentials.h b/gio/gcredentials.h new file mode 100644 index 000000000..a5bab7b7a --- /dev/null +++ b/gio/gcredentials.h @@ -0,0 +1,104 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_CREDENTIALS_H__ +#define __G_CREDENTIALS_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_CREDENTIALS (g_credentials_get_type ()) +#define G_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CREDENTIALS, GCredentials)) +#define G_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CREDENTIALS, GCredentialsClass)) +#define G_CREDENTIALS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CREDENTIALS, GCredentialsClass)) +#define G_IS_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CREDENTIALS)) +#define G_IS_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CREDENTIALS)) + +typedef struct _GCredentialsClass GCredentialsClass; +typedef struct _GCredentialsPrivate GCredentialsPrivate; + +/** + * GCredentials: + * + * The #GCredentials structure contains only private data and + * should only be accessed using the provided API. + */ +struct _GCredentials +{ + /*< private >*/ + GObject parent_instance; + GCredentialsPrivate *priv; +}; + +/** + * GCredentialsClass: + * + * Class structure for #GCredentials. + */ +struct _GCredentialsClass +{ + /*< private >*/ + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +GType g_credentials_get_type (void) G_GNUC_CONST; + +GCredentials *g_credentials_new (void); +GCredentials *g_credentials_new_for_process (void); +GCredentials *g_credentials_new_for_string (const gchar *str, + GError **error); +gchar *g_credentials_to_string (GCredentials *credentials); + +gboolean g_credentials_has_unix_user (GCredentials *credentials); +gint64 g_credentials_get_unix_user (GCredentials *credentials); +void g_credentials_set_unix_user (GCredentials *credentials, + gint64 user_id); + +gboolean g_credentials_has_unix_group (GCredentials *credentials); +gint64 g_credentials_get_unix_group (GCredentials *credentials); +void g_credentials_set_unix_group (GCredentials *credentials, + gint64 group_id); + +gboolean g_credentials_has_unix_process (GCredentials *credentials); +gint64 g_credentials_get_unix_process (GCredentials *credentials); +void g_credentials_set_unix_process (GCredentials *credentials, + gint64 process_id); + +gboolean g_credentials_has_windows_user (GCredentials *credentials); +const gchar *g_credentials_get_windows_user (GCredentials *credentials); +void g_credentials_set_windows_user (GCredentials *credentials, + const gchar *user_sid); + +G_END_DECLS + +#endif /* __G_DBUS_PROXY_H__ */ diff --git a/gio/gdbus-bash-completion.sh b/gio/gdbus-bash-completion.sh new file mode 100644 index 000000000..79f4cb4b4 --- /dev/null +++ b/gio/gdbus-bash-completion.sh @@ -0,0 +1,33 @@ + +# Check for bash +[ -z "$BASH_VERSION" ] && return + +#################################################################################################### + + +__gdbus() { + local IFS=$'\n' + local cur=`_get_cword :` + + local suggestions=$(gdbus complete "${COMP_LINE}" ${COMP_POINT}) + COMPREPLY=($(compgen -W "$suggestions" -- "$cur")) + + # Remove colon-word prefix from COMPREPLY items + case "$cur" in + *:*) + case "$COMP_WORDBREAKS" in + *:*) + local colon_word=${cur%${cur##*:}} + local i=${#COMPREPLY[*]} + while [ $((--i)) -ge 0 ]; do + COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} + done + ;; + esac + ;; + esac +} + +#################################################################################################### + +complete -o nospace -F __gdbus gdbus diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c new file mode 100644 index 000000000..b3233cf45 --- /dev/null +++ b/gio/gdbus-tool.c @@ -0,0 +1,1491 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +G_GNUC_UNUSED static void completion_debug (const gchar *format, ...); + +/* Uncomment to get debug traces in /tmp/gdbus-completion-debug.txt (nice + * to not have it interfere with stdout/stderr) + */ +#if 0 +G_GNUC_UNUSED static void +completion_debug (const gchar *format, ...) +{ + va_list var_args; + gchar *s; + static FILE *f = NULL; + + va_start (var_args, format); + s = g_strdup_vprintf (format, var_args); + if (f == NULL) + { + f = fopen ("/tmp/gdbus-completion-debug.txt", "a+"); + } + fprintf (f, "%s\n", s); + g_free (s); +} +#else +static void +completion_debug (const gchar *format, ...) +{ +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + + +static void +remove_arg (gint num, gint *argc, gchar **argv[]) +{ + gint n; + + g_assert (num <= (*argc)); + + for (n = num; (*argv)[n] != NULL; n++) + (*argv)[n] = (*argv)[n+1]; + (*argv)[n] = NULL; + (*argc) = (*argc) - 1; +} + +static void +usage (gint *argc, gchar **argv[], gboolean use_stdout) +{ + GOptionContext *o; + gchar *s; + gchar *program_name; + + o = g_option_context_new (_("COMMAND")); + g_option_context_set_help_enabled (o, FALSE); + /* Ignore parsing result */ + g_option_context_parse (o, argc, argv, NULL); + program_name = g_path_get_basename ((*argv)[0]); + s = g_strdup_printf (_("Commands:\n" + " help Shows this information\n" + " introspect Introspect a remote object\n" + " call Invoke a method on a remote object\n" + "\n" + "Use \"%s COMMAND --help\" to get help on each command.\n"), + program_name); + g_free (program_name); + g_option_context_set_description (o, s); + g_free (s); + s = g_option_context_get_help (o, FALSE, NULL); + if (use_stdout) + g_print ("%s", s); + else + g_printerr ("%s", s); + g_free (s); + g_option_context_free (o); +} + +static void +modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command) +{ + gchar *s; + gchar *program_name; + + /* TODO: + * 1. get a g_set_prgname() ?; or + * 2. save old argv[0] and restore later + */ + + g_assert (g_strcmp0 ((*argv)[1], command) == 0); + remove_arg (1, argc, argv); + + program_name = g_path_get_basename ((*argv)[0]); + s = g_strdup_printf ("%s %s", (*argv)[0], command); + (*argv)[0] = s; + g_free (program_name); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +print_methods (GDBusConnection *c, + const gchar *name, + const gchar *path) +{ + GVariant *result; + GError *error; + const gchar *xml_data; + GDBusNodeInfo *node; + guint n; + guint m; + + error = NULL; + result = g_dbus_connection_invoke_method_sync (c, + name, + path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); + if (result == NULL) + { + g_printerr (_("Error: %s\n"), error->message); + g_error_free (error); + goto out; + } + if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)"))) + { + g_printerr (_("Error: Result is type `%s', expected `(s)'\n"), + g_variant_get_type_string (result)); + g_variant_unref (result); + goto out; + } + g_variant_get (result, "(s)", &xml_data); + + error = NULL; + node = g_dbus_node_info_new_for_xml (xml_data, &error); + g_variant_unref (result); + if (node == NULL) + { + g_printerr (_("Error parsing introspection XML: %s\n"), error->message); + g_error_free (error); + goto out; + } + + for (n = 0; node->interfaces != NULL && node->interfaces[n] != NULL; n++) + { + const GDBusInterfaceInfo *iface = node->interfaces[n]; + for (m = 0; iface->methods != NULL && iface->methods[m] != NULL; m++) + { + const GDBusMethodInfo *method = iface->methods[m]; + g_print ("%s.%s \n", iface->name, method->name); + } + } + g_dbus_node_info_unref (node); + + out: + ; +} + +static void +print_paths (GDBusConnection *c, + const gchar *name, + const gchar *path) +{ + GVariant *result; + GError *error; + const gchar *xml_data; + GDBusNodeInfo *node; + guint n; + + error = NULL; + result = g_dbus_connection_invoke_method_sync (c, + name, + path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); + if (result == NULL) + { + g_printerr (_("Error: %s\n"), error->message); + g_error_free (error); + goto out; + } + if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)"))) + { + g_printerr (_("Error: Result is type `%s', expected `(s)'\n"), + g_variant_get_type_string (result)); + g_variant_unref (result); + goto out; + } + g_variant_get (result, "(s)", &xml_data); + + error = NULL; + node = g_dbus_node_info_new_for_xml (xml_data, &error); + g_variant_unref (result); + if (node == NULL) + { + g_printerr (_("Error parsing introspection XML: %s\n"), error->message); + g_error_free (error); + goto out; + } + + //g_printerr ("xml=`%s'", xml_data); + + //g_printerr ("bar `%s'\n", path); + + if (node->interfaces != NULL) + g_print ("%s \n", path); + + for (n = 0; node->nodes != NULL && node->nodes[n] != NULL; n++) + { + gchar *s; + + //g_printerr ("foo `%s'\n", node->nodes[n].path); + + if (g_strcmp0 (path, "/") == 0) + s = g_strdup_printf ("/%s", node->nodes[n]->path); + else + s = g_strdup_printf ("%s/%s", path, node->nodes[n]->path); + + print_paths (c, name, s); + + g_free (s); + } + g_dbus_node_info_unref (node); + + out: + ; +} + +static void +print_names (GDBusConnection *c, + gboolean include_unique_names) +{ + GVariant *result; + GError *error; + GVariantIter *iter; + gchar *str; + GHashTable *name_set; + GList *keys; + GList *l; + + name_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + error = NULL; + result = g_dbus_connection_invoke_method_sync (c, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ListNames", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); + if (result == NULL) + { + g_printerr (_("Error: %s\n"), error->message); + g_error_free (error); + goto out; + } + if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)"))) + { + g_printerr (_("Error: Result is type `%s', expected `(as)'\n"), g_variant_get_type_string (result)); + g_variant_unref (result); + goto out; + } + g_variant_get (result, "(as)", &iter); + while (g_variant_iter_loop (iter, "s", &str)) + g_hash_table_insert (name_set, g_strdup (str), NULL); + g_variant_iter_free (iter); + g_variant_unref (result); + + error = NULL; + result = g_dbus_connection_invoke_method_sync (c, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ListActivatableNames", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); + if (result == NULL) + { + g_printerr (_("Error: %s\n"), error->message); + g_error_free (error); + goto out; + } + if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)"))) + { + g_printerr (_("Error: Result is type `%s', expected `(as)'\n"), g_variant_get_type_string (result)); + g_variant_unref (result); + goto out; + } + g_variant_get (result, "(as)", &iter); + while (g_variant_iter_loop (iter, "s", &str)) + g_hash_table_insert (name_set, g_strdup (str), NULL); + g_variant_iter_free (iter); + g_variant_unref (result); + + keys = g_hash_table_get_keys (name_set); + keys = g_list_sort (keys, (GCompareFunc) g_strcmp0); + for (l = keys; l != NULL; l = l->next) + { + const gchar *name = l->data; + if (!include_unique_names && g_str_has_prefix (name, ":")) + continue; + + g_print ("%s \n", name); + } + g_list_free (keys); + + out: + g_hash_table_unref (name_set); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean opt_connection_system = FALSE; +static gboolean opt_connection_session = FALSE; +static gchar *opt_connection_address = NULL; + +static const GOptionEntry connection_entries[] = +{ + { "system", 'y', 0, G_OPTION_ARG_NONE, &opt_connection_system, N_("Connect to the system bus"), NULL}, + { "session", 'e', 0, G_OPTION_ARG_NONE, &opt_connection_session, N_("Connect to the session bus"), NULL}, + { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_connection_address, N_("Connect to given D-Bus address"), NULL}, + { NULL } +}; + +static GOptionGroup * +connection_get_group (void) +{ + static GOptionGroup *g; + + g = g_option_group_new ("connection", + N_("Connection Endpoint Options:"), + N_("Options specifying the connection endpoint"), + NULL, + NULL); + g_option_group_add_entries (g, connection_entries); + return g; +} + +static GDBusConnection * +connection_get_dbus_connection (GError **error) +{ + GDBusConnection *c; + + c = NULL; + + /* First, ensure we have exactly one connect */ + if (!opt_connection_system && !opt_connection_session && opt_connection_address == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("No connection endpoint specified")); + goto out; + } + else if ((opt_connection_system && (opt_connection_session || opt_connection_address != NULL)) || + (opt_connection_session && (opt_connection_system || opt_connection_address != NULL)) || + (opt_connection_address != NULL && (opt_connection_system || opt_connection_session))) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Multiple connection endpoints specified")); + goto out; + } + + if (opt_connection_system) + { + c = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); + } + else if (opt_connection_session) + { + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + } + else if (opt_connection_address != NULL) + { + c = g_dbus_connection_new_for_address_sync (opt_connection_address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GCancellable */ + error); + } + + out: + return c; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GPtrArray * +call_helper_get_method_in_signature (GDBusConnection *c, + const gchar *dest, + const gchar *path, + const gchar *interface_name, + const gchar *method_name, + GError **error) +{ + GPtrArray *ret; + GVariant *result; + GDBusNodeInfo *node_info; + const gchar *xml_data; + const GDBusInterfaceInfo *interface_info; + const GDBusMethodInfo *method_info; + guint n; + + ret = NULL; + result = NULL; + node_info = NULL; + + result = g_dbus_connection_invoke_method_sync (c, + dest, + path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + error); + if (result == NULL) + goto out; + + if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)"))) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Error: Result is type `%s', expected `(s)'\n"), + g_variant_get_type_string (result)); + goto out; + } + + g_variant_get (result, "(s)", &xml_data); + node_info = g_dbus_node_info_new_for_xml (xml_data, error); + if (node_info == NULL) + goto out; + + interface_info = g_dbus_node_info_lookup_interface (node_info, interface_name); + if (interface_info == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Warning: According to introspection data, interface `%s' does not exist\n"), + interface_name); + goto out; + } + + method_info = g_dbus_interface_info_lookup_method (interface_info, method_name); + if (method_info == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Warning: According to introspection data, method `%s' does not exist on interface `%s'\n"), + method_name, + interface_name); + goto out; + } + + ret = g_ptr_array_new_with_free_func ((GDestroyNotify) g_variant_type_free); + for (n = 0; method_info->in_args != NULL && method_info->in_args[n] != NULL; n++) + { + g_ptr_array_add (ret, g_variant_type_new (method_info->in_args[n]->signature)); + } + + out: + if (node_info != NULL) + g_dbus_node_info_unref (node_info); + if (result != NULL) + g_variant_unref (result); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GVariant * +_g_variant_parse_me_harder (GVariantType *type, + const gchar *given_str, + GError **error) +{ + GVariant *value; + gchar *s; + guint n; + GString *str; + + str = g_string_new ("\""); + for (n = 0; given_str[n] != '\0'; n++) + { + if (G_UNLIKELY (given_str[n] == '\"')) + g_string_append (str, "\\\""); + else + g_string_append_c (str, given_str[n]); + } + g_string_append_c (str, '"'); + s = g_string_free (str, FALSE); + + value = g_variant_parse (type, + s, + NULL, + NULL, + error); + g_free (s); + + return value; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar *opt_call_dest = NULL; +static gchar *opt_call_object_path = NULL; +static gchar *opt_call_method = NULL; + +static const GOptionEntry call_entries[] = +{ + { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_call_dest, N_("Destination name to invoke method on"), NULL}, + { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_call_object_path, N_("Object path to invoke method on"), NULL}, + { "method", 'm', 0, G_OPTION_ARG_STRING, &opt_call_method, N_("Method and interface name"), NULL}, + { NULL } +}; + +static gboolean +handle_call (gint *argc, + gchar **argv[], + gboolean request_completion, + const gchar *completion_cur, + const gchar *completion_prev) +{ + gint ret; + GOptionContext *o; + gchar *s; + GError *error; + GDBusConnection *c; + GVariant *parameters; + gchar *interface_name; + gchar *method_name; + GVariant *result; + GPtrArray *in_signature_types; + gboolean complete_names; + gboolean complete_paths; + gboolean complete_methods; + GVariantBuilder builder; + guint n; + + ret = FALSE; + c = NULL; + parameters = NULL; + interface_name = NULL; + method_name = NULL; + result = NULL; + in_signature_types = NULL; + + modify_argv0_for_command (argc, argv, "call"); + + o = g_option_context_new (NULL); + g_option_context_set_help_enabled (o, FALSE); + g_option_context_set_summary (o, _("Invoke a method on a remote object.")); + g_option_context_add_main_entries (o, call_entries, NULL /* GETTEXT_PACKAGE*/); + g_option_context_add_group (o, connection_get_group ()); + + complete_names = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0) + { + complete_names = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + complete_paths = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0) + { + complete_paths = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + complete_methods = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--method") == 0) + { + complete_methods = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + if (!g_option_context_parse (o, argc, argv, NULL)) + { + if (!request_completion) + { + s = g_option_context_get_help (o, FALSE, NULL); + g_printerr ("%s", s); + g_free (s); + goto out; + } + } + + error = NULL; + c = connection_get_dbus_connection (&error); + if (c == NULL) + { + if (request_completion) + { + if (g_strcmp0 (completion_prev, "--address") == 0) + { + g_print ("unix:\n" + "tcp:\n" + "nonce-tcp:\n"); + } + else + { + g_print ("--system \n--session \n--address \n"); + } + } + else + { + g_printerr (_("Error connecting: %s\n"), error->message); + g_error_free (error); + } + goto out; + } + + /* validate and complete destination (bus name) */ + if (g_dbus_connection_get_unique_name (c) != NULL) + { + /* this only makes sense on message bus connections */ + if (complete_names) + { + print_names (c, FALSE); + goto out; + } + if (opt_call_dest == NULL) + { + if (request_completion) + g_print ("--dest \n"); + else + g_printerr (_("Error: Destination is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0) + { + print_names (c, g_str_has_prefix (opt_call_dest, ":")); + goto out; + } + } + + /* validate and complete object path */ + if (complete_paths) + { + print_paths (c, opt_call_dest, "/"); + goto out; + } + if (opt_call_object_path == NULL) + { + if (request_completion) + g_print ("--object-path \n"); + else + g_printerr (_("Error: Object path is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0) + { + gchar *p; + s = g_strdup (opt_call_object_path); + p = strrchr (s, '/'); + if (p != NULL) + { + if (p == s) + p++; + *p = '\0'; + } + print_paths (c, opt_call_dest, s); + g_free (s); + goto out; + } + if (!request_completion && !g_variant_is_object_path (opt_call_object_path)) + { + g_printerr (_("Error: %s is not a valid object path\n"), opt_call_object_path); + goto out; + } + + /* validate and complete method (interface + method name) */ + if (complete_methods) + { + print_methods (c, opt_call_dest, opt_call_object_path); + goto out; + } + if (opt_call_method == NULL) + { + if (request_completion) + g_print ("--method \n"); + else + g_printerr (_("Error: Method name is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--method", completion_prev) == 0) + { + print_methods (c, opt_call_dest, opt_call_object_path); + goto out; + } + s = strrchr (opt_call_method, '.'); + if (!request_completion && s == NULL) + { + g_printerr (_("Error: Method name `%s' is invalid\n"), opt_call_method); + goto out; + } + method_name = g_strdup (s + 1); + interface_name = g_strndup (opt_call_method, s - opt_call_method); + + /* All done with completion now */ + if (request_completion) + goto out; + + /* Introspect, for easy conversion - it's not fatal if we can't do this */ + in_signature_types = call_helper_get_method_in_signature (c, + opt_call_dest, + opt_call_object_path, + interface_name, + method_name, + &error); + if (in_signature_types == NULL) + { + //g_printerr ("Error getting introspection data: %s\n", error->message); + g_error_free (error); + error = NULL; + } + + /* Read parameters */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); + for (n = 1; n < (guint) *argc; n++) + { + GVariant *value; + GVariantType *type; + + type = NULL; + if (in_signature_types != NULL) + { + if (n - 1 >= in_signature_types->len) + { + /* Only warn for the first param */ + if (n - 1 == in_signature_types->len) + { + g_printerr ("Warning: Introspection data indicates %d parameters but more was passed\n", + in_signature_types->len); + } + } + else + { + type = in_signature_types->pdata[n - 1]; + } + } + + error = NULL; + value = g_variant_parse (type, + (*argv)[n], + NULL, + NULL, + &error); + if (value == NULL) + { + g_error_free (error); + error = NULL; + value = _g_variant_parse_me_harder (type, (*argv)[n], &error); + if (value == NULL) + { + if (type != NULL) + { + s = g_variant_type_dup_string (type); + g_printerr (_("Error parsing parameter %d of type `%s': %s\n"), + n, + s, + error->message); + g_free (s); + } + else + { + g_printerr (_("Error parsing parameter %d: %s\n"), + n, + error->message); + } + g_error_free (error); + g_variant_builder_clear (&builder); + goto out; + } + } + g_variant_builder_add_value (&builder, value); + } + parameters = g_variant_builder_end (&builder); + + if (parameters != NULL) + parameters = g_variant_ref_sink (parameters); + result = g_dbus_connection_invoke_method_sync (c, + opt_call_dest, + opt_call_object_path, + interface_name, + method_name, + parameters, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + if (result == NULL) + { + g_printerr (_("Error: %s\n"), error->message); + g_error_free (error); + if (in_signature_types != NULL) + { + GString *s; + s = g_string_new (NULL); + for (n = 0; n < in_signature_types->len; n++) + { + GVariantType *type = in_signature_types->pdata[n]; + g_string_append_len (s, + g_variant_type_peek_string (type), + g_variant_type_get_string_length (type)); + } + g_printerr ("(According to introspection data, you need to pass `%s')\n", s->str); + g_string_free (s, TRUE); + } + goto out; + } + + s = g_variant_print (result, TRUE); + g_print ("%s\n", s); + g_free (s); + + ret = TRUE; + + out: + if (in_signature_types != NULL) + g_ptr_array_unref (in_signature_types); + if (result != NULL) + g_variant_unref (result); + if (c != NULL) + g_object_unref (c); + if (parameters != NULL) + g_variant_unref (parameters); + g_free (interface_name); + g_free (method_name); + g_option_context_free (o); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: dump annotations */ + +static void +dump_arg (const GDBusArgInfo *o, + guint indent, + const gchar *direction, + gboolean ignore_indent, + gboolean include_newline) +{ + g_print ("%*s%s%s %s%s", + ignore_indent ? 0 : indent, "", + direction, + o->signature, + o->name, + include_newline ? ",\n" : ""); +} + +static guint +count_args (GDBusArgInfo **args) +{ + guint n; + n = 0; + if (args == NULL) + goto out; + while (args[n] != NULL) + n++; + out: + return n; +} + +static void +dump_method (const GDBusMethodInfo *o, + guint indent) +{ + guint n; + guint m; + guint name_len; + guint total_num_args; + g_print ("%*s%s(", indent, "", o->name); + name_len = strlen (o->name); + total_num_args = count_args (o->in_args) + count_args (o->out_args); + for (n = 0, m = 0; o->in_args != NULL && o->in_args[n] != NULL; n++, m++) + { + gboolean ignore_indent = (m == 0); + gboolean include_newline = (m != total_num_args - 1); + dump_arg (o->in_args[n], + indent + name_len + 1, + "in ", + ignore_indent, + include_newline); + } + for (n = 0; o->out_args != NULL && o->out_args[n] != NULL; n++, m++) + { + gboolean ignore_indent = (m == 0); + gboolean include_newline = (m != total_num_args - 1); + dump_arg (o->out_args[n], + indent + name_len + 1, + "out ", + ignore_indent, + include_newline); + } + g_print (");\n"); +} + +static void +dump_signal (const GDBusSignalInfo *o, + guint indent) +{ + guint n; + guint name_len; + guint total_num_args; + g_print ("%*s%s(", indent, "", o->name); + name_len = strlen (o->name); + total_num_args = count_args (o->args); + for (n = 0; o->args != NULL && o->args[n] != NULL; n++) + { + gboolean ignore_indent = (n == 0); + gboolean include_newline = (n != total_num_args - 1); + dump_arg (o->args[n], + indent + name_len + 1, + "", + ignore_indent, + include_newline); + } + g_print (");\n"); +} + +static void +dump_property (const GDBusPropertyInfo *o, + guint indent, + GVariant *value) +{ + const gchar *access; + if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + access = "readonly"; + else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE) + access = "writeonly"; + else if (o->flags == (G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) + access = "readwrite"; + else + g_assert_not_reached (); + if (value != NULL) + { + gchar *s = g_variant_print (value, FALSE); + g_print ("%*s%s %s %s = %s;\n", indent, "", access, o->signature, o->name, s); + g_free (s); + } + else + { + g_print ("%*s%s %s %s;\n", indent, "", access, o->signature, o->name); + } +} + +static void +dump_interface (GDBusConnection *c, + const gchar *name, + const GDBusInterfaceInfo *o, + guint indent, + const gchar *object_path) +{ + guint n; + GHashTable *properties; + + properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); + + /* Try to get properties */ + if (c != NULL && name != NULL && object_path != NULL) + { + GVariant *result; + result = g_dbus_connection_invoke_method_sync (c, + name, + object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", o->name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, + NULL, + NULL); + if (result != NULL) + { + if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})"))) + { + GVariantIter *iter; + GVariant *item; + g_variant_get (result, + "(a{sv})", + &iter); + while ((item = g_variant_iter_next_value (iter))) + { + const gchar *key; + GVariant *value; + g_variant_get (item, + "{sv}", + &key, + &value); + + g_hash_table_insert (properties, g_strdup (key), g_variant_ref (value)); + } + } + g_variant_unref (result); + } + } + + g_print ("%*sinterface %s {\n", indent, "", o->name); + if (o->methods != NULL) + { + g_print ("%*s methods:\n", indent, ""); + for (n = 0; o->methods[n] != NULL; n++) + dump_method (o->methods[n], indent + 4); + } + if (o->signals != NULL) + { + g_print ("%*s signals:\n", indent, ""); + for (n = 0; o->signals[n] != NULL; n++) + dump_signal (o->signals[n], indent + 4); + } + if (o->properties != NULL) + { + g_print ("%*s properties:\n", indent, ""); + for (n = 0; o->properties[n] != NULL; n++) + { + dump_property (o->properties[n], + indent + 4, + g_hash_table_lookup (properties, (o->properties[n])->name)); + } + } + g_print ("%*s};\n", + indent, ""); + + g_hash_table_unref (properties); +} + +static void +dump_node (GDBusConnection *c, + const gchar *name, + const GDBusNodeInfo *o, + guint indent, + const gchar *object_path) +{ + guint n; + const gchar *object_path_to_print; + + object_path_to_print = object_path; + if (o->path != NULL) + object_path_to_print = o->path; + + g_print ("%*snode %s", indent, "", object_path_to_print != NULL ? object_path_to_print : "(not set)"); + if (o->interfaces != NULL || o->nodes != NULL) + { + g_print (" {\n"); + for (n = 0; o->interfaces != NULL && o->interfaces[n] != NULL; n++) + dump_interface (c, name, o->interfaces[n], indent + 2, object_path); + for (n = 0; o->nodes != NULL && o->nodes[n] != NULL; n++) + dump_node (NULL, NULL, o->nodes[n], indent + 2, NULL); + g_print ("%*s};\n", + indent, ""); + } + else + { + g_print ("\n"); + } +} + +static gchar *opt_introspect_dest = NULL; +static gchar *opt_introspect_object_path = NULL; + +static const GOptionEntry introspect_entries[] = +{ + { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_introspect_dest, N_("Destination name to introspect"), NULL}, + { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_introspect_object_path, N_("Object path to introspect"), NULL}, + { NULL } +}; + +static gboolean +handle_introspect (gint *argc, + gchar **argv[], + gboolean request_completion, + const gchar *completion_cur, + const gchar *completion_prev) +{ + gint ret; + GOptionContext *o; + gchar *s; + GError *error; + GDBusConnection *c; + GVariant *result; + const gchar *xml_data; + GDBusNodeInfo *node; + gboolean complete_names; + gboolean complete_paths; + + ret = FALSE; + c = NULL; + node = NULL; + result = NULL; + + modify_argv0_for_command (argc, argv, "introspect"); + + o = g_option_context_new (NULL); + if (request_completion) + g_option_context_set_ignore_unknown_options (o, TRUE); + g_option_context_set_help_enabled (o, FALSE); + g_option_context_set_summary (o, _("Introspect a remote object.")); + g_option_context_add_main_entries (o, introspect_entries, NULL /* GETTEXT_PACKAGE*/); + g_option_context_add_group (o, connection_get_group ()); + + complete_names = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0) + { + complete_names = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + complete_paths = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0) + { + complete_paths = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + if (!g_option_context_parse (o, argc, argv, NULL)) + { + if (!request_completion) + { + s = g_option_context_get_help (o, FALSE, NULL); + g_printerr ("%s", s); + g_free (s); + goto out; + } + } + + error = NULL; + c = connection_get_dbus_connection (&error); + if (c == NULL) + { + if (request_completion) + { + if (g_strcmp0 (completion_prev, "--address") == 0) + { + g_print ("unix:\n" + "tcp:\n" + "nonce-tcp:\n"); + } + else + { + g_print ("--system \n--session \n--address \n"); + } + } + else + { + g_printerr (_("Error connecting: %s\n"), error->message); + g_error_free (error); + } + goto out; + } + + if (g_dbus_connection_get_unique_name (c) != NULL) + { + if (complete_names) + { + print_names (c, FALSE); + goto out; + } + /* this only makes sense on message bus connections */ + if (opt_introspect_dest == NULL) + { + if (request_completion) + g_print ("--dest \n"); + else + g_printerr (_("Error: Destination is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0) + { + print_names (c, g_str_has_prefix (opt_introspect_dest, ":")); + goto out; + } + } + if (complete_paths) + { + print_paths (c, opt_introspect_dest, "/"); + goto out; + } + if (opt_introspect_object_path == NULL) + { + if (request_completion) + g_print ("--object-path \n"); + else + g_printerr (_("Error: Object path is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0) + { + gchar *p; + s = g_strdup (opt_introspect_object_path); + p = strrchr (s, '/'); + if (p != NULL) + { + if (p == s) + p++; + *p = '\0'; + } + print_paths (c, opt_introspect_dest, s); + g_free (s); + goto out; + } + if (!request_completion && !g_variant_is_object_path (opt_introspect_object_path)) + { + g_printerr (_("Error: %s is not a valid object path\n"), opt_introspect_object_path); + goto out; + } + + /* All done with completion now */ + if (request_completion) + goto out; + + result = g_dbus_connection_invoke_method_sync (c, + opt_introspect_dest, + opt_introspect_object_path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 3000, /* 3 sec */ + NULL, + &error); + if (result == NULL) + { + g_printerr (_("Error: %s\n"), error->message); + g_error_free (error); + goto out; + } + if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)"))) + { + g_printerr (_("Error: Result is type `%s', expected `(s)'\n"), + g_variant_get_type_string (result)); + goto out; + } + g_variant_get (result, "(s)", &xml_data); + + error = NULL; + node = g_dbus_node_info_new_for_xml (xml_data, &error); + if (node == NULL) + { + g_printerr (_("Error parsing introspection XML: %s\n"), error->message); + g_error_free (error); + goto out; + } + + dump_node (c, opt_introspect_dest, node, 0, opt_introspect_object_path); + + ret = TRUE; + + out: + if (node != NULL) + g_dbus_node_info_unref (node); + if (result != NULL) + g_variant_unref (result); + if (c != NULL) + g_object_unref (c); + g_option_context_free (o); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +pick_word_at (const gchar *s, + gint cursor, + gint *out_word_begins_at) +{ + gint begin; + gint end; + + if (s[0] == '\0') + { + if (out_word_begins_at != NULL) + *out_word_begins_at = -1; + return NULL; + } + + if (g_ascii_isspace (s[cursor]) && ((cursor > 0 && g_ascii_isspace(s[cursor-1])) || cursor == 0)) + { + if (out_word_begins_at != NULL) + *out_word_begins_at = cursor; + return g_strdup (""); + } + + while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0) + cursor--; + begin = cursor; + + end = begin; + while (!g_ascii_isspace (s[end]) && s[end] != '\0') + end++; + + if (out_word_begins_at != NULL) + *out_word_begins_at = begin; + + return g_strndup (s + begin, end - begin); +} + +gint +main (gint argc, gchar *argv[]) +{ + gint ret; + const gchar *command; + gboolean request_completion; + gchar *completion_cur; + gchar *completion_prev; + + ret = 1; + completion_cur = NULL; + completion_prev = NULL; + + g_type_init (); + + if (argc < 2) + { + usage (&argc, &argv, FALSE); + goto out; + } + + request_completion = FALSE; + + //completion_debug ("---- argc=%d --------------------------------------------------------", argc); + + again: + command = argv[1]; + if (g_strcmp0 (command, "help") == 0) + { + if (request_completion) + { + /* do nothing */ + } + else + { + usage (&argc, &argv, TRUE); + ret = 0; + } + goto out; + } + else if (g_strcmp0 (command, "call") == 0) + { + if (handle_call (&argc, + &argv, + request_completion, + completion_cur, + completion_prev)) + ret = 0; + goto out; + } + else if (g_strcmp0 (command, "introspect") == 0) + { + if (handle_introspect (&argc, + &argv, + request_completion, + completion_cur, + completion_prev)) + ret = 0; + goto out; + } + else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion) + { + const gchar *completion_line; + gchar **completion_argv; + gint completion_argc; + gint completion_point; + gchar *endp; + gint cur_begin; + + request_completion = TRUE; + + completion_line = argv[2]; + completion_point = strtol (argv[3], &endp, 10); + if (endp == argv[3] || *endp != '\0') + goto out; + +#if 0 + completion_debug ("completion_point=%d", completion_point); + completion_debug ("----"); + completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789"); + completion_debug ("`%s'", completion_line); + completion_debug (" %*s^", + completion_point, ""); + completion_debug ("----"); +#endif + + if (!g_shell_parse_argv (completion_line, + &completion_argc, + &completion_argv, + NULL)) + { + /* it's very possible the command line can't be parsed (for + * example, missing quotes etc) - in that case, we just + * don't autocomplete at all + */ + goto out; + } + + /* compute cur and prev */ + completion_prev = NULL; + completion_cur = pick_word_at (completion_line, completion_point, &cur_begin); + if (cur_begin > 0) + { + gint prev_end; + for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--) + { + if (!g_ascii_isspace (completion_line[prev_end])) + { + completion_prev = pick_word_at (completion_line, prev_end, NULL); + break; + } + } + } +#if 0 + completion_debug (" cur=`%s'", completion_cur); + completion_debug ("prev=`%s'", completion_prev); +#endif + + argc = completion_argc; + argv = completion_argv; + + ret = 0; + + goto again; + } + else + { + if (request_completion) + { + g_print ("help \ncall \nintrospect \n"); + ret = 0; + goto out; + } + else + { + g_printerr ("Unknown command `%s'\n", command); + usage (&argc, &argv, FALSE); + goto out; + } + } + + out: + g_free (completion_cur); + g_free (completion_prev); + return ret; +} diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c new file mode 100644 index 000000000..e50e73781 --- /dev/null +++ b/gio/gdbusaddress.c @@ -0,0 +1,1004 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include + +#include "gdbusutils.h" +#include "gdbusaddress.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" +#include "gdbusprivate.h" + +#ifdef G_OS_UNIX +#include +#endif + +/** + * SECTION:gdbusaddress + * @title: D-Bus Addresses + * @short_description: D-Bus connection endpoints + * @include: gdbus/gdbus.h + * + * Routines for working with D-Bus addresses. + */ + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_is_address: + * @string: A string. + * + * Checks if @string is a D-Bus address. + * + * This doesn't check if @string is actually supported by #GDBusServer + * or #GDBusConnection - use g_dbus_is_supported_address() to do more + * checks. + * + * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise. + */ +gboolean +g_dbus_is_address (const gchar *string) +{ + guint n; + gchar **a; + gboolean ret; + + ret = FALSE; + + g_return_val_if_fail (string != NULL, FALSE); + + a = g_strsplit (string, ";", 0); + for (n = 0; a[n] != NULL; n++) + { + if (!_g_dbus_address_parse_entry (a[n], + NULL, + NULL, + NULL)) + goto out; + } + + ret = TRUE; + + out: + g_strfreev (a); + return ret; +} + +static gboolean +is_valid_unix (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *path; + const gchar *tmpdir; + const gchar *abstract; + + ret = FALSE; + keys = NULL; + path = NULL; + tmpdir = NULL; + abstract = NULL; + + keys = g_hash_table_get_keys (key_value_pairs); + for (l = keys; l != NULL; l = l->next) + { + const gchar *key = l->data; + if (g_strcmp0 (key, "path") == 0) + path = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "tmpdir") == 0) + tmpdir = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "abstract") == 0) + abstract = g_hash_table_lookup (key_value_pairs, key); + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported key `%s' in address entry `%s'"), + key, + address_entry); + goto out; + } + } + + if (path != NULL) + { + if (tmpdir != NULL || abstract != NULL) + goto meaningless; + /* TODO: validate path */ + } + else if (tmpdir != NULL) + { + if (path != NULL || abstract != NULL) + goto meaningless; + /* TODO: validate tmpdir */ + } + else if (abstract != NULL) + { + if (path != NULL || tmpdir != NULL) + goto meaningless; + /* TODO: validate abstract */ + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys"), + address_entry); + goto out; + } + + + ret= TRUE; + goto out; + + meaningless: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Meaningless key/value pair combination in address entry `%s'"), + address_entry); + + out: + g_list_free (keys); + + return ret; +} + +static gboolean +is_valid_nonce_tcp (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *host; + const gchar *port; + const gchar *family; + const gchar *nonce_file; + gint port_num; + gchar *endp; + + ret = FALSE; + keys = NULL; + host = NULL; + port = NULL; + family = NULL; + nonce_file = NULL; + + keys = g_hash_table_get_keys (key_value_pairs); + for (l = keys; l != NULL; l = l->next) + { + const gchar *key = l->data; + if (g_strcmp0 (key, "host") == 0) + host = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "port") == 0) + port = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "family") == 0) + family = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "noncefile") == 0) + nonce_file = g_hash_table_lookup (key_value_pairs, key); + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported key `%s' in address entry `%s'"), + key, + address_entry); + goto out; + } + } + + if (port != NULL) + { + port_num = strtol (port, &endp, 10); + if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the port attribute is malformed"), + address_entry); + goto out; + } + } + + if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the family attribute is malformed"), + address_entry); + goto out; + } + + ret= TRUE; + + out: + g_list_free (keys); + + return ret; +} + +static gboolean +is_valid_tcp (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *host; + const gchar *port; + const gchar *family; + gint port_num; + gchar *endp; + + ret = FALSE; + keys = NULL; + host = NULL; + port = NULL; + family = NULL; + + keys = g_hash_table_get_keys (key_value_pairs); + for (l = keys; l != NULL; l = l->next) + { + const gchar *key = l->data; + if (g_strcmp0 (key, "host") == 0) + host = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "port") == 0) + port = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "family") == 0) + family = g_hash_table_lookup (key_value_pairs, key); + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported key `%s' in address entry `%s'"), + key, + address_entry); + goto out; + } + } + + if (port != NULL) + { + port_num = strtol (port, &endp, 10); + if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the port attribute is malformed"), + address_entry); + goto out; + } + } + + if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the family attribute is malformed"), + address_entry); + goto out; + } + + ret= TRUE; + + out: + g_list_free (keys); + + return ret; +} + +/** + * g_dbus_is_supported_address: + * @string: A string. + * @error: Return location for error or %NULL. + * + * Like g_dbus_is_address() but also checks if the library suppors the + * transports in @string and that key/value pairs for each transport + * are valid. + * + * Returns: %TRUE if @string is a valid D-Bus address that is + * supported by this library, %FALSE if @error is set. + */ +gboolean +g_dbus_is_supported_address (const gchar *string, + GError **error) +{ + guint n; + gchar **a; + gboolean ret; + + ret = FALSE; + + g_return_val_if_fail (string != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + a = g_strsplit (string, ";", 0); + for (n = 0; a[n] != NULL; n++) + { + gchar *transport_name; + GHashTable *key_value_pairs; + gboolean supported; + + if (!_g_dbus_address_parse_entry (a[n], + &transport_name, + &key_value_pairs, + error)) + goto out; + + supported = FALSE; + if (g_strcmp0 (transport_name, "unix") == 0) + supported = is_valid_unix (a[n], key_value_pairs, error); + else if (g_strcmp0 (transport_name, "tcp") == 0) + supported = is_valid_tcp (a[n], key_value_pairs, error); + else if (g_strcmp0 (transport_name, "nonce-tcp") == 0) + supported = is_valid_nonce_tcp (a[n], key_value_pairs, error); + + g_free (transport_name); + g_hash_table_unref (key_value_pairs); + + if (!supported) + goto out; + } + + ret = TRUE; + + out: + g_strfreev (a); + + g_assert (ret || (!ret && (error == NULL || *error != NULL))); + + return ret; +} + +gboolean +_g_dbus_address_parse_entry (const gchar *address_entry, + gchar **out_transport_name, + GHashTable **out_key_value_pairs, + GError **error) +{ + gboolean ret; + GHashTable *key_value_pairs; + gchar *transport_name; + gchar **kv_pairs; + const gchar *s; + guint n; + + ret = FALSE; + kv_pairs = NULL; + transport_name = NULL; + key_value_pairs = NULL; + + s = strchr (address_entry, ':'); + if (s == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Address element `%s', does not contain a colon (:)"), + address_entry); + goto out; + } + + transport_name = g_strndup (address_entry, s - address_entry); + key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + kv_pairs = g_strsplit (s + 1, ",", 0); + for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++) + { + const gchar *kv_pair = kv_pairs[n]; + gchar *key; + gchar *value; + + s = strchr (kv_pair, '='); + if (s == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"), + n, + kv_pair, + address_entry); + goto out; + } + + /* TODO: actually validate that no illegal characters are present before and after then '=' sign */ + key = g_uri_unescape_segment (kv_pair, s, NULL); + value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL); + g_hash_table_insert (key_value_pairs, key, value); + } + + ret = TRUE; + +out: + g_strfreev (kv_pairs); + if (ret) + { + if (out_transport_name != NULL) + *out_transport_name = transport_name; + else + g_free (transport_name); + if (out_key_value_pairs != NULL) + *out_key_value_pairs = key_value_pairs; + else if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); + } + else + { + g_free (transport_name); + if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); + } + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: Declare an extension point called GDBusTransport (or similar) + * and move code below to extensions implementing said extension + * point. That way we can implement a D-Bus transport over X11 without + * making libgio link to libX11... + */ +static GIOStream * +g_dbus_address_connect (const gchar *address_entry, + const gchar *transport_name, + GHashTable *key_value_pairs, + GCancellable *cancellable, + GError **error) +{ + GIOStream *ret; + GSocketConnectable *connectable; + const gchar *nonce_file; + + connectable = NULL; + ret = NULL; + nonce_file = NULL; + + if (FALSE) + { + } +#ifdef G_OS_UNIX + else if (g_strcmp0 (transport_name, "unix") == 0) + { + const gchar *path; + const gchar *abstract; + path = g_hash_table_lookup (key_value_pairs, "path"); + abstract = g_hash_table_lookup (key_value_pairs, "abstract"); + if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the unix transport requires exactly one of the " + "keys `path' or `abstract' to be set"), + address_entry); + } + else if (path != NULL) + { + connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path)); + } + else if (abstract != NULL) + { + connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract, + -1, + G_UNIX_SOCKET_ADDRESS_ABSTRACT)); + } + else + { + g_assert_not_reached (); + } + } +#endif + else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0) + { + const gchar *s; + const gchar *host; + guint port; + gchar *endp; + gboolean is_nonce; + + is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0); + + host = g_hash_table_lookup (key_value_pairs, "host"); + if (host == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the host attribute is missing or malformed"), + address_entry); + goto out; + } + + s = g_hash_table_lookup (key_value_pairs, "port"); + if (s == NULL) + s = "0"; + port = strtol (s, &endp, 10); + if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the port attribute is missing or malformed"), + address_entry); + goto out; + } + + + if (is_nonce) + { + nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile"); + if (nonce_file == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the noncefile attribute is missing or malformed"), + address_entry); + goto out; + } + } + + /* TODO: deal with family */ + connectable = g_network_address_new (host, port); + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unknown or unsupported transport `%s' for address `%s'"), + transport_name, + address_entry); + } + + if (connectable != NULL) + { + GSocketClient *client; + GSocketConnection *connection; + + g_assert (ret == NULL); + client = g_socket_client_new (); + connection = g_socket_client_connect (client, + connectable, + cancellable, + error); + g_object_unref (connectable); + g_object_unref (client); + if (connection == NULL) + goto out; + + ret = G_IO_STREAM (connection); + + if (nonce_file != NULL) + { + gchar *nonce_contents; + gsize nonce_length; + + /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */ + if (!g_file_get_contents (nonce_file, + &nonce_contents, + &nonce_length, + error)) + { + g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file); + g_object_unref (ret); + ret = NULL; + goto out; + } + + if (nonce_length != 16) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("The nonce-file `%s' was %" G_GSIZE_FORMAT " bytes. Expected 16 bytes."), + nonce_file, + nonce_length); + g_free (nonce_contents); + g_object_unref (ret); + ret = NULL; + goto out; + } + + if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret), + nonce_contents, + nonce_length, + NULL, + cancellable, + error)) + { + g_prefix_error (error, _("Error write contents of nonce file `%s' to stream:"), nonce_file); + g_object_unref (ret); + ret = NULL; + g_free (nonce_contents); + goto out; + } + g_free (nonce_contents); + } + } + + out: + + return ret; +} + +static GIOStream * +g_dbus_address_try_connect_one (const gchar *address_entry, + gchar **out_guid, + GCancellable *cancellable, + GError **error) +{ + GIOStream *ret; + GHashTable *key_value_pairs; + gchar *transport_name; + const gchar *guid; + + ret = NULL; + transport_name = NULL; + key_value_pairs = NULL; + + if (!_g_dbus_address_parse_entry (address_entry, + &transport_name, + &key_value_pairs, + error)) + goto out; + + ret = g_dbus_address_connect (address_entry, + transport_name, + key_value_pairs, + cancellable, + error); + if (ret == NULL) + goto out; + + /* TODO: validate that guid is of correct format */ + guid = g_hash_table_lookup (key_value_pairs, "guid"); + if (guid != NULL && out_guid != NULL) + *out_guid = g_strdup (guid); + +out: + g_free (transport_name); + if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); + return ret; +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct { + gchar *address; + GIOStream *stream; + gchar *guid; +} GetStreamData; + +static void +get_stream_data_free (GetStreamData *data) +{ + g_free (data->address); + if (data->stream != NULL) + g_object_unref (data->stream); + g_free (data->guid); + g_free (data); +} + +static void +get_stream_thread_func (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + GetStreamData *data; + GError *error; + + data = g_simple_async_result_get_op_res_gpointer (res); + + error = NULL; + data->stream = g_dbus_address_get_stream_sync (data->address, + &data->guid, + cancellable, + &error); + if (data->stream == NULL) + { + g_simple_async_result_set_from_error (res, error); + g_error_free (error); + } +} + +/** + * g_dbus_address_get_stream: + * @address: A valid D-Bus address. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: Data to pass to @callback. + * + * Asynchronously connects to an endpoint specified by @address and + * sets up the connection so it is in a state to run the client-side + * of the D-Bus authentication conversation. + * + * When the operation is finished, @callback will be invoked. You can + * then call g_dbus_address_get_stream_finish() to get the result of + * the operation. + * + * This is an asynchronous failable function. See + * g_dbus_address_get_stream_sync() for the synchronous version. + */ +void +g_dbus_address_get_stream (const gchar *address, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + GetStreamData *data; + + g_return_if_fail (address != NULL); + + res = g_simple_async_result_new (NULL, + callback, + user_data, + g_dbus_address_get_stream); + data = g_new0 (GetStreamData, 1); + data->address = g_strdup (address); + g_simple_async_result_set_op_res_gpointer (res, + data, + (GDestroyNotify) get_stream_data_free); + g_simple_async_result_run_in_thread (res, + get_stream_thread_func, + G_PRIORITY_DEFAULT, + cancellable); + g_object_unref (res); +} + +/** + * g_dbus_address_get_stream_finish: + * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream(). + * @out_guid: %NULL or return location to store the GUID extracted from @address, if any. + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_address_get_stream(). + * + * Returns: A #GIOStream or %NULL if @error is set. + */ +GIOStream * +g_dbus_address_get_stream_finish (GAsyncResult *res, + gchar **out_guid, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GetStreamData *data; + GIOStream *ret; + + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream); + + ret = NULL; + + data = g_simple_async_result_get_op_res_gpointer (simple); + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + ret = g_object_ref (data->stream); + if (out_guid != NULL) + *out_guid = g_strdup (data->guid); + + out: + return ret; +} + +/** + * g_dbus_address_get_stream_sync: + * @address: A valid D-Bus address. + * @out_guid: %NULL or return location to store the GUID extracted from @address, if any. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously connects to an endpoint specified by @address and + * sets up the connection so it is in a state to run the client-side + * of the D-Bus authentication conversation. + * + * This is a synchronous failable function. See + * g_dbus_address_get_stream() for the asynchronous version. + * + * Returns: A #GIOStream or %NULL if @error is set. + */ +GIOStream * +g_dbus_address_get_stream_sync (const gchar *address, + gchar **out_guid, + GCancellable *cancellable, + GError **error) +{ + GIOStream *ret; + gchar **addr_array; + guint n; + GError *last_error; + + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; + last_error = NULL; + + addr_array = g_strsplit (address, ";", 0); + last_error = NULL; + for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++) + { + const gchar *addr = addr_array[n]; + GError *this_error; + this_error = NULL; + ret = g_dbus_address_try_connect_one (addr, + out_guid, + cancellable, + &this_error); + if (ret != NULL) + { + goto out; + } + else + { + g_assert (this_error != NULL); + if (last_error != NULL) + g_error_free (last_error); + last_error = this_error; + } + } + + out: + if (ret != NULL) + { + if (last_error != NULL) + g_error_free (last_error); + } + else + { + g_assert (last_error != NULL); + g_propagate_error (error, last_error); + } + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: implement for UNIX, Win32 and OS X */ +static gchar * +get_session_address_platform_specific (void) +{ + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_address_get_for_bus_sync: + * @bus_type: A #GBusType. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously looks up the D-Bus address for the well-known message + * bus instance specified by @bus_type. This may involve using various + * platform specific mechanisms. + * + * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set. + */ +gchar * +g_dbus_address_get_for_bus_sync (GBusType bus_type, + GCancellable *cancellable, + GError **error) +{ + gchar *ret; + const gchar *starter_bus; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; + + switch (bus_type) + { + case G_BUS_TYPE_SYSTEM: + ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS")); + if (ret == NULL) + { + ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket"); + } + break; + + case G_BUS_TYPE_SESSION: + ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")); + if (ret == NULL) + { + ret = get_session_address_platform_specific (); + if (ret == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Cannot determine session bus address (TODO: run dbus-launch to find out)")); + } + } + break; + + case G_BUS_TYPE_STARTER: + starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE"); + if (g_strcmp0 (starter_bus, "session") == 0) + { + ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, error); + goto out; + } + else if (g_strcmp0 (starter_bus, "system") == 0) + { + ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, error); + goto out; + } + else + { + if (starter_bus != NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable" + " - unknown value `%s'"), + starter_bus); + } + else + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment " + "variable is not set")); + } + } + break; + + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unknown bus type %d"), + bus_type); + break; + } + + out: + return ret; +} diff --git a/gio/gdbusaddress.h b/gio/gdbusaddress.h new file mode 100644 index 000000000..feac1a56e --- /dev/null +++ b/gio/gdbusaddress.h @@ -0,0 +1,54 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_ADDRESS_H__ +#define __G_DBUS_ADDRESS_H__ + +#include + +G_BEGIN_DECLS + +gboolean g_dbus_is_address (const gchar *string); +gboolean g_dbus_is_supported_address (const gchar *string, + GError **error); + +void g_dbus_address_get_stream (const gchar *address, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GIOStream *g_dbus_address_get_stream_finish (GAsyncResult *res, + gchar **out_guid, + GError **error); + +GIOStream *g_dbus_address_get_stream_sync (const gchar *address, + gchar **out_guid, + GCancellable *cancellable, + GError **error); + +gchar *g_dbus_address_get_for_bus_sync (GBusType bus_type, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* __G_DBUS_ADDRESS_H__ */ diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c new file mode 100644 index 000000000..850db9aa6 --- /dev/null +++ b/gio/gdbusauth.c @@ -0,0 +1,1538 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusauth.h" +#include "gdbusauthmechanismanon.h" +#include "gdbusauthmechanismexternal.h" +#include "gdbusauthmechanismsha1.h" + +#include "gdbusauthobserver.h" + +#include "gdbuserror.h" +#include "gdbusutils.h" +#include "gioenumtypes.h" +#include "gcredentials.h" +#include "gdbusprivate.h" + +#ifdef G_OS_UNIX +#include +#include "gunixcredentialsmessage.h" +#include +#include +#endif + +#define DEBUG_ENABLED 1 + +static void +debug_print (const gchar *message, ...) +{ +#if DEBUG_ENABLED + if (G_UNLIKELY (_g_dbus_debug_authentication ())) + { + gchar *s; + GString *str; + va_list var_args; + guint n; + + va_start (var_args, message); + s = g_strdup_vprintf (message, var_args); + va_end (var_args); + + str = g_string_new (NULL); + for (n = 0; s[n] != '\0'; n++) + { + if (G_UNLIKELY (s[n] == '\r')) + g_string_append (str, "\\r"); + else if (G_UNLIKELY (s[n] == '\n')) + g_string_append (str, "\\n"); + else + g_string_append_c (str, s[n]); + } + g_print ("GDBus-debug:Auth: %s\n", str->str); + g_string_free (str, TRUE); + g_free (s); + } +#endif +} + + +/* ---------------------------------------------------------------------------------------------------- */ +/* TODO: move to gio */ + +/** + * g_unix_connection_send_credentials: + * @connection: A #GUnixConnection. + * @credentials: A #GCredentials to send. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Passes the credentials stored in @credentials to the recieving side + * of the connection. The recieving end has to call + * g_unix_connection_receive_credentials() (or similar) to accept the + * credentials. + * + * The credentials which the sender specifies are checked by the + * kernel. A process with effective user ID 0 is allowed to specify + * values that do not match its own. This means that the credentials + * can be used to authenticate other connections. + * + * As well as sending the credentials this also writes a single NUL + * byte to the stream, as this is required for credentials passing to + * work on some implementations. + * + * Returns: %TRUE on success, %FALSE if @error is set. + * + * Since: 2.26 + */ +static gboolean +g_unix_connection_send_credentials (GUnixConnection *connection, + GCredentials *credentials, + GCancellable *cancellable, + GError **error) +{ + GSocketControlMessage *scm; + GSocket *socket; + gboolean ret; + GOutputVector vector; + guchar nul_byte[1] = {'\0'}; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + + vector.buffer = &nul_byte; + vector.size = 1; + scm = g_unix_credentials_message_new_with_credentials (credentials); + g_object_get (connection, "socket", &socket, NULL); + if (g_socket_send_message (socket, + NULL, /* address */ + &vector, + 1, + &scm, + 1, + G_SOCKET_MSG_NONE, + cancellable, + error) != 1) + { + g_prefix_error (error, _("Error sending credentials: ")); + goto out; + } + + ret = TRUE; + + out: + g_object_unref (socket); + g_object_unref (scm); + return ret; +} + +/** + * g_unix_connection_receive_credentials: + * @connection: A #GUnixConnection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Receives credentials from the sending end of the connection. The + * sending end has to call g_unix_connection_send_credentials() (or + * similar) for this to work. + * + * As well as reading the credentials this also reads (and discards) a + * single byte from the stream, as this is required for credentials + * passing to work on some implementations. + * + * Returns: Received credentials on success (free with + * g_object_unref()), %NULL if @error is set. + * + * Since: 2.26 + */ +static GCredentials * +g_unix_connection_receive_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GCredentials *ret; + GSocketControlMessage **scms; + gint nscm; + GSocket *socket; + gint n; + volatile GType credentials_message_gtype; + gssize num_bytes_read; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; + scms = NULL; + + g_object_get (connection, "socket", &socket, NULL); + +#if 1 + /* TODO: Move this to gsocket.c... */ + { + int opt_val = 1; + if (setsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + sizeof opt_val) != 0) + { + g_warning ("boo, error setting SO_PASSCRED: %m"); + } + } +#endif + + /* ensure the type of GUnixCredentialsMessage has been registered with the type system */ + credentials_message_gtype = G_TYPE_UNIX_CREDENTIALS_MESSAGE; + num_bytes_read = g_socket_receive_message (socket, + NULL, /* GSocketAddress **address */ + NULL, + 0, + &scms, + &nscm, + NULL, + cancellable, + error); + if (num_bytes_read != 1) + { + /* Handle situation where g_socket_receive_message() returns + * 0 bytes and not setting @error + */ + if (num_bytes_read == 0 && error != NULL && *error == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting to read a single byte for receiving credentials but read zero bytes")); + } + goto out; + } + + if (nscm != 1) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting 1 control message, got %d"), + nscm); + goto out; + } + + if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0])) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected type of ancillary data")); + goto out; + } + + ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0])); + g_object_ref (ret); + + out: + if (scms != NULL) + { + for (n = 0; n < nscm; n++) + g_object_unref (scms[n]); + g_free (scms); + } + g_object_unref (socket); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + const gchar *name; + gint priority; + GType gtype; +} Mechanism; + +static void mechanism_free (Mechanism *m); + +struct _GDBusAuthPrivate +{ + GIOStream *stream; + + /* A list of available Mechanism, sorted according to priority */ + GList *available_mechanisms; +}; + +enum +{ + PROP_0, + PROP_STREAM +}; + +G_DEFINE_TYPE (GDBusAuth, _g_dbus_auth, G_TYPE_OBJECT); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +_g_dbus_auth_finalize (GObject *object) +{ + GDBusAuth *auth = G_DBUS_AUTH (object); + + if (auth->priv->stream != NULL) + g_object_unref (auth->priv->stream); + g_list_foreach (auth->priv->available_mechanisms, (GFunc) mechanism_free, NULL); + g_list_free (auth->priv->available_mechanisms); + + if (G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize != NULL) + G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize (object); +} + +static void +_g_dbus_auth_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusAuth *auth = G_DBUS_AUTH (object); + + switch (prop_id) + { + case PROP_STREAM: + g_value_set_object (value, auth->priv->stream); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_g_dbus_auth_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusAuth *auth = G_DBUS_AUTH (object); + + switch (prop_id) + { + case PROP_STREAM: + auth->priv->stream = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_g_dbus_auth_class_init (GDBusAuthClass *klass) +{ + GObjectClass *gobject_class; + + g_type_class_add_private (klass, sizeof (GDBusAuthPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->get_property = _g_dbus_auth_get_property; + gobject_class->set_property = _g_dbus_auth_set_property; + gobject_class->finalize = _g_dbus_auth_finalize; + + g_object_class_install_property (gobject_class, + PROP_STREAM, + g_param_spec_object ("stream", + _("IO Stream"), + _("The underlying GIOStream used for I/O"), + G_TYPE_IO_STREAM, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); +} + +static void +mechanism_free (Mechanism *m) +{ + g_free (m); +} + +static void +add_mechanism (GDBusAuth *auth, + GType mechanism_type) +{ + Mechanism *m; + + m = g_new0 (Mechanism, 1); + m->name = _g_dbus_auth_mechanism_get_name (mechanism_type); + m->priority = _g_dbus_auth_mechanism_get_priority (mechanism_type); + m->gtype = mechanism_type; + + auth->priv->available_mechanisms = g_list_prepend (auth->priv->available_mechanisms, m); +} + +static gint +mech_compare_func (Mechanism *a, Mechanism *b) +{ + gint ret; + /* ensure deterministic order */ + ret = b->priority - a->priority; + if (ret == 0) + ret = g_strcmp0 (b->name, a->name); + return ret; +} + +static void +_g_dbus_auth_init (GDBusAuth *auth) +{ + auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, G_TYPE_DBUS_AUTH, GDBusAuthPrivate); + + /* TODO: trawl extension points */ + add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_ANON); + add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_SHA1); + add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL); + + auth->priv->available_mechanisms = g_list_sort (auth->priv->available_mechanisms, + (GCompareFunc) mech_compare_func); +} + +static GType +find_mech_by_name (GDBusAuth *auth, + const gchar *name) +{ + GType ret; + GList *l; + + ret = (GType) 0; + + for (l = auth->priv->available_mechanisms; l != NULL; l = l->next) + { + Mechanism *m = l->data; + if (g_strcmp0 (name, m->name) == 0) + { + ret = m->gtype; + goto out; + } + } + + out: + return ret; +} + +GDBusAuth * +_g_dbus_auth_new (GIOStream *stream) +{ + return g_object_new (G_TYPE_DBUS_AUTH, + "stream", stream, + NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* like g_data_input_stream_read_line() but sets error if there's no content to read */ +static gchar * +_my_g_data_input_stream_read_line (GDataInputStream *dis, + gsize *out_line_length, + GCancellable *cancellable, + GError **error) +{ + gchar *ret; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = g_data_input_stream_read_line (dis, + out_line_length, + cancellable, + error); + if (ret == NULL && error != NULL && *error == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected lack of content trying to read a line")); + } + + return ret; +} + +/* This function is to avoid situations like this + * + * BEGIN\r\nl\0\0\1... + * + * e.g. where we read into the first D-Bus message while waiting for + * the final line from the client (TODO: file bug against gio for + * this) + */ +static gchar * +_my_g_input_stream_read_line_safe (GInputStream *i, + gsize *out_line_length, + GCancellable *cancellable, + GError **error) +{ + GString *str; + gchar c; + gssize num_read; + gboolean last_was_cr; + + str = g_string_new (NULL); + + last_was_cr = FALSE; + while (TRUE) + { + num_read = g_input_stream_read (i, + &c, + 1, + cancellable, + error); + if (num_read == -1) + goto fail; + if (num_read == 0) + { + if (error != NULL && *error == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected lack of content trying to (safely) read a line")); + } + goto fail; + } + + g_string_append_c (str, (gint) c); + if (last_was_cr) + { + if (c == 0x0a) + { + g_assert (str->len >= 2); + g_string_set_size (str, str->len - 2); + goto out; + } + } + last_was_cr = (c == 0x0d); + } + + out: + if (out_line_length != NULL) + *out_line_length = str->len; + return g_string_free (str, FALSE); + + fail: + g_assert (error == NULL || *error != NULL); + g_string_free (str, TRUE); + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +append_nibble (GString *s, gint val) +{ + g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val)); +} + +static gchar * +hexdecode (const gchar *str, + gsize *out_len, + GError **error) +{ + gchar *ret; + GString *s; + guint n; + + ret = NULL; + s = g_string_new (NULL); + + for (n = 0; str[n] != '\0'; n += 2) + { + gint upper_nibble; + gint lower_nibble; + guint value; + + upper_nibble = g_ascii_xdigit_value (str[n]); + lower_nibble = g_ascii_xdigit_value (str[n + 1]); + if (upper_nibble == -1 || lower_nibble == -1) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Error hexdecoding string `%s' around position %d", + str, n); + goto out; + } + value = (upper_nibble<<4) | lower_nibble; + g_string_append_c (s, value); + } + + ret = g_string_free (s, FALSE); + s = NULL; + + out: + if (s != NULL) + g_string_free (s, TRUE); + return ret; +} + +/* TODO: take len */ +static gchar * +hexencode (const gchar *str) +{ + guint n; + GString *s; + + s = g_string_new (NULL); + for (n = 0; str[n] != '\0'; n++) + { + gint val; + gint upper_nibble; + gint lower_nibble; + + val = ((const guchar *) str)[n]; + upper_nibble = val >> 4; + lower_nibble = val & 0x0f; + + append_nibble (s, upper_nibble); + append_nibble (s, lower_nibble); + } + + return g_string_free (s, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanism * +client_choose_mech_and_send_initial_response (GDBusAuth *auth, + GCredentials *credentials_that_were_sent, + const gchar* const *supported_auth_mechs, + GPtrArray *attempted_auth_mechs, + GDataOutputStream *dos, + GCancellable *cancellable, + GError **error) +{ + GDBusAuthMechanism *mech; + GType auth_mech_to_use_gtype; + guint n; + guint m; + gchar *initial_response; + gsize initial_response_len; + gchar *encoded; + gchar *s; + + again: + mech = NULL; + + debug_print ("CLIENT: Trying to choose mechanism"); + + /* find an authentication mechanism to try, if any */ + auth_mech_to_use_gtype = (GType) 0; + for (n = 0; supported_auth_mechs[n] != NULL; n++) + { + gboolean attempted_already; + attempted_already = FALSE; + for (m = 0; m < attempted_auth_mechs->len; m++) + { + if (g_strcmp0 (supported_auth_mechs[n], attempted_auth_mechs->pdata[m]) == 0) + { + attempted_already = TRUE; + break; + } + } + if (!attempted_already) + { + auth_mech_to_use_gtype = find_mech_by_name (auth, supported_auth_mechs[n]); + if (auth_mech_to_use_gtype != (GType) 0) + break; + } + } + + if (auth_mech_to_use_gtype == (GType) 0) + { + guint n; + gchar *available; + GString *tried_str; + + debug_print ("CLIENT: Exhausted all available mechanisms"); + + available = g_strjoinv (", ", (gchar **) supported_auth_mechs); + + tried_str = g_string_new (NULL); + for (n = 0; n < attempted_auth_mechs->len; n++) + { + if (n > 0) + g_string_append (tried_str, ", "); + g_string_append (tried_str, attempted_auth_mechs->pdata[n]); + } + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Exhausted all available authentication mechanisms (tried: %s) (available: %s)"), + tried_str->str, + available); + g_string_free (tried_str, TRUE); + g_free (available); + goto out; + } + + /* OK, decided on a mechanism - let's do this thing */ + mech = g_object_new (auth_mech_to_use_gtype, + "stream", auth->priv->stream, + "credentials", credentials_that_were_sent, + NULL); + debug_print ("CLIENT: Trying mechanism `%s'", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); + g_ptr_array_add (attempted_auth_mechs, (gpointer) _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); + + /* the auth mechanism may not be supported + * (for example, EXTERNAL only works if credentials were exchanged) + */ + if (!_g_dbus_auth_mechanism_is_supported (mech)) + { + debug_print ("CLIENT: Mechanism `%s' says it is not supported", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); + g_object_unref (mech); + mech = NULL; + goto again; + } + + initial_response_len = -1; + initial_response = _g_dbus_auth_mechanism_client_initiate (mech, + &initial_response_len); +#if 0 + g_printerr ("using auth mechanism with name `%s' of type `%s' with initial response `%s'\n", + _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype), + g_type_name (G_TYPE_FROM_INSTANCE (mech)), + initial_response); +#endif + if (initial_response != NULL) + { + //g_printerr ("initial_response = `%s'\n", initial_response); + encoded = hexencode (initial_response); + s = g_strdup_printf ("AUTH %s %s\r\n", + _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype), + encoded); + g_free (initial_response); + g_free (encoded); + } + else + { + s = g_strdup_printf ("AUTH %s\r\n", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); + } + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_object_unref (mech); + mech = NULL; + g_free (s); + goto out; + } + g_free (s); + + out: + return mech; +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef enum +{ + CLIENT_STATE_WAITING_FOR_DATA, + CLIENT_STATE_WAITING_FOR_OK, + CLIENT_STATE_WAITING_FOR_REJECT, + CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD +} ClientState; + +gchar * +_g_dbus_auth_run_client (GDBusAuth *auth, + GDBusCapabilityFlags offered_capabilities, + GDBusCapabilityFlags *out_negotiated_capabilities, + GCancellable *cancellable, + GError **error) +{ + gchar *s; + GDataInputStream *dis; + GDataOutputStream *dos; + GCredentials *credentials; + gchar *ret_guid; + gchar *line; + gsize line_length; + gchar **supported_auth_mechs; + GPtrArray *attempted_auth_mechs; + GDBusAuthMechanism *mech; + ClientState state; + GDBusCapabilityFlags negotiated_capabilities; + + debug_print ("CLIENT: initiating"); + + ret_guid = NULL; + supported_auth_mechs = NULL; + attempted_auth_mechs = g_ptr_array_new (); + mech = NULL; + negotiated_capabilities = 0; + credentials = NULL; + + dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream))); + dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream))); + + g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); + +#ifdef G_OS_UNIX + if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ()) + { + credentials = g_credentials_new_for_process (); + if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream), + credentials, + cancellable, + error)) + goto out; + } + else + { + if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error)) + goto out; + } +#else + if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error)) + goto out; +#endif + + if (credentials != NULL) + { + if (G_UNLIKELY (_g_dbus_debug_authentication ())) + { + s = g_credentials_to_string (credentials); + debug_print ("CLIENT: sent credentials `%s'", s); + g_free (s); + } + } + else + { + debug_print ("CLIENT: didn't send any credentials"); + } + + /* TODO: to reduce rountrips, try to pick an auth mechanism to start with */ + + /* Get list of supported authentication mechanisms */ + s = "AUTH\r\n"; + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + state = CLIENT_STATE_WAITING_FOR_REJECT; + + while (TRUE) + { + switch (state) + { + case CLIENT_STATE_WAITING_FOR_REJECT: + debug_print ("CLIENT: WaitingForReject"); + line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); + if (line == NULL) + goto out; + debug_print ("CLIENT: WaitingForReject, read '%s'", line); + foobar: + if (!g_str_has_prefix (line, "REJECTED ")) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "In WaitingForReject: Expected `REJECTED am1 am2 ... amN', got `%s'", + line); + g_free (line); + goto out; + } + if (supported_auth_mechs == NULL) + { + supported_auth_mechs = g_strsplit (line + sizeof ("REJECTED ") - 1, " ", 0); +#if 0 + for (n = 0; supported_auth_mechs != NULL && supported_auth_mechs[n] != NULL; n++) + g_printerr ("supported_auth_mechs[%d] = `%s'\n", n, supported_auth_mechs[n]); +#endif + } + g_free (line); + mech = client_choose_mech_and_send_initial_response (auth, + credentials, + (const gchar* const *) supported_auth_mechs, + attempted_auth_mechs, + dos, + cancellable, + error); + if (mech == NULL) + goto out; + if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA) + state = CLIENT_STATE_WAITING_FOR_DATA; + else + state = CLIENT_STATE_WAITING_FOR_OK; + break; + + case CLIENT_STATE_WAITING_FOR_OK: + debug_print ("CLIENT: WaitingForOK"); + line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); + if (line == NULL) + goto out; + debug_print ("CLIENT: WaitingForOK, read `%s'", line); + if (g_str_has_prefix (line, "OK ")) + { + if (!g_dbus_is_guid (line + 3)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Invalid OK response `%s'", + line); + g_free (line); + goto out; + } + ret_guid = g_strdup (line + 3); + g_free (line); + + if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) + { + s = "NEGOTIATE_UNIX_FD\r\n"; + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + state = CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD; + } + else + { + s = "BEGIN\r\n"; + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + /* and we're done! */ + goto out; + } + } + else if (g_str_has_prefix (line, "REJECTED ")) + { + goto foobar; + } + else + { + /* TODO: handle other valid responses */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "In WaitingForOk: unexpected response `%s'", + line); + g_free (line); + goto out; + } + break; + + case CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD: + debug_print ("CLIENT: WaitingForAgreeUnixFD"); + line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); + if (line == NULL) + goto out; + debug_print ("CLIENT: WaitingForAgreeUnixFD, read=`%s'", line); + if (g_strcmp0 (line, "AGREE_UNIX_FD") == 0) + { + negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; + s = "BEGIN\r\n"; + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + /* and we're done! */ + goto out; + } + else if (g_str_has_prefix (line, "ERROR") && (line[5] == 0 || g_ascii_isspace (line[5]))) + { + //g_strstrip (line + 5); g_debug ("bah, no unix_fd: `%s'", line + 5); + g_free (line); + s = "BEGIN\r\n"; + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + /* and we're done! */ + goto out; + } + else + { + /* TODO: handle other valid responses */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "In WaitingForAgreeUnixFd: unexpected response `%s'", + line); + g_free (line); + goto out; + } + break; + + case CLIENT_STATE_WAITING_FOR_DATA: + debug_print ("CLIENT: WaitingForData"); + line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); + if (line == NULL) + goto out; + debug_print ("CLIENT: WaitingForData, read=`%s'", line); + if (g_str_has_prefix (line, "DATA ")) + { + gchar *encoded; + gchar *decoded_data; + gsize decoded_data_len; + + encoded = g_strdup (line + 5); + g_free (line); + g_strstrip (encoded); + decoded_data = hexdecode (encoded, &decoded_data_len, error); + g_free (encoded); + if (decoded_data == NULL) + { + g_prefix_error (error, "DATA response is malformed: "); + /* invalid encoding, disconnect! */ + goto out; + } + _g_dbus_auth_mechanism_client_data_receive (mech, decoded_data, decoded_data_len); + g_free (decoded_data); + + if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND) + { + gchar *data; + gsize data_len; + gchar *encoded_data; + data = _g_dbus_auth_mechanism_client_data_send (mech, &data_len); + encoded_data = hexencode (data); + s = g_strdup_printf ("DATA %s\r\n", encoded_data); + g_free (encoded_data); + g_free (data); + debug_print ("CLIENT: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_free (s); + goto out; + } + g_free (s); + } + state = CLIENT_STATE_WAITING_FOR_OK; + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "In WaitingForData: unexpected response `%s'", + line); + g_free (line); + goto out; + } + break; + + default: + g_assert_not_reached (); + break; + } + + }; /* main authentication client loop */ + + out: + if (mech != NULL) + g_object_unref (mech); + g_ptr_array_unref (attempted_auth_mechs); + g_strfreev (supported_auth_mechs); + g_object_ref (dis); + g_object_ref (dos); + + /* ensure return value is NULL if error is set */ + if (error != NULL && *error != NULL) + { + g_free (ret_guid); + ret_guid = NULL; + } + + if (ret_guid != NULL) + { + if (out_negotiated_capabilities != NULL) + *out_negotiated_capabilities = negotiated_capabilities; + } + + if (credentials != NULL) + g_object_unref (credentials); + + debug_print ("CLIENT: Done, authenticated=%d", ret_guid != NULL); + + return ret_guid; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +get_auth_mechanisms (GDBusAuth *auth, + gboolean allow_anonymous, + const gchar *prefix, + const gchar *suffix, + const gchar *separator) +{ + GList *l; + GString *str; + gboolean need_sep; + + str = g_string_new (prefix); + need_sep = FALSE; + for (l = auth->priv->available_mechanisms; l != NULL; l = l->next) + { + Mechanism *m = l->data; + + if (!allow_anonymous && g_strcmp0 (m->name, "ANONYMOUS") == 0) + continue; + + if (need_sep) + g_string_append (str, separator); + g_string_append (str, m->name); + need_sep = TRUE; + } + + g_string_append (str, suffix); + return g_string_free (str, FALSE); +} + + +typedef enum +{ + SERVER_STATE_WAITING_FOR_AUTH, + SERVER_STATE_WAITING_FOR_DATA, + SERVER_STATE_WAITING_FOR_BEGIN +} ServerState; + +gboolean +_g_dbus_auth_run_server (GDBusAuth *auth, + GDBusAuthObserver *observer, + const gchar *guid, + gboolean allow_anonymous, + GDBusCapabilityFlags offered_capabilities, + GDBusCapabilityFlags *out_negotiated_capabilities, + GCredentials **out_received_credentials, + GCancellable *cancellable, + GError **error) +{ + gboolean ret; + ServerState state; + GDataInputStream *dis; + GDataOutputStream *dos; + GError *local_error; + guchar byte; + gchar *line; + gsize line_length; + GDBusAuthMechanism *mech; + gchar *s; + GDBusCapabilityFlags negotiated_capabilities; + GCredentials *credentials; + + debug_print ("SERVER: initiating"); + + ret = FALSE; + dis = NULL; + dos = NULL; + mech = NULL; + negotiated_capabilities = 0; + credentials = NULL; + + if (!g_dbus_is_guid (guid)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "The given guid `%s' is not valid", + guid); + goto out; + } + + dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream))); + dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream))); + + g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); + + /* first read the NUL-byte (TODO: read credentials if using a unix domain socket) */ +#ifdef G_OS_UNIX + if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ()) + { + local_error = NULL; + credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream), + cancellable, + &local_error); + if (credentials == NULL) + { + g_propagate_error (error, local_error); + goto out; + } + } + else + { + local_error = NULL; + byte = g_data_input_stream_read_byte (dis, cancellable, &local_error); + if (local_error != NULL) + { + g_propagate_error (error, local_error); + goto out; + } + } +#else + local_error = NULL; + byte = g_data_input_stream_read_byte (dis, cancellable, &local_error); + if (local_error != NULL) + { + g_propagate_error (error, local_error); + goto out; + } +#endif + if (credentials != NULL) + { + if (G_UNLIKELY (_g_dbus_debug_authentication ())) + { + s = g_credentials_to_string (credentials); + debug_print ("SERVER: received credentials `%s'", s); + g_free (s); + } + } + else + { + debug_print ("SERVER: didn't receive any credentials"); + } + + state = SERVER_STATE_WAITING_FOR_AUTH; + while (TRUE) + { + switch (state) + { + case SERVER_STATE_WAITING_FOR_AUTH: + debug_print ("SERVER: WaitingForAuth"); + line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); + debug_print ("SERVER: WaitingForAuth, read `%s'", line); + if (line == NULL) + goto out; + if (g_strcmp0 (line, "AUTH") == 0) + { + s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " "); + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_free (s); + goto out; + } + g_free (s); + g_free (line); + } + else if (g_str_has_prefix (line, "AUTH ")) + { + gchar **tokens; + const gchar *encoded; + const gchar *mech_name; + GType auth_mech_to_use_gtype; + + tokens = g_strsplit (line, " ", 0); + g_free (line); + + switch (g_strv_length (tokens)) + { + case 2: + /* no initial response */ + mech_name = tokens[1]; + encoded = NULL; + break; + + case 3: + /* initial response */ + mech_name = tokens[1]; + encoded = tokens[2]; + break; + + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unexpected line `%s' while in WaitingForAuth state", + line); + g_strfreev (tokens); + goto out; + } + + /* TODO: record that the client has attempted to use this mechanism */ + //g_debug ("client is trying `%s'", mech_name); + + auth_mech_to_use_gtype = find_mech_by_name (auth, mech_name); + if ((auth_mech_to_use_gtype == (GType) 0) || + (!allow_anonymous && g_strcmp0 (mech_name, "ANONYMOUS") == 0)) + { + /* We don't support this auth mechanism */ + g_strfreev (tokens); + s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " "); + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_free (s); + goto out; + } + g_free (s); + + /* stay in WAITING FOR AUTH */ + state = SERVER_STATE_WAITING_FOR_AUTH; + } + else + { + gchar *initial_response; + gsize initial_response_len; + + mech = g_object_new (auth_mech_to_use_gtype, + "stream", auth->priv->stream, + "credentials", credentials, + NULL); + + initial_response = NULL; + initial_response_len = 0; + if (encoded != NULL) + { + initial_response = hexdecode (encoded, &initial_response_len, error); + if (initial_response == NULL) + { + g_prefix_error (error, "Initial response is malformed: "); + /* invalid encoding, disconnect! */ + g_strfreev (tokens); + goto out; + } + } + + _g_dbus_auth_mechanism_server_initiate (mech, + initial_response, + initial_response_len); + g_free (initial_response); + g_strfreev (tokens); + + change_state: + switch (_g_dbus_auth_mechanism_server_get_state (mech)) + { + case G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED: + if (observer != NULL && + g_dbus_auth_observer_deny_authenticated_peer (observer, + auth->priv->stream, + credentials)) + { + /* disconnect */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Cancelled via GDBusAuthObserver::deny-authenticated-peer")); + goto out; + } + else + { + s = g_strdup_printf ("OK %s\r\n", guid); + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_free (s); + goto out; + } + g_free (s); + state = SERVER_STATE_WAITING_FOR_BEGIN; + } + break; + + case G_DBUS_AUTH_MECHANISM_STATE_REJECTED: + s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " "); + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_free (s); + goto out; + } + g_free (s); + state = SERVER_STATE_WAITING_FOR_AUTH; + break; + + case G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA: + state = SERVER_STATE_WAITING_FOR_DATA; + break; + + case G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND: + { + gchar *data; + gsize data_len; + gchar *encoded_data; + data = _g_dbus_auth_mechanism_server_data_send (mech, &data_len); + encoded_data = hexencode (data); + s = g_strdup_printf ("DATA %s\r\n", encoded_data); + g_free (encoded_data); + g_free (data); + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + { + g_free (s); + goto out; + } + g_free (s); + } + goto change_state; + break; + + default: + /* TODO */ + g_assert_not_reached (); + break; + } + } + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unexpected line `%s' while in WaitingForAuth state", + line); + g_free (line); + goto out; + } + break; + + case SERVER_STATE_WAITING_FOR_DATA: + debug_print ("SERVER: WaitingForData"); + line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); + debug_print ("SERVER: WaitingForData, read `%s'", line); + if (line == NULL) + goto out; + if (g_str_has_prefix (line, "DATA ")) + { + gchar *encoded; + gchar *decoded_data; + gsize decoded_data_len; + + encoded = g_strdup (line + 5); + g_free (line); + g_strstrip (encoded); + decoded_data = hexdecode (encoded, &decoded_data_len, error); + g_free (encoded); + if (decoded_data == NULL) + { + g_prefix_error (error, "DATA response is malformed: "); + /* invalid encoding, disconnect! */ + goto out; + } + _g_dbus_auth_mechanism_server_data_receive (mech, decoded_data, decoded_data_len); + g_free (decoded_data); + /* oh man, this goto-crap is so ugly.. really need to rewrite the state machine */ + goto change_state; + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unexpected line `%s' while in WaitingForData state", + line); + g_free (line); + } + goto out; + + case SERVER_STATE_WAITING_FOR_BEGIN: + debug_print ("SERVER: WaitingForBegin"); + /* Use extremely slow (but reliable) line reader - this basically + * does a recvfrom() system call per character + * + * (the problem with using GDataInputStream's read_line is that because of + * buffering it might start reading into the first D-Bus message that + * appears after "BEGIN\r\n"....) + */ + line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), + &line_length, + cancellable, + error); + debug_print ("SERVER: WaitingForBegin, read `%s'", line); + if (line == NULL) + goto out; + if (g_strcmp0 (line, "BEGIN") == 0) + { + /* YAY, done! */ + ret = TRUE; + g_free (line); + goto out; + } + else if (g_strcmp0 (line, "NEGOTIATE_UNIX_FD") == 0) + { + if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) + { + negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; + s = "AGREE_UNIX_FD\r\n"; + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + } + else + { + s = "ERROR \"fd passing not offered\"\r\n"; + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + } + } + else + { + g_debug ("Unexpected line `%s' while in WaitingForBegin state", line); + g_free (line); + s = "ERROR \"Unknown Command\"\r\n"; + debug_print ("SERVER: writing `%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) + goto out; + } + break; + + default: + g_assert_not_reached (); + break; + } + } + + + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Not implemented (server)"); + + out: + if (mech != NULL) + g_object_unref (mech); + if (dis != NULL) + g_object_ref (dis); + if (dis != NULL) + g_object_ref (dos); + + /* ensure return value is FALSE if error is set */ + if (error != NULL && *error != NULL) + { + ret = FALSE; + } + + if (ret) + { + if (out_negotiated_capabilities != NULL) + *out_negotiated_capabilities = negotiated_capabilities; + if (out_received_credentials != NULL) + *out_received_credentials = credentials != NULL ? g_object_ref (credentials) : NULL; + } + + if (credentials != NULL) + g_object_unref (credentials); + + debug_print ("SERVER: Done, authenticated=%d", ret); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusauth.h b/gio/gdbusauth.h new file mode 100644 index 000000000..2fc750017 --- /dev/null +++ b/gio/gdbusauth.h @@ -0,0 +1,86 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +#if !defined (GIO_COMPILATION) +#error "gdbusauth.h is a private header file." +#endif + +#ifndef __G_DBUS_AUTH_H__ +#define __G_DBUS_AUTH_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_AUTH (_g_dbus_auth_get_type ()) +#define G_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH, GDBusAuth)) +#define G_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH, GDBusAuthClass)) +#define G_DBUS_AUTH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH, GDBusAuthClass)) +#define G_IS_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH)) +#define G_IS_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH)) + +typedef struct _GDBusAuth GDBusAuth; +typedef struct _GDBusAuthClass GDBusAuthClass; +typedef struct _GDBusAuthPrivate GDBusAuthPrivate; + +struct _GDBusAuthClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +struct _GDBusAuth +{ + GObject parent_instance; + GDBusAuthPrivate *priv; +}; + +GType _g_dbus_auth_get_type (void) G_GNUC_CONST; +GDBusAuth *_g_dbus_auth_new (GIOStream *stream); + +/* TODO: need a way to set allowed authentication mechanisms */ + +/* TODO: need a way to convey credentials etc. */ + +/* TODO: need a way to convey negotiated features (e.g. returning flags from e.g. GDBusConnectionFeatures) */ + +/* TODO: need to expose encode()/decode() from the AuthMechanism (and whether it is needed at all) */ + +gboolean _g_dbus_auth_run_server (GDBusAuth *auth, + GDBusAuthObserver *observer, + const gchar *guid, + gboolean allow_anonymous, + GDBusCapabilityFlags offered_capabilities, + GDBusCapabilityFlags *out_negotiated_capabilities, + GCredentials **out_received_credentials, + GCancellable *cancellable, + GError **error); + +gchar *_g_dbus_auth_run_client (GDBusAuth *auth, + GDBusCapabilityFlags offered_capabilities, + GDBusCapabilityFlags *out_negotiated_capabilities, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* __G_DBUS_AUTH_H__ */ diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c new file mode 100644 index 000000000..72eff74be --- /dev/null +++ b/gio/gdbusauthmechanism.c @@ -0,0 +1,342 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusauthmechanism.h" +#include "gcredentials.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" +#include "giostream.h" + +/* ---------------------------------------------------------------------------------------------------- */ + +struct _GDBusAuthMechanismPrivate +{ + GIOStream *stream; + GCredentials *credentials; +}; + +enum +{ + PROP_0, + PROP_STREAM, + PROP_CREDENTIALS +}; + +G_DEFINE_ABSTRACT_TYPE (GDBusAuthMechanism, _g_dbus_auth_mechanism, G_TYPE_OBJECT); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +_g_dbus_auth_mechanism_finalize (GObject *object) +{ + GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object); + + if (mechanism->priv->stream != NULL) + g_object_unref (mechanism->priv->stream); + if (mechanism->priv->credentials != NULL) + g_object_unref (mechanism->priv->credentials); + + if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize != NULL) + G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize (object); +} + +static void +_g_dbus_auth_mechanism_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object); + + switch (prop_id) + { + case PROP_STREAM: + g_value_set_object (value, mechanism->priv->stream); + break; + + case PROP_CREDENTIALS: + g_value_set_object (value, mechanism->priv->credentials); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_g_dbus_auth_mechanism_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object); + + switch (prop_id) + { + case PROP_STREAM: + mechanism->priv->stream = g_value_dup_object (value); + break; + + case PROP_CREDENTIALS: + mechanism->priv->credentials = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_g_dbus_auth_mechanism_class_init (GDBusAuthMechanismClass *klass) +{ + GObjectClass *gobject_class; + + g_type_class_add_private (klass, sizeof (GDBusAuthMechanismPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->get_property = _g_dbus_auth_mechanism_get_property; + gobject_class->set_property = _g_dbus_auth_mechanism_set_property; + gobject_class->finalize = _g_dbus_auth_mechanism_finalize; + + g_object_class_install_property (gobject_class, + PROP_STREAM, + g_param_spec_object ("stream", + _("IO Stream"), + _("The underlying GIOStream used for I/O"), + G_TYPE_IO_STREAM, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusAuthMechanism:credentials: + * + * If authenticating as a server, this property contains the + * received credentials, if any. + * + * If authenticating as a client, the property contains the + * credentials that were sent, if any. + */ + g_object_class_install_property (gobject_class, + PROP_CREDENTIALS, + g_param_spec_object ("credentials", + _("Credentials"), + _("The credentials of the remote peer"), + G_TYPE_CREDENTIALS, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); +} + +static void +_g_dbus_auth_mechanism_init (GDBusAuthMechanism *mechanism) +{ + /* not used for now */ + mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism, + G_TYPE_DBUS_AUTH_MECHANISM, + GDBusAuthMechanismPrivate); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +GIOStream * +_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return mechanism->priv->stream; +} + +GCredentials * +_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return mechanism->priv->credentials; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +const gchar * +_g_dbus_auth_mechanism_get_name (GType mechanism_type) +{ + const gchar *name; + GDBusAuthMechanismClass *klass; + + g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), NULL); + + klass = g_type_class_ref (mechanism_type); + g_assert (klass != NULL); + name = klass->get_name (); + //g_type_class_unref (klass); + + return name; +} + +gint +_g_dbus_auth_mechanism_get_priority (GType mechanism_type) +{ + gint priority; + GDBusAuthMechanismClass *klass; + + g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), 0); + + klass = g_type_class_ref (mechanism_type); + g_assert (klass != NULL); + priority = klass->get_priority (); + //g_type_class_unref (klass); + + return priority; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean +_g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), FALSE); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->is_supported (mechanism); +} + +gchar * +_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->encode_data (mechanism, data, data_len, out_data_len); +} + + +gchar * +_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->decode_data (mechanism, data, data_len, out_data_len); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +GDBusAuthMechanismState +_g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_state (mechanism); +} + +void +_g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len) +{ + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); + G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_initiate (mechanism, initial_response, initial_response_len); +} + +void +_g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); + G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_receive (mechanism, data, data_len); +} + +gchar * +_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_send (mechanism, out_data_len); +} + +gchar * +_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_reject_reason (mechanism); +} + +void +_g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism) +{ + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); + G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_shutdown (mechanism); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +GDBusAuthMechanismState +_g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism); +} + +gchar * +_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism, + out_initial_response_len); +} + +void +_g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); + G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len); +} + +gchar * +_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len); +} + +void +_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism) +{ + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); + G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism); +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusauthmechanism.h b/gio/gdbusauthmechanism.h new file mode 100644 index 000000000..e00cb1753 --- /dev/null +++ b/gio/gdbusauthmechanism.h @@ -0,0 +1,174 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +#if !defined (GIO_COMPILATION) +#error "gdbusauthmechanism.h is a private header file." +#endif + +#ifndef __G_DBUS_AUTH_MECHANISM_H__ +#define __G_DBUS_AUTH_MECHANISM_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_AUTH_MECHANISM (_g_dbus_auth_mechanism_get_type ()) +#define G_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanism)) +#define G_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass)) +#define G_DBUS_AUTH_MECHANISM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass)) +#define G_IS_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM)) +#define G_IS_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM)) + +typedef struct _GDBusAuthMechanism GDBusAuthMechanism; +typedef struct _GDBusAuthMechanismClass GDBusAuthMechanismClass; +typedef struct _GDBusAuthMechanismPrivate GDBusAuthMechanismPrivate; + +typedef enum { + G_DBUS_AUTH_MECHANISM_STATE_INVALID, + G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA, + G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, + G_DBUS_AUTH_MECHANISM_STATE_REJECTED, + G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED, +} GDBusAuthMechanismState; + +struct _GDBusAuthMechanismClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + + /* VTable */ + + /* TODO: server_initiate and client_initiate probably needs to have a + * GCredentials parameter... + */ + + gint (*get_priority) (void); + const gchar *(*get_name) (void); + + /* functions shared by server/client */ + gboolean (*is_supported) (GDBusAuthMechanism *mechanism); + gchar *(*encode_data) (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); + gchar *(*decode_data) (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); + + /* functions for server-side authentication */ + GDBusAuthMechanismState (*server_get_state) (GDBusAuthMechanism *mechanism); + void (*server_initiate) (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len); + void (*server_data_receive) (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); + gchar *(*server_data_send) (GDBusAuthMechanism *mechanism, + gsize *out_data_len); + gchar *(*server_get_reject_reason) (GDBusAuthMechanism *mechanism); + void (*server_shutdown) (GDBusAuthMechanism *mechanism); + + /* functions for client-side authentication */ + GDBusAuthMechanismState (*client_get_state) (GDBusAuthMechanism *mechanism); + gchar *(*client_initiate) (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len); + void (*client_data_receive) (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); + gchar *(*client_data_send) (GDBusAuthMechanism *mechanism, + gsize *out_data_len); + void (*client_shutdown) (GDBusAuthMechanism *mechanism); + + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); + void (*_g_reserved9) (void); + void (*_g_reserved10) (void); + void (*_g_reserved11) (void); + void (*_g_reserved12) (void); + void (*_g_reserved13) (void); + void (*_g_reserved14) (void); + void (*_g_reserved15) (void); + void (*_g_reserved16) (void); +}; + +struct _GDBusAuthMechanism +{ + GObject parent_instance; + GDBusAuthMechanismPrivate *priv; +}; + +GType _g_dbus_auth_mechanism_get_type (void) G_GNUC_CONST; + +gint _g_dbus_auth_mechanism_get_priority (GType mechanism_type); +const gchar *_g_dbus_auth_mechanism_get_name (GType mechanism_type); + +GIOStream *_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism); +GCredentials *_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism); + +gboolean _g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism); +gchar *_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +gchar *_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); + +GDBusAuthMechanismState _g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism); +void _g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len); +void _g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +gchar *_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +gchar *_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); +void _g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism); + +GDBusAuthMechanismState _g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism); +gchar *_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len); +void _g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +gchar *_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +void _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism); + + +G_END_DECLS + +#endif /* __G_DBUS_AUTH_MECHANISM_H__ */ diff --git a/gio/gdbusauthmechanismanon.c b/gio/gdbusauthmechanismanon.c new file mode 100644 index 000000000..4c666ec1f --- /dev/null +++ b/gio/gdbusauthmechanismanon.c @@ -0,0 +1,327 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusauthmechanismanon.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" + +struct _GDBusAuthMechanismAnonPrivate +{ + gboolean is_client; + gboolean is_server; + GDBusAuthMechanismState state; +}; + +static gint mechanism_get_priority (void); +static const gchar *mechanism_get_name (void); + +static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism); +static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism); +static void mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len); +static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); +static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism); +static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism); +static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len); +static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism); + +/* ---------------------------------------------------------------------------------------------------- */ + +G_DEFINE_TYPE (GDBusAuthMechanismAnon, _g_dbus_auth_mechanism_anon, G_TYPE_DBUS_AUTH_MECHANISM); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +_g_dbus_auth_mechanism_anon_finalize (GObject *object) +{ + //GDBusAuthMechanismAnon *mechanism = G_DBUS_AUTH_MECHANISM_ANON (object); + + if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize != NULL) + G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize (object); +} + +static void +_g_dbus_auth_mechanism_anon_class_init (GDBusAuthMechanismAnonClass *klass) +{ + GObjectClass *gobject_class; + GDBusAuthMechanismClass *mechanism_class; + + g_type_class_add_private (klass, sizeof (GDBusAuthMechanismAnonPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = _g_dbus_auth_mechanism_anon_finalize; + + mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass); + mechanism_class->get_priority = mechanism_get_priority; + mechanism_class->get_name = mechanism_get_name; + mechanism_class->is_supported = mechanism_is_supported; + mechanism_class->encode_data = mechanism_encode_data; + mechanism_class->decode_data = mechanism_decode_data; + mechanism_class->server_get_state = mechanism_server_get_state; + mechanism_class->server_initiate = mechanism_server_initiate; + mechanism_class->server_data_receive = mechanism_server_data_receive; + mechanism_class->server_data_send = mechanism_server_data_send; + mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason; + mechanism_class->server_shutdown = mechanism_server_shutdown; + mechanism_class->client_get_state = mechanism_client_get_state; + mechanism_class->client_initiate = mechanism_client_initiate; + mechanism_class->client_data_receive = mechanism_client_data_receive; + mechanism_class->client_data_send = mechanism_client_data_send; + mechanism_class->client_shutdown = mechanism_client_shutdown; +} + +static void +_g_dbus_auth_mechanism_anon_init (GDBusAuthMechanismAnon *mechanism) +{ + mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism, + G_TYPE_DBUS_AUTH_MECHANISM_ANON, + GDBusAuthMechanismAnonPrivate); +} + +/* ---------------------------------------------------------------------------------------------------- */ + + +static gint +mechanism_get_priority (void) +{ + /* We prefer ANONYMOUS to most other mechanism (such as DBUS_COOKIE_SHA1) but not to EXTERNAL */ + return 50; +} + + +static const gchar * +mechanism_get_name (void) +{ + return "ANONYMOUS"; +} + +static gboolean +mechanism_is_supported (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), FALSE); + return TRUE; +} + +static gchar * +mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + return NULL; +} + + +static gchar * +mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanismState +mechanism_server_get_state (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID); + + return m->priv->state; +} + +static void +mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism)); + g_return_if_fail (!m->priv->is_server && !m->priv->is_client); + + if (initial_response != NULL) + g_debug ("ANONYMOUS: initial_response was `%s'", initial_response); + + m->priv->is_server = TRUE; + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; +} + +static void +mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism)); + g_return_if_fail (m->priv->is_server && !m->priv->is_client); + g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA); + + /* can never end up here because we are never in the WAITING_FOR_DATA state */ + g_assert_not_reached (); +} + +static gchar * +mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + + /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */ + g_assert_not_reached (); + + return NULL; +} + +static gchar * +mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL); + + /* can never end up here because we are never in the REJECTED state */ + g_assert_not_reached (); + + return NULL; +} + +static void +mechanism_server_shutdown (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism)); + g_return_if_fail (m->priv->is_server && !m->priv->is_client); + + m->priv->is_server = FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanismState +mechanism_client_get_state (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID); + + return m->priv->state; +} + +static gchar * +mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL); + g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL); + + m->priv->is_client = TRUE; + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; + + *out_initial_response_len = -1; + + /* just return our library name and version */ + return g_strdup ("GDBus 0.1"); +} + +static void +mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism)); + g_return_if_fail (m->priv->is_client && !m->priv->is_server); + g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA); + + /* can never end up here because we are never in the WAITING_FOR_DATA state */ + g_assert_not_reached (); +} + +static gchar * +mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL); + g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + + /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */ + g_assert_not_reached (); + + return NULL; +} + +static void +mechanism_client_shutdown (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism)); + g_return_if_fail (m->priv->is_client && !m->priv->is_server); + + m->priv->is_client = FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusauthmechanismanon.h b/gio/gdbusauthmechanismanon.h new file mode 100644 index 000000000..d3c2c2472 --- /dev/null +++ b/gio/gdbusauthmechanismanon.h @@ -0,0 +1,82 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +#if !defined (GIO_COMPILATION) +#error "gdbusauthmechanismanon.h is a private header file." +#endif + +#ifndef __G_DBUS_AUTH_MECHANISM_ANON_H__ +#define __G_DBUS_AUTH_MECHANISM_ANON_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_AUTH_MECHANISM_ANON (_g_dbus_auth_mechanism_anon_get_type ()) +#define G_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnon)) +#define G_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass)) +#define G_DBUS_AUTH_MECHANISM_ANON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass)) +#define G_IS_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON)) +#define G_IS_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON)) + +typedef struct _GDBusAuthMechanismAnon GDBusAuthMechanismAnon; +typedef struct _GDBusAuthMechanismAnonClass GDBusAuthMechanismAnonClass; +typedef struct _GDBusAuthMechanismAnonPrivate GDBusAuthMechanismAnonPrivate; + +struct _GDBusAuthMechanismAnonClass +{ + /*< private >*/ + GDBusAuthMechanismClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); + void (*_g_reserved9) (void); + void (*_g_reserved10) (void); + void (*_g_reserved11) (void); + void (*_g_reserved12) (void); + void (*_g_reserved13) (void); + void (*_g_reserved14) (void); + void (*_g_reserved15) (void); + void (*_g_reserved16) (void); +}; + +struct _GDBusAuthMechanismAnon +{ + GDBusAuthMechanism parent_instance; + GDBusAuthMechanismAnonPrivate *priv; +}; + +GType _g_dbus_auth_mechanism_anon_get_type (void) G_GNUC_CONST; + + +G_END_DECLS + +#endif /* __G_DBUS_AUTH_MECHANISM_ANON_H__ */ diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c new file mode 100644 index 000000000..bf8d9318a --- /dev/null +++ b/gio/gdbusauthmechanismexternal.c @@ -0,0 +1,416 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusauthmechanismexternal.h" +#include "gcredentials.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" + +#ifdef G_OS_UNIX +#include +#include +#endif + +struct _GDBusAuthMechanismExternalPrivate +{ + gboolean is_client; + gboolean is_server; + GDBusAuthMechanismState state; +}; + +static gint mechanism_get_priority (void); +static const gchar *mechanism_get_name (void); + +static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism); +static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism); +static void mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len); +static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); +static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism); +static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism); +static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len); +static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism); + +/* ---------------------------------------------------------------------------------------------------- */ + +G_DEFINE_TYPE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +_g_dbus_auth_mechanism_external_finalize (GObject *object) +{ + //GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object); + + if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL) + G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object); +} + +static void +_g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass) +{ + GObjectClass *gobject_class; + GDBusAuthMechanismClass *mechanism_class; + + g_type_class_add_private (klass, sizeof (GDBusAuthMechanismExternalPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize; + + mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass); + mechanism_class->get_name = mechanism_get_name; + mechanism_class->get_priority = mechanism_get_priority; + mechanism_class->is_supported = mechanism_is_supported; + mechanism_class->encode_data = mechanism_encode_data; + mechanism_class->decode_data = mechanism_decode_data; + mechanism_class->server_get_state = mechanism_server_get_state; + mechanism_class->server_initiate = mechanism_server_initiate; + mechanism_class->server_data_receive = mechanism_server_data_receive; + mechanism_class->server_data_send = mechanism_server_data_send; + mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason; + mechanism_class->server_shutdown = mechanism_server_shutdown; + mechanism_class->client_get_state = mechanism_client_get_state; + mechanism_class->client_initiate = mechanism_client_initiate; + mechanism_class->client_data_receive = mechanism_client_data_receive; + mechanism_class->client_data_send = mechanism_client_data_send; + mechanism_class->client_shutdown = mechanism_client_shutdown; +} + +static void +_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism) +{ + mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism, + G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, + GDBusAuthMechanismExternalPrivate); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +mechanism_is_supported (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE); + /* This mechanism is only available if credentials has been exchanged */ + if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL) + return TRUE; + else + return FALSE; +} + +static gint +mechanism_get_priority (void) +{ + /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */ + return 100; +} + +static const gchar * +mechanism_get_name (void) +{ + return "EXTERNAL"; +} + +static gchar * +mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + return NULL; +} + + +static gchar * +mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanismState +mechanism_server_get_state (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID); + + return m->priv->state; +} + +static gboolean +data_matches_credentials (const gchar *data, + GCredentials *credentials) +{ + gboolean match; + + match = FALSE; + + if (credentials == NULL) + goto out; + + if (data == NULL || strlen (data) == 0) + goto out; + +#if defined(G_OS_UNIX) + { + gint64 alleged_uid; + gchar *endp; + + /* on UNIX, this is the uid as a string in base 10 */ + alleged_uid = g_ascii_strtoll (data, &endp, 10); + if (*endp == '\0') + { + if (g_credentials_has_unix_user (credentials) && + g_credentials_get_unix_user (credentials) == alleged_uid) + { + match = TRUE; + } + } + } +#elif defined(G_OS_WIN32) + { + const gchar *alleged_sid; + + /* on Win32, this is the User SID */ + alleged_sid = data; + if (g_credentials_has_windows_user (credentials) && + g_strcmp0 (g_credentials_get_windows_user (credentials), alleged_sid) == 0) + { + match = TRUE; + } + } +#else +#error Dont know how to read credentials on this OS. Please implement. +#endif + + out: + return match; +} + +static void +mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism)); + g_return_if_fail (!m->priv->is_server && !m->priv->is_client); + + m->priv->is_server = TRUE; + + if (initial_response != NULL) + { + if (data_matches_credentials (initial_response, _g_dbus_auth_mechanism_get_credentials (mechanism))) + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; + } + else + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + } + } + else + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA; + } +} + +static void +mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism)); + g_return_if_fail (m->priv->is_server && !m->priv->is_client); + g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA); + + if (data_matches_credentials (data, _g_dbus_auth_mechanism_get_credentials (mechanism))) + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; + } + else + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + } +} + +static gchar * +mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + + /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */ + g_assert_not_reached (); + + return NULL; +} + +static gchar * +mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL); + + /* can never end up here because we are never in the REJECTED state */ + g_assert_not_reached (); + + return NULL; +} + +static void +mechanism_server_shutdown (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism)); + g_return_if_fail (m->priv->is_server && !m->priv->is_client); + + m->priv->is_server = FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanismState +mechanism_client_get_state (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID); + + return m->priv->state; +} + +static gchar * +mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + gchar *initial_response; + GCredentials *credentials; + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL); + g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL); + + m->priv->is_client = TRUE; + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; + + *out_initial_response_len = -1; + + credentials = _g_dbus_auth_mechanism_get_credentials (mechanism); + g_assert (credentials != NULL); + + /* return the uid */ +#if defined(G_OS_UNIX) + initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, g_credentials_get_unix_user (credentials)); +#elif defined(G_OS_WIN32) + initial_response = g_strdup_printf ("%s", g_credentials_get_windows_user ()); +#else +#warning Dont know how to send credentials on this OS. Please implement. + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; +#endif + return initial_response; +} + +static void +mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism)); + g_return_if_fail (m->priv->is_client && !m->priv->is_server); + g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA); + + /* can never end up here because we are never in the WAITING_FOR_DATA state */ + g_assert_not_reached (); +} + +static gchar * +mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL); + g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + + /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */ + g_assert_not_reached (); + + return NULL; +} + +static void +mechanism_client_shutdown (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism)); + g_return_if_fail (m->priv->is_client && !m->priv->is_server); + + m->priv->is_client = FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusauthmechanismexternal.h b/gio/gdbusauthmechanismexternal.h new file mode 100644 index 000000000..39e7fa217 --- /dev/null +++ b/gio/gdbusauthmechanismexternal.h @@ -0,0 +1,82 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +#if !defined (GIO_COMPILATION) +#error "gdbusauthmechanismexternal.h is a private header file." +#endif + +#ifndef __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__ +#define __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL (_g_dbus_auth_mechanism_external_get_type ()) +#define G_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternal)) +#define G_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass)) +#define G_DBUS_AUTH_MECHANISM_EXTERNAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass)) +#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL)) +#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL)) + +typedef struct _GDBusAuthMechanismExternal GDBusAuthMechanismExternal; +typedef struct _GDBusAuthMechanismExternalClass GDBusAuthMechanismExternalClass; +typedef struct _GDBusAuthMechanismExternalPrivate GDBusAuthMechanismExternalPrivate; + +struct _GDBusAuthMechanismExternalClass +{ + /*< private >*/ + GDBusAuthMechanismClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); + void (*_g_reserved9) (void); + void (*_g_reserved10) (void); + void (*_g_reserved11) (void); + void (*_g_reserved12) (void); + void (*_g_reserved13) (void); + void (*_g_reserved14) (void); + void (*_g_reserved15) (void); + void (*_g_reserved16) (void); +}; + +struct _GDBusAuthMechanismExternal +{ + GDBusAuthMechanism parent_instance; + GDBusAuthMechanismExternalPrivate *priv; +}; + +GType _g_dbus_auth_mechanism_external_get_type (void) G_GNUC_CONST; + + +G_END_DECLS + +#endif /* __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__ */ diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c new file mode 100644 index 000000000..ee94e49a0 --- /dev/null +++ b/gio/gdbusauthmechanismsha1.c @@ -0,0 +1,1216 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusauthmechanismsha1.h" +#include "gcredentials.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" +#include "gioerror.h" + +#ifdef G_OS_UNIX +#include +#include +#include +#endif + +#include + +#include + +struct _GDBusAuthMechanismSha1Private +{ + gboolean is_client; + gboolean is_server; + GDBusAuthMechanismState state; + + /* used on the client side */ + gchar *to_send; + + /* used on the server side */ + gchar *cookie; + gchar *server_challenge; +}; + +static gint mechanism_get_priority (void); +static const gchar *mechanism_get_name (void); + +static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism); +static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len); +static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism); +static void mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len); +static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); +static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism); +static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism); +static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len); +static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len); +static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism); + +/* ---------------------------------------------------------------------------------------------------- */ + +G_DEFINE_TYPE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +_g_dbus_auth_mechanism_sha1_finalize (GObject *object) +{ + GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object); + + g_free (mechanism->priv->to_send); + + g_free (mechanism->priv->cookie); + g_free (mechanism->priv->server_challenge); + + if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize != NULL) + G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize (object); +} + +static void +_g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass) +{ + GObjectClass *gobject_class; + GDBusAuthMechanismClass *mechanism_class; + + g_type_class_add_private (klass, sizeof (GDBusAuthMechanismSha1Private)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize; + + mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass); + mechanism_class->get_priority = mechanism_get_priority; + mechanism_class->get_name = mechanism_get_name; + mechanism_class->is_supported = mechanism_is_supported; + mechanism_class->encode_data = mechanism_encode_data; + mechanism_class->decode_data = mechanism_decode_data; + mechanism_class->server_get_state = mechanism_server_get_state; + mechanism_class->server_initiate = mechanism_server_initiate; + mechanism_class->server_data_receive = mechanism_server_data_receive; + mechanism_class->server_data_send = mechanism_server_data_send; + mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason; + mechanism_class->server_shutdown = mechanism_server_shutdown; + mechanism_class->client_get_state = mechanism_client_get_state; + mechanism_class->client_initiate = mechanism_client_initiate; + mechanism_class->client_data_receive = mechanism_client_data_receive; + mechanism_class->client_data_send = mechanism_client_data_send; + mechanism_class->client_shutdown = mechanism_client_shutdown; +} + +static void +_g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism) +{ + mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism, + G_TYPE_DBUS_AUTH_MECHANISM_SHA1, + GDBusAuthMechanismSha1Private); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +mechanism_get_priority (void) +{ + return 0; +} + +static const gchar * +mechanism_get_name (void) +{ + return "DBUS_COOKIE_SHA1"; +} + +static gboolean +mechanism_is_supported (GDBusAuthMechanism *mechanism) +{ + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE); + return TRUE; +} + +static gchar * +mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + return NULL; +} + + +static gchar * +mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) +{ + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +random_ascii (void) +{ + gint ret; + ret = g_random_int_range (0, 60); + if (ret < 25) + ret += 'A'; + else if (ret < 50) + ret += 'a' - 25; + else + ret += '0' - 50; + return ret; +} + +static gchar * +random_ascii_string (guint len) +{ + GString *challenge; + guint n; + + challenge = g_string_new (NULL); + for (n = 0; n < len; n++) + g_string_append_c (challenge, random_ascii ()); + return g_string_free (challenge, FALSE); +} + +static gchar * +random_blob (guint len) +{ + GString *challenge; + guint n; + + challenge = g_string_new (NULL); + for (n = 0; n < len; n++) + g_string_append_c (challenge, g_random_int_range (0, 256)); + return g_string_free (challenge, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* ensure keyring dir exists and permissions are correct */ +static gchar * +ensure_keyring_directory (GError **error) +{ + gchar *path; + const gchar *e; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR"); + if (e != NULL) + { + path = g_strdup (e); + } + else + { + path = g_build_filename (g_get_home_dir (), + ".dbus-keyrings", + NULL); + } + + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + { + if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL) + { +#ifdef G_OS_UNIX + struct stat statbuf; + if (stat (path, &statbuf) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error statting directory `%s': %s"), + path, + strerror (errno)); + g_free (path); + path = NULL; + goto out; + } + if ((statbuf.st_mode & 0777) != 0700) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Permissions on directory `%s' are malformed. Expected mode 0700, got 0%o"), + path, + statbuf.st_mode & 0777); + g_free (path); + path = NULL; + goto out; + } +#else +#error Please implement permission checking on non-UNIX platforms +#endif + } + goto out; + } + + if (g_mkdir (path, 0700) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error creating directory `%s': %s"), + path, + strerror (errno)); + g_free (path); + path = NULL; + goto out; + } + +out: + return path; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +append_nibble (GString *s, gint val) +{ + g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val)); +} + +static gchar * +hexencode (const gchar *str, + gssize len) +{ + guint n; + GString *s; + + if (len == -1) + len = strlen (str); + + s = g_string_new (NULL); + for (n = 0; n < len; n++) + { + gint val; + gint upper_nibble; + gint lower_nibble; + + val = ((const guchar *) str)[n]; + upper_nibble = val >> 4; + lower_nibble = val & 0x0f; + + append_nibble (s, upper_nibble); + append_nibble (s, lower_nibble); + } + + return g_string_free (s, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* looks up an entry in the keyring */ +static gchar * +keyring_lookup_entry (const gchar *cookie_context, + gint cookie_id, + GError **error) +{ + gchar *ret; + gchar *keyring_dir; + gchar *contents; + gchar *path; + guint n; + gchar **lines; + + g_return_val_if_fail (cookie_context != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = NULL; + path = NULL; + contents = NULL; + lines = NULL; + + keyring_dir = ensure_keyring_directory (error); + if (keyring_dir == NULL) + goto out; + + path = g_build_filename (keyring_dir, cookie_context, NULL); + + if (!g_file_get_contents (path, + &contents, + NULL, + error)) + { + g_prefix_error (error, + _("Error opening keyring `%s' for reading: "), + path); + goto out; + } + g_assert (contents != NULL); + + lines = g_strsplit (contents, "\n", 0); + for (n = 0; lines[n] != NULL; n++) + { + const gchar *line = lines[n]; + gchar **tokens; + gchar *endp; + gint line_id; + guint64 line_when; + + if (line[0] == '\0') + continue; + + tokens = g_strsplit (line, " ", 0); + if (g_strv_length (tokens) != 3) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Line %d of the keyring at `%s' with content `%s' is malformed"), + n + 1, + path, + line); + g_strfreev (tokens); + goto out; + } + + line_id = g_ascii_strtoll (tokens[0], &endp, 10); + if (*endp != '\0') + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("First token of line %d of the keyring at `%s' with content `%s' is malformed"), + n + 1, + path, + line); + g_strfreev (tokens); + goto out; + } + + line_when = g_ascii_strtoll (tokens[1], &endp, 10); + if (*endp != '\0') + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Second token of line %d of the keyring at `%s' with content `%s' is malformed"), + n + 1, + path, + line); + g_strfreev (tokens); + goto out; + } + + if (line_id == cookie_id) + { + /* YAY, success */ + ret = tokens[2]; /* steal pointer */ + tokens[2] = NULL; + g_strfreev (tokens); + goto out; + } + + g_strfreev (tokens); + } + + /* BOOH, didn't find the cookie */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Didn't find cookie with id %d in the keyring at `%s'"), + cookie_id, + path); + + out: + g_free (keyring_dir); + g_free (path); + g_free (contents); + g_strfreev (lines); + return ret; +} + +/* function for logging important events that the system administrator should take notice of */ +static void +_log (const gchar *message, + ...) +{ + gchar *s; + va_list var_args; + + va_start (var_args, message); + s = g_strdup_vprintf (message, var_args); + va_end (var_args); + + /* TODO: might want to send this to syslog instead */ + g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s); + g_free (s); +} + +static gint +keyring_acquire_lock (const gchar *path, + GError **error) +{ + gchar *lock; + gint ret; + guint num_tries; + guint num_create_tries; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = -1; + lock = g_strdup_printf ("%s.lock", path); + + /* This is what the D-Bus spec says + * + * Create a lockfile name by appending ".lock" to the name of the + * cookie file. The server should attempt to create this file using + * O_CREAT | O_EXCL. If file creation fails, the lock + * fails. Servers should retry for a reasonable period of time, + * then they may choose to delete an existing lock to keep users + * from having to manually delete a stale lock. [1] + * + * [1] : Lockfiles are used instead of real file locking fcntl() because + * real locking implementations are still flaky on network filesystems + */ + + num_create_tries = 0; +#ifdef EEXISTS + again: +#endif + num_tries = 0; + while (g_file_test (lock, G_FILE_TEST_EXISTS)) + { + /* sleep 10ms, then try again */ + g_usleep (1000*10); + num_tries++; + if (num_tries == 50) + { + /* ok, we slept 50*10ms = 0.5 seconds.. Conclude that the lock-file must be + * stale (nuke the it from orbit) + */ + if (g_unlink (lock) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error deleting stale lock-file `%s': %s"), + lock, + strerror (errno)); + goto out; + } + _log ("Deleted stale lock-file `%s'", lock); + break; + } + } + + ret = g_open (lock, O_CREAT | +#ifdef O_EXCL + O_EXCL, +#else + 0, +#endif + 0700); + if (ret == -1) + { +#ifdef EEXISTS + /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */ + if (errno == EEXISTS) + { + num_create_tries++; + if (num_create_tries < 5) + goto again; + } +#endif + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error creating lock-file `%s': %s"), + lock, + strerror (errno)); + goto out; + } + + out: + g_free (lock); + return ret; +} + +static gboolean +keyring_release_lock (const gchar *path, + gint lock_fd, + GError **error) +{ + gchar *lock; + gboolean ret; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (lock_fd != -1, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + lock = g_strdup_printf ("%s.lock", path); + if (g_unlink (lock) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error unlinking lock-file `%s': %s"), + lock, + strerror (errno)); + goto out; + } + if (close (lock_fd) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error closing (unlinked) lock-file `%s': %s"), + lock, + strerror (errno)); + goto out; + } + + ret = TRUE; + + out: + g_free (lock); + return ret; +} + + +/* adds an entry to the keyring, taking care of locking and deleting stale/future entries */ +static gboolean +keyring_generate_entry (const gchar *cookie_context, + gint *out_id, + gchar **out_cookie, + GError **error) +{ + gboolean ret; + gchar *keyring_dir; + gchar *path; + gchar *contents; + GError *local_error; + gchar **lines; + gint max_line_id; + GString *new_contents; + guint64 now; + gboolean have_id; + gint use_id; + gchar *use_cookie; + gboolean changed_file; + gint lock_fd; + + g_return_val_if_fail (cookie_context != NULL, FALSE); + g_return_val_if_fail (out_id != NULL, FALSE); + g_return_val_if_fail (out_cookie != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + path = NULL; + contents = NULL; + lines = NULL; + new_contents = NULL; + have_id = FALSE; + use_cookie = NULL; + lock_fd = -1; + + keyring_dir = ensure_keyring_directory (error); + if (keyring_dir == NULL) + goto out; + + path = g_build_filename (keyring_dir, cookie_context, NULL); + + lock_fd = keyring_acquire_lock (path, error); + if (lock_fd == -1) + goto out; + + local_error = NULL; + contents = NULL; + if (!g_file_get_contents (path, + &contents, + NULL, + &local_error)) + { + if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT) + { + /* file doesn't have to exist */ + g_error_free (local_error); + } + else + { + g_propagate_prefixed_error (error, + local_error, + _("Error opening keyring `%s' for writing: "), + path); + goto out; + } + } + + new_contents = g_string_new (NULL); + now = time (NULL); + changed_file = FALSE; + + max_line_id = 0; + if (contents != NULL) + { + guint n; + lines = g_strsplit (contents, "\n", 0); + for (n = 0; lines[n] != NULL; n++) + { + const gchar *line = lines[n]; + gchar **tokens; + gchar *endp; + gint line_id; + guint64 line_when; + gboolean keep_entry; + + if (line[0] == '\0') + continue; + + tokens = g_strsplit (line, " ", 0); + if (g_strv_length (tokens) != 3) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Line %d of the keyring at `%s' with content `%s' is malformed"), + n + 1, + path, + line); + g_strfreev (tokens); + goto out; + } + + line_id = g_ascii_strtoll (tokens[0], &endp, 10); + if (*endp != '\0') + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("First token of line %d of the keyring at `%s' with content `%s' is malformed"), + n + 1, + path, + line); + g_strfreev (tokens); + goto out; + } + + line_when = g_ascii_strtoll (tokens[1], &endp, 10); + if (*endp != '\0') + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Second token of line %d of the keyring at `%s' with content `%s' is malformed"), + n + 1, + path, + line); + g_strfreev (tokens); + goto out; + } + + /* D-Bus spec says: + * + * Once the lockfile has been created, the server loads the + * cookie file. It should then delete any cookies that are + * old (the timeout can be fairly short), or more than a + * reasonable time in the future (so that cookies never + * accidentally become permanent, if the clock was set far + * into the future at some point). If no recent keys remain, + * the server may generate a new key. + * + */ + keep_entry = TRUE; + if (line_when > now) + { + /* Oddball case: entry is more recent than our current wall-clock time.. + * This is OK, it means that another server on another machine but with + * same $HOME wrote the entry. + * + * So discard the entry if it's more than 1 day in the future ("reasonable + * time in the future"). + */ + if (line_when - now > 24*60*60) + { + keep_entry = FALSE; + _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now); + } + } + else + { + /* Discard entry if it's older than 15 minutes ("can be fairly short") */ + if (now - line_when > 15*60) + { + keep_entry = FALSE; + } + } + + if (!keep_entry) + { + changed_file = FALSE; + } + else + { + g_string_append_printf (new_contents, + "%d %" G_GUINT64_FORMAT " %s\n", + line_id, + line_when, + tokens[2]); + max_line_id = MAX (line_id, max_line_id); + /* Only reuse entry if not older than 10 minutes. + * + * (We need a bit of grace time compared to 15 minutes above.. otherwise + * there's a race where we reuse the 14min59.9 secs old entry and a + * split-second later another server purges the now 15 minute old entry.) + */ + if (now - line_when < 10 * 60) + { + if (!have_id) + { + use_id = line_id; + use_cookie = tokens[2]; /* steal memory */ + tokens[2] = NULL; + have_id = TRUE; + } + } + } + g_strfreev (tokens); + } + } /* for each line */ + + ret = TRUE; + + if (have_id) + { + *out_id = use_id; + *out_cookie = use_cookie; + use_cookie = NULL; + } + else + { + gchar *raw_cookie; + *out_id = max_line_id + 1; + raw_cookie = random_blob (32); + *out_cookie = hexencode (raw_cookie, 32); + g_free (raw_cookie); + + g_string_append_printf (new_contents, + "%d %" G_GUINT64_FORMAT " %s\n", + *out_id, + (guint64) time (NULL), + *out_cookie); + changed_file = TRUE; + } + + /* and now actually write the cookie file if there are changes (this is atomic) */ + if (changed_file) + { + if (!g_file_set_contents (path, + new_contents->str, + -1, + error)) + { + *out_id = 0; + *out_cookie = 0; + g_free (*out_cookie); + ret = FALSE; + goto out; + } + } + + out: + + if (lock_fd != -1) + { + GError *local_error; + local_error = NULL; + if (!keyring_release_lock (path, lock_fd, &local_error)) + { + if (error != NULL) + { + if (*error == NULL) + { + *error = local_error; + } + else + { + g_prefix_error (error, + _("(Additionally, releasing the lock for `%s' also failed: %s) "), + path, + local_error->message); + } + } + else + { + g_error_free (local_error); + } + } + } + + g_free (keyring_dir); + g_free (path); + g_strfreev (lines); + g_free (contents); + if (new_contents != NULL) + g_string_free (new_contents, TRUE); + g_free (use_cookie); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +generate_sha1 (const gchar *server_challenge, + const gchar *client_challenge, + const gchar *cookie) +{ + GString *str; + gchar *sha1; + + str = g_string_new (server_challenge); + g_string_append_c (str, ':'); + g_string_append (str, client_challenge); + g_string_append_c (str, ':'); + g_string_append (str, cookie); + sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1); + g_string_free (str, TRUE); + + return sha1; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanismState +mechanism_server_get_state (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID); + + return m->priv->state; +} + +static void +mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism)); + g_return_if_fail (!m->priv->is_server && !m->priv->is_client); + + m->priv->is_server = TRUE; + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + + if (initial_response != NULL && strlen (initial_response) > 0) + { +#ifdef G_OS_UNIX + gint64 uid; + gchar *endp; + + uid = g_ascii_strtoll (initial_response, &endp, 10); + if (*endp == '\0') + { + if (uid == getuid ()) + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND; + } + } +#elif defined(G_OS_WIN32) + GCredentials *credentials; + credentials = g_credentials_new_for_process (); + if (g_strcmp0 (g_credentials_get_windows_user (credentials), initial_response) == 0) + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND; + g_object_unref (credentials); +#else +#error Please implement for your OS +#endif + } +} + +static void +mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + gchar **tokens; + const gchar *client_challenge; + const gchar *alleged_sha1; + gchar *sha1; + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism)); + g_return_if_fail (m->priv->is_server && !m->priv->is_client); + g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA); + + tokens = NULL; + sha1 = NULL; + + tokens = g_strsplit (data, " ", 0); + if (g_strv_length (tokens) != 2) + { + g_warning ("Malformed data `%s'", data); + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + goto out; + } + + client_challenge = tokens[0]; + alleged_sha1 = tokens[1]; + + sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie); + + if (g_strcmp0 (sha1, alleged_sha1) == 0) + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; + } + else + { + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + } + + out: + g_strfreev (tokens); + g_free (sha1); +} + +static gchar * +mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + gchar *s; + gint cookie_id; + const gchar *cookie_context; + GError *error; + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + + s = NULL; + + /* TODO: use GDBusAuthObserver here to get the cookie context to use? */ + cookie_context = "org_gtk_gdbus_general"; + + error = NULL; + if (!keyring_generate_entry (cookie_context, + &cookie_id, + &m->priv->cookie, + &error)) + { + g_warning ("Error adding entry to keyring: %s", error->message); + g_error_free (error); + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + goto out; + } + + m->priv->server_challenge = random_ascii_string (16); + s = g_strdup_printf ("%s %d %s", + cookie_context, + cookie_id, + m->priv->server_challenge); + + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA; + + out: + return s; +} + +static gchar * +mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL); + + /* can never end up here because we are never in the REJECTED state */ + g_assert_not_reached (); + + return NULL; +} + +static void +mechanism_server_shutdown (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism)); + g_return_if_fail (m->priv->is_server && !m->priv->is_client); + + m->priv->is_server = FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAuthMechanismState +mechanism_client_get_state (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); + g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID); + + return m->priv->state; +} + +static gchar * +mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + gchar *initial_response; + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL); + g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL); + + m->priv->is_client = TRUE; + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA; + + *out_initial_response_len = -1; + +#ifdef G_OS_UNIX + initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ()); +#elif defined (G_OS_WIN32) + { + GCredentials *credentials; + credentials = g_credentials_new_for_process (); + initial_response = g_strdup (g_credentials_get_windows_user (credentials)); + g_object_unref (credentials); + } +#else +#endif + g_assert (initial_response != NULL); + + return initial_response; +} + +static void +mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + gchar **tokens; + const gchar *cookie_context; + guint cookie_id; + const gchar *server_challenge; + gchar *client_challenge; + gchar *endp; + gchar *cookie; + GError *error; + gchar *sha1; + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism)); + g_return_if_fail (m->priv->is_client && !m->priv->is_server); + g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA); + + tokens = NULL; + cookie = NULL; + client_challenge = NULL; + + tokens = g_strsplit (data, " ", 0); + if (g_strv_length (tokens) != 3) + { + g_warning ("Malformed data `%s'", data); + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + goto out; + } + + cookie_context = tokens[0]; + cookie_id = g_ascii_strtoll (tokens[1], &endp, 10); + if (*endp != '\0') + { + g_warning ("Malformed cookie_id `%s'", tokens[1]); + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + goto out; + } + server_challenge = tokens[2]; + + error = NULL; + cookie = keyring_lookup_entry (cookie_context, cookie_id, &error); + if (cookie == NULL) + { + g_warning ("Problems looking up entry in keyring: %s", error->message); + g_error_free (error); + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; + goto out; + } + + client_challenge = random_ascii_string (16); + sha1 = generate_sha1 (server_challenge, client_challenge, cookie); + m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1); + g_free (sha1); + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND; + + out: + g_strfreev (tokens); + g_free (cookie); + g_free (client_challenge); +} + +static gchar * +mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL); + g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + + g_assert (m->priv->to_send != NULL); + + m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED; + + return g_strdup (m->priv->to_send); +} + +static void +mechanism_client_shutdown (GDBusAuthMechanism *mechanism) +{ + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism)); + g_return_if_fail (m->priv->is_client && !m->priv->is_server); + + m->priv->is_client = FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusauthmechanismsha1.h b/gio/gdbusauthmechanismsha1.h new file mode 100644 index 000000000..67839fd11 --- /dev/null +++ b/gio/gdbusauthmechanismsha1.h @@ -0,0 +1,82 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +#if !defined (GIO_COMPILATION) +#error "gdbusauthmechanismsha1.h is a private header file." +#endif + +#ifndef __G_DBUS_AUTH_MECHANISM_SHA1_H__ +#define __G_DBUS_AUTH_MECHANISM_SHA1_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_AUTH_MECHANISM_SHA1 (_g_dbus_auth_mechanism_sha1_get_type ()) +#define G_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1)) +#define G_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class)) +#define G_DBUS_AUTH_MECHANISM_SHA1_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class)) +#define G_IS_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1)) +#define G_IS_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1)) + +typedef struct _GDBusAuthMechanismSha1 GDBusAuthMechanismSha1; +typedef struct _GDBusAuthMechanismSha1Class GDBusAuthMechanismSha1Class; +typedef struct _GDBusAuthMechanismSha1Private GDBusAuthMechanismSha1Private; + +struct _GDBusAuthMechanismSha1Class +{ + /*< private >*/ + GDBusAuthMechanismClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); + void (*_g_reserved9) (void); + void (*_g_reserved10) (void); + void (*_g_reserved11) (void); + void (*_g_reserved12) (void); + void (*_g_reserved13) (void); + void (*_g_reserved14) (void); + void (*_g_reserved15) (void); + void (*_g_reserved16) (void); +}; + +struct _GDBusAuthMechanismSha1 +{ + GDBusAuthMechanism parent_instance; + GDBusAuthMechanismSha1Private *priv; +}; + +GType _g_dbus_auth_mechanism_sha1_get_type (void) G_GNUC_CONST; + + +G_END_DECLS + +#endif /* __G_DBUS_AUTH_MECHANISM_SHA1_H__ */ diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c new file mode 100644 index 000000000..f0411aba8 --- /dev/null +++ b/gio/gdbusauthobserver.c @@ -0,0 +1,218 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusauthobserver.h" +#include "gio-marshal.h" +#include "gcredentials.h" +#include "gioenumtypes.h" +#include "giostream.h" + +/** + * SECTION:gdbusauthobserver + * @short_description: Object used for authenticating connections + * @include: gdbus/gdbus.h + * + * The #GDBusAuthObserver type provides a mechanism for participating + * in how a #GDBusServer (or a #GDBusConnection) authenticates remote + * peers. Simply instantiate a #GDBusAuthObserver and connect to the + * signals you are interested in. Note that new signals may be added + * in the future + * + * For example, if you only want to allow D-Bus connections from + * processes owned by the same uid as the server, you would do this: + * Controlling Authentication + * static gboolean + * on_deny_authenticated_peer (GDBusAuthObserver *observer, + * GIOStream *stream, + * GCredentials *credentials, + * gpointer user_data) + * { + * gboolean deny; + * deny = TRUE; + * if (credentials != NULL && + * g_credentials_has_unix_user (credentials) && + * g_credentials_get_unix_user (credentials) == getuid ()) + * deny = FALSE; + * return deny; + * } + * + * static gboolean + * on_new_connection (GDBusServer *server, + * GDBusConnection *connection, + * gpointer user_data) + * { + * /* Guaranteed here that @connection is from a process owned by the same user */ + * } + * + * [...] + * + * GDBusAuthObserver *observer; + * GDBusServer *server; + * GError *error; + * + * error = NULL; + * observer = g_dbus_auth_observer_new (); + * server = g_dbus_server_new_sync ("unix:tmpdir=/tmp/my-app-name", + * G_DBUS_SERVER_FLAGS_NONE, + * observer, + * NULL, /* GCancellable */ + * &error); + * g_signal_connect (observer, + * "deny-authenticated-peer", + * G_CALLBACK (on_deny_authenticated_peer), + * NULL); + * g_signal_connect (server, + * "new-connection", + * G_CALLBACK (on_new_connection), + * NULL); + * g_object_unref (observer); + * g_dbus_server_start (server); + * + */ + +struct _GDBusAuthObserverPrivate +{ + gint foo; +}; + +enum +{ + DENY_AUTHENTICATED_PEER_SIGNAL, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GDBusAuthObserver, g_dbus_auth_observer, G_TYPE_OBJECT); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_dbus_auth_observer_finalize (GObject *object) +{ + //GDBusAuthObserver *observer = G_DBUS_AUTH_OBSERVER (object); + + if (G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize (object); +} + +static gboolean +g_dbus_auth_observer_deny_authenticated_peer_real (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials) +{ + return FALSE; +} + +static void +g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_auth_observer_finalize; + + klass->deny_authenticated_peer = g_dbus_auth_observer_deny_authenticated_peer_real; + + /** + * GDBusAuthObserver::deny-authenticated-peer: + * @observer: The #GDBusAuthObserver emitting the signal. + * @stream: A #GIOStream for the #GDBusConnection. + * @credentials: Credentials received from the peer or %NULL. + * + * Emitted to check if a peer that is successfully authenticated + * should be denied. + * + * Returns: %TRUE if the peer should be denied, %FALSE otherwise. + */ + signals[DENY_AUTHENTICATED_PEER_SIGNAL] = + g_signal_new ("deny-authenticated-peer", + G_TYPE_DBUS_AUTH_OBSERVER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusAuthObserverClass, deny_authenticated_peer), + g_signal_accumulator_true_handled, + NULL, /* accu_data */ + _gio_marshal_BOOLEAN__OBJECT_OBJECT, + G_TYPE_BOOLEAN, + 2, + G_TYPE_IO_STREAM, + G_TYPE_CREDENTIALS); + + + g_type_class_add_private (klass, sizeof (GDBusAuthObserverPrivate)); +} + +static void +g_dbus_auth_observer_init (GDBusAuthObserver *observer) +{ + /* not used for now */ + observer->priv = G_TYPE_INSTANCE_GET_PRIVATE (observer, + G_TYPE_DBUS_AUTH_OBSERVER, + GDBusAuthObserverPrivate);; +} + +/** + * g_dbus_auth_observer_new: + * + * Creates a new #GDBusAuthObserver object. + * + * Returns: A #GDBusAuthObserver. Free with g_object_unref(). + */ +GDBusAuthObserver * +g_dbus_auth_observer_new (void) +{ + return g_object_new (G_TYPE_DBUS_AUTH_OBSERVER, NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_auth_observer_deny_authenticated_peer: + * @observer: A #GDBusAuthObserver. + * @stream: A #GIOStream for the #GDBusConnection. + * @credentials: Credentials received from the peer or %NULL. + * + * Emits the #GDBusAuthObserver::deny-authenticated-peer signal on @observer. + * + * Returns: %TRUE if the peer should be denied, %FALSE otherwise. + */ +gboolean +g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials) +{ + gboolean denied; + + denied = FALSE; + g_signal_emit (observer, + signals[DENY_AUTHENTICATED_PEER_SIGNAL], + 0, + stream, + credentials, + &denied); + return denied; +} + + diff --git a/gio/gdbusauthobserver.h b/gio/gdbusauthobserver.h new file mode 100644 index 000000000..6ed52e8f9 --- /dev/null +++ b/gio/gdbusauthobserver.h @@ -0,0 +1,100 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_AUTH_OBSERVER_H__ +#define __G_DBUS_AUTH_OBSERVER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_AUTH_OBSERVER (g_dbus_auth_observer_get_type ()) +#define G_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserver)) +#define G_DBUS_AUTH_OBSERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserverClass)) +#define G_DBUS_AUTH_OBSERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserverClass)) +#define G_IS_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_OBSERVER)) +#define G_IS_DBUS_AUTH_OBSERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_OBSERVER)) + +typedef struct _GDBusAuthObserverClass GDBusAuthObserverClass; +typedef struct _GDBusAuthObserverPrivate GDBusAuthObserverPrivate; + + +/** + * GDBusAuthObserverClass: + * @deny_authenticated_peer: Signal class handler for the #GDBusAuthObserver::deny-authenticated-peer signal. + * + * Class structure for #GDBusAuthObserverClass. + */ +struct _GDBusAuthObserverClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + + /* Signals */ + gboolean (*deny_authenticated_peer) (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials); + + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); + void (*_g_reserved9) (void); + void (*_g_reserved10) (void); + void (*_g_reserved11) (void); + void (*_g_reserved12) (void); + void (*_g_reserved13) (void); + void (*_g_reserved14) (void); + void (*_g_reserved15) (void); + void (*_g_reserved16) (void); +}; + +/** + * GDBusAuthObserver: + * + * The #GDBusAuthObserver structure contains only private data and + * should only be accessed using the provided API. + */ +struct _GDBusAuthObserver +{ + GObject parent_instance; + GDBusAuthObserverPrivate *priv; +}; + +GType g_dbus_auth_observer_get_type (void) G_GNUC_CONST; +GDBusAuthObserver *g_dbus_auth_observer_new (void); +gboolean g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials); + +G_END_DECLS + +#endif /* _G_DBUS_AUTH_OBSERVER_H__ */ diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c new file mode 100644 index 000000000..0bcd58f89 --- /dev/null +++ b/gio/gdbusconnection.c @@ -0,0 +1,5280 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +/* + * TODO for GDBus: + * + * - would be nice to expose GDBusAuthMechanism and an extension point + * + * - need to expose an extension point for resolving D-Bus address and + * turning them into GIOStream objects. This will allow us to implement + * e.g. X11 D-Bus transports without dlopen()'ing or linking against + * libX11 from libgio. + * - see g_dbus_address_connect() in gdbusaddress.c + * + * - would be cute to use kernel-specific APIs to resolve fds for + * debug output when using G_DBUS_DEBUG=messages, e.g. in addition to + * + * fd 21: dev=8:1,mode=0100644,ino=1171231,uid=0,gid=0,rdev=0:0,size=234,atime=1273070640,mtime=1267126160,ctime=1267126160 + * + * maybe we can show more information about what fd 21 really is. + * Ryan suggests looking in /proc/self/fd for clues / symlinks! + * Initial experiments on Linux 2.6 suggests that the symlink looks + * like this: + * + * 3 -> /proc/18068/fd + * + * e.g. not of much use. + */ + +#include "config.h" + +#include + +#include + +#ifdef G_OS_UNIX +#include +#include +#endif + +#include +#include +#include + +#include "gdbusauth.h" + +#include "gdbusutils.h" +#include "gdbusaddress.h" +#include "gdbusmessage.h" +#include "gdbusconnection.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" +#include "gdbusintrospection.h" +#include "gdbusmethodinvocation.h" +#include "gdbusprivate.h" +#include "gdbusauthobserver.h" +#include "gio-marshal.h" + +/** + * SECTION:gdbusconnection + * @short_description: D-Bus Connections + * @include: gdbus/gdbus.h + * + * + * This class is rarely used directly in D-Bus clients. If you are + * writing an D-Bus client, it is often easier to use the + * g_bus_own_name(), g_bus_watch_name() or g_bus_watch_proxy() APIs. + * + * + * The #GDBusConnection type is used for D-Bus connections to remote + * peers such as a message buses. + * + * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT + * + * D-Bus subtree exampleFIXME: MISSING XINCLUDE CONTENT + * + * D-Bus UNIX File Descriptor exampleFIXME: MISSING XINCLUDE CONTENT + */ + +/* ---------------------------------------------------------------------------------------------------- */ + +G_LOCK_DEFINE_STATIC (message_bus_lock); + +static GDBusConnection *the_session_bus = NULL; +static GDBusConnection *the_system_bus = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +_g_strv_has_string (const gchar* const * haystack, + const gchar *needle) +{ + guint n; + + for (n = 0; haystack != NULL && haystack[n] != NULL; n++) + { + if (g_strcmp0 (haystack[n], needle) == 0) + return TRUE; + } + return FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_WIN32 +#define CONNECTION_ENSURE_LOCK(obj) do { ; } while (FALSE) +#else +// TODO: for some reason this doesn't work on Windows +#define CONNECTION_ENSURE_LOCK(obj) do { \ + if (G_UNLIKELY (g_mutex_trylock((obj)->priv->lock))) \ + { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "CONNECTION_ENSURE_LOCK: GDBusConnection object lock is not locked"); \ + } \ + } while (FALSE) +#endif + +#define CONNECTION_LOCK(obj) do { \ + g_mutex_lock ((obj)->priv->lock); \ + } while (FALSE) + +#define CONNECTION_UNLOCK(obj) do { \ + g_mutex_unlock ((obj)->priv->lock); \ + } while (FALSE) + +struct _GDBusConnectionPrivate +{ + /* ------------------------------------------------------------------------ */ + /* -- General object state ------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ + + /* object-wide lock */ + GMutex *lock; + + /* A lock used in the init() method of the GInitable interface - see comments + * in initable_init() for why a separate lock is needed + */ + GMutex *init_lock; + + /* Set (by loading the contents of /var/lib/dbus/machine-id) the first time + * someone calls org.freedesktop.DBus.GetMachineId() + */ + gchar *machine_id; + + /* The underlying stream used for communication */ + GIOStream *stream; + + /* The object used for authentication (if any) */ + GDBusAuth *auth; + + /* Set to TRUE if the connection has been closed */ + gboolean closed; + + /* Last serial used */ + guint32 last_serial; + + /* The object used to send/receive message */ + GDBusWorker *worker; + + /* If connected to a message bus, this contains the unique name assigned to + * us by the bus (e.g. ":1.42") + */ + gchar *bus_unique_name; + + /* The GUID returned by the other side if we authenticed as a client or + * the GUID to use if authenticating as a server + */ + gchar *guid; + + /* set to TRUE exactly when initable_init() has finished running */ + gboolean is_initialized; + + /* If the connection could not be established during initable_init(), this GError will set */ + GError *initialization_error; + + /* The result of g_main_context_get_thread_default() when the object + * was created (the GObject _init() function) - this is used for delivery + * of the :closed GObject signal. + */ + GMainContext *main_context_at_construction; + + /* construct properties */ + gchar *address; + GDBusConnectionFlags flags; + + /* Map used for managing method replies */ + GHashTable *map_method_serial_to_send_message_data; /* guint32 -> SendMessageData* */ + + /* Maps used for managing signal subscription */ + GHashTable *map_rule_to_signal_data; /* gchar* -> SignalData */ + GHashTable *map_id_to_signal_data; /* guint -> SignalData */ + GHashTable *map_sender_to_signal_data_array; /* gchar* -> GPtrArray* of SignalData */ + + /* Maps used for managing exported objects and subtrees */ + GHashTable *map_object_path_to_eo; /* gchar* -> ExportedObject* */ + GHashTable *map_id_to_ei; /* guint -> ExportedInterface* */ + GHashTable *map_object_path_to_es; /* gchar* -> ExportedSubtree* */ + GHashTable *map_id_to_es; /* guint -> ExportedSubtree* */ + + /* Structure used for message filters */ + GPtrArray *filters; + + /* Whether to exit on close */ + gboolean exit_on_close; + + /* Capabilities negotiated during authentication */ + GDBusCapabilityFlags capabilities; + + GDBusAuthObserver *authentication_observer; + GCredentials *crendentials; +}; + +typedef struct ExportedObject ExportedObject; +static void exported_object_free (ExportedObject *eo); + +typedef struct ExportedSubtree ExportedSubtree; +static void exported_subtree_free (ExportedSubtree *es); + +enum +{ + CLOSED_SIGNAL, + LAST_SIGNAL, +}; + +enum +{ + PROP_0, + PROP_STREAM, + PROP_ADDRESS, + PROP_FLAGS, + PROP_GUID, + PROP_UNIQUE_NAME, + PROP_CLOSED, + PROP_EXIT_ON_CLOSE, + PROP_CAPABILITY_FLAGS, + PROP_AUTHENTICATION_OBSERVER, +}; + +static void distribute_signals (GDBusConnection *connection, + GDBusMessage *message); + +static void distribute_method_call (GDBusConnection *connection, + GDBusMessage *message); + +static gboolean handle_generic_unlocked (GDBusConnection *connection, + GDBusMessage *message); + + +static void purge_all_signal_subscriptions (GDBusConnection *connection); +static void purge_all_filters (GDBusConnection *connection); + +#define _G_ENSURE_LOCK(name) do { \ + if (G_UNLIKELY (G_TRYLOCK(name))) \ + { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "_G_ENSURE_LOCK: Lock `" #name "' is not locked"); \ + } \ + } while (FALSE) \ + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void initable_iface_init (GInitableIface *initable_iface); +static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); + +G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) + ); + +static void +g_dbus_connection_dispose (GObject *object) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (object); + + G_LOCK (message_bus_lock); + //g_debug ("disposing %p", connection); + if (connection == the_session_bus) + { + the_session_bus = NULL; + } + else if (connection == the_system_bus) + { + the_system_bus = NULL; + } + if (connection->priv->worker != NULL) + { + _g_dbus_worker_stop (connection->priv->worker); + connection->priv->worker = NULL; + } + G_UNLOCK (message_bus_lock); + + if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose != NULL) + G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose (object); +} + +static void +g_dbus_connection_finalize (GObject *object) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (object); + + if (connection->priv->authentication_observer != NULL) + g_object_unref (connection->priv->authentication_observer); + + if (connection->priv->auth != NULL) + g_object_unref (connection->priv->auth); + + //g_debug ("finalizing %p", connection); + if (connection->priv->stream != NULL) + { + /* We don't really care if closing the stream succeeds or not */ + g_io_stream_close_async (connection->priv->stream, + G_PRIORITY_DEFAULT, + NULL, /* GCancellable */ + NULL, /* GAsyncReadyCallback */ + NULL); /* userdata */ + g_object_unref (connection->priv->stream); + connection->priv->stream = NULL; + } + + g_free (connection->priv->address); + + g_free (connection->priv->guid); + g_free (connection->priv->bus_unique_name); + + if (connection->priv->initialization_error != NULL) + g_error_free (connection->priv->initialization_error); + + g_hash_table_unref (connection->priv->map_method_serial_to_send_message_data); + + purge_all_signal_subscriptions (connection); + g_hash_table_unref (connection->priv->map_rule_to_signal_data); + g_hash_table_unref (connection->priv->map_id_to_signal_data); + g_hash_table_unref (connection->priv->map_sender_to_signal_data_array); + + g_hash_table_unref (connection->priv->map_id_to_ei); + g_hash_table_unref (connection->priv->map_object_path_to_eo); + g_hash_table_unref (connection->priv->map_id_to_es); + g_hash_table_unref (connection->priv->map_object_path_to_es); + + purge_all_filters (connection); + g_ptr_array_unref (connection->priv->filters); + + if (connection->priv->main_context_at_construction != NULL) + g_main_context_unref (connection->priv->main_context_at_construction); + + g_free (connection->priv->machine_id); + + g_mutex_free (connection->priv->init_lock); + g_mutex_free (connection->priv->lock); + + if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object); +} + +static void +g_dbus_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (object); + + switch (prop_id) + { + case PROP_STREAM: + g_value_set_object (value, g_dbus_connection_get_stream (connection)); + break; + + case PROP_GUID: + g_value_set_string (value, g_dbus_connection_get_guid (connection)); + break; + + case PROP_UNIQUE_NAME: + g_value_set_string (value, g_dbus_connection_get_unique_name (connection)); + break; + + case PROP_CLOSED: + g_value_set_boolean (value, g_dbus_connection_is_closed (connection)); + break; + + case PROP_EXIT_ON_CLOSE: + g_value_set_boolean (value, g_dbus_connection_get_exit_on_close (connection)); + break; + + case PROP_CAPABILITY_FLAGS: + g_value_set_flags (value, g_dbus_connection_get_capabilities (connection)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (object); + + switch (prop_id) + { + case PROP_STREAM: + connection->priv->stream = g_value_dup_object (value); + break; + + case PROP_GUID: + connection->priv->guid = g_value_dup_string (value); + break; + + case PROP_ADDRESS: + connection->priv->address = g_value_dup_string (value); + break; + + case PROP_FLAGS: + connection->priv->flags = g_value_get_flags (value); + break; + + case PROP_EXIT_ON_CLOSE: + g_dbus_connection_set_exit_on_close (connection, g_value_get_boolean (value)); + break; + + case PROP_AUTHENTICATION_OBSERVER: + connection->priv->authentication_observer = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_connection_real_closed (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error) +{ + if (remote_peer_vanished && connection->priv->exit_on_close) + { + g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC); + raise (SIGTERM); + } +} + +static void +g_dbus_connection_class_init (GDBusConnectionClass *klass) +{ + GObjectClass *gobject_class; + + g_type_class_add_private (klass, sizeof (GDBusConnectionPrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_connection_finalize; + gobject_class->dispose = g_dbus_connection_dispose; + gobject_class->set_property = g_dbus_connection_set_property; + gobject_class->get_property = g_dbus_connection_get_property; + + klass->closed = g_dbus_connection_real_closed; + + /** + * GDBusConnection:stream: + * + * The underlying #GIOStream used for I/O. + */ + g_object_class_install_property (gobject_class, + PROP_STREAM, + g_param_spec_object ("stream", + _("IO Stream"), + _("The underlying streams used for I/O"), + G_TYPE_IO_STREAM, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:address: + * + * A D-Bus address specifying potential endpoints that can be used + * when establishing the connection. + */ + g_object_class_install_property (gobject_class, + PROP_ADDRESS, + g_param_spec_string ("address", + _("Address"), + _("D-Bus address specifying potential socket endpoints"), + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:flags: + * + * Flags from the #GDBusConnectionFlags enumeration. + */ + g_object_class_install_property (gobject_class, + PROP_FLAGS, + g_param_spec_flags ("flags", + _("Flags"), + _("Flags"), + G_TYPE_DBUS_CONNECTION_FLAGS, + G_DBUS_CONNECTION_FLAGS_NONE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:guid: + * + * The GUID of the peer performing the role of server when + * authenticating. + * + * If you are constructing a #GDBusConnection and pass + * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER in the + * #GDBusConnection:flags property then you MUST also set this + * property to a valid guid. + * + * If you are constructing a #GDBusConnection and pass + * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT in the + * #GDBusConnection:flags property you will be able to read the GUID + * of the other peer here after the connection has been succesfully + * initialized. + */ + g_object_class_install_property (gobject_class, + PROP_GUID, + g_param_spec_string ("guid", + _("GUID"), + _("GUID of the server peer"), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:unique-name: + * + * The unique name as assigned by the message bus or %NULL if the + * connection is not open or not a message bus connection. + */ + g_object_class_install_property (gobject_class, + PROP_UNIQUE_NAME, + g_param_spec_string ("unique-name", + _("unique-name"), + _("Unique name of bus connection"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:closed: + * + * A boolean specifying whether the connection has been closed. + */ + g_object_class_install_property (gobject_class, + PROP_CLOSED, + g_param_spec_boolean ("closed", + _("Closed"), + _("Whether the connection is closed"), + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:exit-on-close: + * + * A boolean specifying whether the process will be terminated (by + * calling raise(SIGTERM)) if the connection + * is closed by the remote peer. + */ + g_object_class_install_property (gobject_class, + PROP_EXIT_ON_CLOSE, + g_param_spec_boolean ("exit-on-close", + _("Exit on close"), + _("Whether the process is terminated when the connection is closed"), + FALSE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:capabilities: + * + * Flags from the #GDBusCapabilityFlags enumeration + * representing connection features negotiated with the other peer. + */ + g_object_class_install_property (gobject_class, + PROP_CAPABILITY_FLAGS, + g_param_spec_flags ("capabilities", + _("Capabilities"), + _("Capabilities"), + G_TYPE_DBUS_CAPABILITY_FLAGS, + G_DBUS_CAPABILITY_FLAGS_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection:authentication-observer: + * + * A #GDBusAuthObserver object to assist in the authentication process or %NULL. + */ + g_object_class_install_property (gobject_class, + PROP_AUTHENTICATION_OBSERVER, + g_param_spec_object ("authentication-observer", + _("Authentication Observer"), + _("Object used to assist in the authentication process"), + G_TYPE_DBUS_AUTH_OBSERVER, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusConnection::closed: + * @connection: The #GDBusConnection emitting the signal. + * @remote_peer_vanished: %TRUE if @connection is closed because the + * remote peer closed its end of the connection. + * @error: A #GError with more details about the event or %NULL. + * + * Emitted when the connection is closed. + * + * The cause of this event can be + * + * + * If g_dbus_connection_close() is called. In this case + * @remote_peer_vanished is set to %FALSE and @error is %NULL. + * + * + * If the remote peer closes the connection. In this case + * @remote_peer_vanished is set to %TRUE and @error is set. + * + * + * If the remote peer sends invalid or malformed data. In this + * case @remote_peer_vanished is set to %FALSE and @error + * is set. + * + * + * + * Upon receiving this signal, you should give up your reference to + * @connection. You are guaranteed that this signal is emitted only + * once. + */ + signals[CLOSED_SIGNAL] = g_signal_new ("closed", + G_TYPE_DBUS_CONNECTION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusConnectionClass, closed), + NULL, + NULL, + _gio_marshal_VOID__BOOLEAN_BOXED, + G_TYPE_NONE, + 2, + G_TYPE_BOOLEAN, + G_TYPE_ERROR); +} + +static void +g_dbus_connection_init (GDBusConnection *connection) +{ + connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_DBUS_CONNECTION, GDBusConnectionPrivate); + + connection->priv->lock = g_mutex_new (); + connection->priv->init_lock = g_mutex_new (); + + connection->priv->map_method_serial_to_send_message_data = g_hash_table_new (g_direct_hash, g_direct_equal); + + connection->priv->map_rule_to_signal_data = g_hash_table_new (g_str_hash, + g_str_equal); + connection->priv->map_id_to_signal_data = g_hash_table_new (g_direct_hash, + g_direct_equal); + connection->priv->map_sender_to_signal_data_array = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + connection->priv->map_object_path_to_eo = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) exported_object_free); + + connection->priv->map_id_to_ei = g_hash_table_new (g_direct_hash, + g_direct_equal); + + connection->priv->map_object_path_to_es = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) exported_subtree_free); + + connection->priv->map_id_to_es = g_hash_table_new (g_direct_hash, + g_direct_equal); + + connection->priv->main_context_at_construction = g_main_context_get_thread_default (); + if (connection->priv->main_context_at_construction != NULL) + g_main_context_ref (connection->priv->main_context_at_construction); + + connection->priv->filters = g_ptr_array_new (); +} + +GIOStream * +g_dbus_connection_get_stream (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + return connection->priv->stream; +} + + +/** + * g_dbus_connection_is_closed: + * @connection: A #GDBusConnection. + * + * Gets whether @connection is closed. + * + * Returns: %TRUE if the connection is closed, %FALSE otherwise. + **/ +gboolean +g_dbus_connection_is_closed (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + return connection->priv->closed; +} + +/** + * g_dbus_connection_get_capabilities: + * @connection: A #GDBusConnection. + * + * Gets the capabilities negotiated with the remote peer + * + * Returns: One or more flags from the #GDBusCapabilityFlags enumeration. + */ +GDBusCapabilityFlags +g_dbus_connection_get_capabilities (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_DBUS_CAPABILITY_FLAGS_NONE); + return connection->priv->capabilities; +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GDBusConnection *connection; + GError *error; + gboolean remote_peer_vanished; +} EmitClosedData; + +static void +emit_closed_data_free (EmitClosedData *data) +{ + g_object_unref (data->connection); + if (data->error != NULL) + g_error_free (data->error); + g_free (data); +} + +static gboolean +emit_closed_in_idle (gpointer user_data) +{ + EmitClosedData *data = user_data; + gboolean result; + + g_object_notify (G_OBJECT (data->connection), "closed"); + g_signal_emit (data->connection, + signals[CLOSED_SIGNAL], + 0, + data->remote_peer_vanished, + data->error, + &result); + return FALSE; +} + +/* Can be called from any thread, must hold lock */ +static void +set_closed_unlocked (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error) +{ + GSource *idle_source; + EmitClosedData *data; + + CONNECTION_ENSURE_LOCK (connection); + + g_assert (!connection->priv->closed); + + connection->priv->closed = TRUE; + + data = g_new0 (EmitClosedData, 1); + data->connection = g_object_ref (connection); + data->remote_peer_vanished = remote_peer_vanished; + data->error = error != NULL ? g_error_copy (error) : NULL; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + emit_closed_in_idle, + data, + (GDestroyNotify) emit_closed_data_free); + g_source_attach (idle_source, connection->priv->main_context_at_construction); + g_source_unref (idle_source); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_close: + * @connection: A #GDBusConnection. + * + * Closes @connection. Note that this never causes the process to + * exit (this might only happen if the other end of a shared message + * bus connection disconnects). + * + * If @connection is already closed, this method does nothing. + */ +void +g_dbus_connection_close (GDBusConnection *connection) +{ + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + CONNECTION_LOCK (connection); + if (!connection->priv->closed) + { + GError *error = NULL; + + /* TODO: do this async */ + //g_debug ("closing connection %p's stream %p", connection, connection->priv->stream); + if (!g_io_stream_close (connection->priv->stream, NULL, &error)) + { + g_warning ("Error closing stream: %s", error->message); + g_error_free (error); + } + + set_closed_unlocked (connection, FALSE, NULL); + } + CONNECTION_UNLOCK (connection); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +g_dbus_connection_send_message_unlocked (GDBusConnection *connection, + GDBusMessage *message, + volatile guint32 *out_serial, + GError **error) +{ + guchar *blob; + gsize blob_size; + guint32 serial_to_use; + gboolean ret; + + CONNECTION_ENSURE_LOCK (connection); + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); + + /* TODO: check all necessary headers are present */ + + ret = FALSE; + blob = NULL; + + if (out_serial != NULL) + *out_serial = 0; + + if (connection->priv->closed) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + _("The connection is closed")); + goto out; + } + + blob = g_dbus_message_to_blob (message, + &blob_size, + error); + if (blob == NULL) + goto out; + + serial_to_use = ++connection->priv->last_serial; /* TODO: handle overflow */ + + switch (blob[0]) + { + case 'l': + ((guint32 *) blob)[2] = GUINT32_TO_LE (serial_to_use); + break; + case 'B': + ((guint32 *) blob)[2] = GUINT32_TO_BE (serial_to_use); + break; + default: + g_assert_not_reached (); + break; + } + +#if 0 + g_printerr ("Writing message of %" G_GSIZE_FORMAT " bytes (serial %d) on %p:\n", + blob_size, serial_to_use, connection); + g_printerr ("----\n"); + hexdump (blob, blob_size); + g_printerr ("----\n"); +#endif + + /* TODO: use connection->priv->auth to encode the blob */ + + if (out_serial != NULL) + { + *out_serial = serial_to_use; + } + g_dbus_message_set_serial (message, serial_to_use); + + _g_dbus_worker_send_message (connection->priv->worker, + message, + (gchar*) blob, + blob_size); + blob = NULL; /* since _g_dbus_worker_send_message() steals the blob */ + + ret = TRUE; + + out: + g_free (blob); + + return ret; +} + +/** + * g_dbus_connection_send_message: + * @connection: A #GDBusConnection. + * @message: A #GDBusMessage + * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. + * @error: Return location for error or %NULL. + * + * Asynchronously sends @message to the peer represented by @connection. + * + * If @out_serial is not %NULL, then the serial number assigned to + * @message by @connection will be written to this location prior to + * submitting the message to the underlying transport. + * + * If @connection is closed then the operation will fail with + * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will + * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, + * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. + * + * See and for an example of how to use this + * low-level API to send and receive UNIX file descriptors. + * + * Returns: %TRUE if the message was well-formed and queued for + * transmission, %FALSE if @error is set. + */ +gboolean +g_dbus_connection_send_message (GDBusConnection *connection, + GDBusMessage *message, + volatile guint32 *out_serial, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + CONNECTION_LOCK (connection); + ret = g_dbus_connection_send_message_unlocked (connection, message, out_serial, error); + CONNECTION_UNLOCK (connection); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + volatile gint ref_count; + GDBusConnection *connection; + guint32 serial; + GSimpleAsyncResult *simple; + + GMainContext *main_context; + + GCancellable *cancellable; + + gulong cancellable_handler_id; + + GSource *timeout_source; + + gboolean delivered; +} SendMessageData; + +static SendMessageData * +send_message_data_ref (SendMessageData *data) +{ + g_atomic_int_inc (&data->ref_count); + return data; +} + +static void +send_message_data_unref (SendMessageData *data) +{ + if (g_atomic_int_dec_and_test (&data->ref_count)) + { + g_assert (data->timeout_source == NULL); + g_assert (data->simple == NULL); + g_assert (data->cancellable_handler_id == 0); + g_object_unref (data->connection); + if (data->cancellable != NULL) + g_object_unref (data->cancellable); + if (data->main_context != NULL) + g_main_context_unref (data->main_context); + g_free (data); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* can be called from any thread with lock held - caller must have prepared GSimpleAsyncResult already */ +static void +send_message_with_reply_deliver (SendMessageData *data) +{ + CONNECTION_ENSURE_LOCK (data->connection); + + g_assert (!data->delivered); + + data->delivered = TRUE; + + g_simple_async_result_complete_in_idle (data->simple); + g_object_unref (data->simple); + data->simple = NULL; + + if (data->timeout_source != NULL) + { + g_source_destroy (data->timeout_source); + data->timeout_source = NULL; + } + if (data->cancellable_handler_id > 0) + { + g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id); + data->cancellable_handler_id = 0; + } + + g_warn_if_fail (g_hash_table_remove (data->connection->priv->map_method_serial_to_send_message_data, + GUINT_TO_POINTER (data->serial))); + + send_message_data_unref (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* must hold lock */ +static void +send_message_data_deliver_reply_unlocked (SendMessageData *data, + GDBusMessage *reply) +{ + if (data->delivered) + goto out; + + g_simple_async_result_set_op_res_gpointer (data->simple, + g_object_ref (reply), + g_object_unref); + + send_message_with_reply_deliver (data); + + out: + ; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +send_message_with_reply_cancelled_idle_cb (gpointer user_data) +{ + SendMessageData *data = user_data; + + CONNECTION_LOCK (data->connection); + if (data->delivered) + goto out; + + g_simple_async_result_set_error (data->simple, + G_IO_ERROR, + G_IO_ERROR_CANCELLED, + _("Operation was cancelled")); + + send_message_with_reply_deliver (data); + + out: + CONNECTION_UNLOCK (data->connection); + return FALSE; +} + +/* Can be called from any thread with or without lock held */ +static void +send_message_with_reply_cancelled_cb (GCancellable *cancellable, + gpointer user_data) +{ + SendMessageData *data = user_data; + GSource *idle_source; + + /* postpone cancellation to idle handler since we may be called directly + * via g_cancellable_connect() (e.g. holding lock) + */ + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + send_message_with_reply_cancelled_idle_cb, + send_message_data_ref (data), + (GDestroyNotify) send_message_data_unref); + g_source_attach (idle_source, data->main_context); + g_source_unref (idle_source); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +send_message_with_reply_timeout_cb (gpointer user_data) +{ + SendMessageData *data = user_data; + + CONNECTION_LOCK (data->connection); + if (data->delivered) + goto out; + + g_simple_async_result_set_error (data->simple, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + _("Timeout was reached")); + + send_message_with_reply_deliver (data); + + out: + CONNECTION_UNLOCK (data->connection); + + return FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connection, + GDBusMessage *message, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + SendMessageData *data; + GError *error; + volatile guint32 serial; + + data = NULL; + + if (out_serial == NULL) + out_serial = &serial; + + if (timeout_msec == -1) + timeout_msec = 30 * 1000; /* TODO: check 30 secs is the default timeout */ + + simple = g_simple_async_result_new (G_OBJECT (connection), + callback, + user_data, + g_dbus_connection_send_message_with_reply); + + if (g_cancellable_is_cancelled (cancellable)) + { + g_simple_async_result_set_error (simple, + G_IO_ERROR, + G_IO_ERROR_CANCELLED, + _("Operation was cancelled")); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + goto out; + } + + if (connection->priv->closed) + { + g_simple_async_result_set_error (simple, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + _("The connection is closed")); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + goto out; + } + + error = NULL; + if (!g_dbus_connection_send_message_unlocked (connection, message, out_serial, &error)) + { + g_simple_async_result_set_from_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + goto out; + } + + data = g_new0 (SendMessageData, 1); + data->ref_count = 1; + data->connection = g_object_ref (connection); + data->simple = simple; + data->serial = *out_serial; + data->main_context = g_main_context_get_thread_default (); + if (data->main_context != NULL) + g_main_context_ref (data->main_context); + + if (cancellable != NULL) + { + data->cancellable = g_object_ref (cancellable); + data->cancellable_handler_id = g_cancellable_connect (cancellable, + G_CALLBACK (send_message_with_reply_cancelled_cb), + send_message_data_ref (data), + (GDestroyNotify) send_message_data_unref); + g_object_set_data_full (G_OBJECT (simple), + "cancellable", + g_object_ref (cancellable), + (GDestroyNotify) g_object_unref); + } + + data->timeout_source = g_timeout_source_new (timeout_msec); + g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); + g_source_set_callback (data->timeout_source, + send_message_with_reply_timeout_cb, + send_message_data_ref (data), + (GDestroyNotify) send_message_data_unref); + g_source_attach (data->timeout_source, data->main_context); + g_source_unref (data->timeout_source); + + g_hash_table_insert (connection->priv->map_method_serial_to_send_message_data, + GUINT_TO_POINTER (*out_serial), + data); + + out: + ; +} + +/** + * g_dbus_connection_send_message_with_reply: + * @connection: A #GDBusConnection. + * @message: A #GDBusMessage. + * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. + * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't + * care about the result. + * @user_data: The data to pass to @callback. + * + * Asynchronously sends @message to the peer represented by @connection. + * + * If @out_serial is not %NULL, then the serial number assigned to + * @message by @connection will be written to this location prior to + * submitting the message to the underlying transport. + * + * If @connection is closed then the operation will fail with + * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will + * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, + * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. + * + * This is an asynchronous method. When the operation is finished, @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_connection_send_message_with_reply_finish() to get the result of the operation. + * See g_dbus_connection_send_message_with_reply_sync() for the synchronous version. + * + * See and for an example of how to use this + * low-level API to send and receive UNIX file descriptors. + */ +void +g_dbus_connection_send_message_with_reply (GDBusConnection *connection, + GDBusMessage *message, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); + + CONNECTION_LOCK (connection); + g_dbus_connection_send_message_with_reply_unlocked (connection, + message, + timeout_msec, + out_serial, + cancellable, + callback, + user_data); + CONNECTION_UNLOCK (connection); +} + +/** + * g_dbus_connection_send_message_with_reply_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_send_message_with_reply(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_send_message_with_reply(). + * + * Note that @error is only set if a local in-process error + * occured. That is to say that the returned #GDBusMessage object may + * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use + * g_dbus_message_to_gerror() to transcode this to a #GError. + * + * See and for an example of how to use this + * low-level API to send and receive UNIX file descriptors. + * + * Returns: A #GDBusMessage or %NULL if @error is set. + */ +GDBusMessage * +g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GDBusMessage *reply; + GCancellable *cancellable; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + reply = NULL; + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_send_message_with_reply); + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + reply = g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); + cancellable = g_object_get_data (G_OBJECT (simple), "cancellable"); + if (cancellable != NULL && g_cancellable_is_cancelled (cancellable)) + { + g_object_unref (reply); + reply = NULL; + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CANCELLED, + _("Operation was cancelled")); + } + out: + return reply; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GAsyncResult *res; + GMainContext *context; + GMainLoop *loop; +} SendMessageSyncData; + +static void +send_message_with_reply_sync_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + SendMessageSyncData *data = user_data; + data->res = g_object_ref (res); + g_main_loop_quit (data->loop); +} + +/** + * g_dbus_connection_send_message_with_reply_sync: + * @connection: A #GDBusConnection. + * @message: A #GDBusMessage. + * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. + * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously sends @message to the peer represented by @connection + * and blocks the calling thread until a reply is received or the + * timeout is reached. See g_dbus_connection_send_message_with_reply() + * for the asynchronous version of this method. + * + * If @out_serial is not %NULL, then the serial number assigned to + * @message by @connection will be written to this location prior to + * submitting the message to the underlying transport. + * + * If @connection is closed then the operation will fail with + * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will + * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, + * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. + * + * Note that @error is only set if a local in-process error + * occured. That is to say that the returned #GDBusMessage object may + * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use + * g_dbus_message_to_gerror() to transcode this to a #GError. + * + * See and for an example of how to use this + * low-level API to send and receive UNIX file descriptors. + * + * Returns: A #GDBusMessage that is the reply to @message or %NULL if @error is set. + */ +GDBusMessage * +g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, + GDBusMessage *message, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GError **error) +{ + SendMessageSyncData *data; + GDBusMessage *reply; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + data = g_new0 (SendMessageSyncData, 1); + data->context = g_main_context_new (); + data->loop = g_main_loop_new (data->context, FALSE); + + g_main_context_push_thread_default (data->context); + + g_dbus_connection_send_message_with_reply (connection, + message, + timeout_msec, + out_serial, + cancellable, + (GAsyncReadyCallback) send_message_with_reply_sync_cb, + data); + g_main_loop_run (data->loop); + reply = g_dbus_connection_send_message_with_reply_finish (connection, + data->res, + error); + + g_main_context_pop_thread_default (data->context); + + g_main_context_unref (data->context); + g_main_loop_unref (data->loop); + g_object_unref (data->res); + g_free (data); + + return reply; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GDBusMessageFilterFunction func; + gpointer user_data; +} FilterCallback; + +typedef struct +{ + guint id; + GDBusMessageFilterFunction filter_function; + gpointer user_data; + GDestroyNotify user_data_free_func; +} FilterData; + +/* Called in worker's thread - we must not block */ +static void +on_worker_message_received (GDBusWorker *worker, + GDBusMessage *message, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + FilterCallback *filters; + gboolean consumed_by_filter; + guint num_filters; + guint n; + + //g_debug ("in on_worker_message_received"); + + g_object_ref (connection); + + /* First collect the set of callback functions */ + CONNECTION_LOCK (connection); + num_filters = connection->priv->filters->len; + filters = g_new0 (FilterCallback, num_filters); + for (n = 0; n < num_filters; n++) + { + FilterData *data = connection->priv->filters->pdata[n]; + filters[n].func = data->filter_function; + filters[n].user_data = data->user_data; + } + CONNECTION_UNLOCK (connection); + + /* the call the filters in order (without holding the lock) */ + consumed_by_filter = FALSE; + for (n = 0; n < num_filters; n++) + { + consumed_by_filter = filters[n].func (connection, + message, + filters[n].user_data); + if (consumed_by_filter) + break; + } + + /* Standard dispatch unless the filter ate the message */ + if (!consumed_by_filter) + { + GDBusMessageType message_type; + + message_type = g_dbus_message_get_type (message); + if (message_type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN || message_type == G_DBUS_MESSAGE_TYPE_ERROR) + { + guint32 reply_serial; + SendMessageData *send_message_data; + + reply_serial = g_dbus_message_get_reply_serial (message); + CONNECTION_LOCK (connection); + send_message_data = g_hash_table_lookup (connection->priv->map_method_serial_to_send_message_data, + GUINT_TO_POINTER (reply_serial)); + if (send_message_data != NULL) + { + //g_debug ("delivering reply/error for serial %d for %p", reply_serial, connection); + send_message_data_deliver_reply_unlocked (send_message_data, message); + } + else + { + //g_debug ("message reply/error for serial %d but no SendMessageData found for %p", reply_serial, connection); + } + CONNECTION_UNLOCK (connection); + } + else if (message_type == G_DBUS_MESSAGE_TYPE_SIGNAL) + { + CONNECTION_LOCK (connection); + distribute_signals (connection, message); + CONNECTION_UNLOCK (connection); + } + else if (message_type == G_DBUS_MESSAGE_TYPE_METHOD_CALL) + { + CONNECTION_LOCK (connection); + distribute_method_call (connection, message); + CONNECTION_UNLOCK (connection); + } + } + + g_object_unref (connection); + g_free (filters); +} + +/* Called in worker's thread - we must not block */ +static void +on_worker_closed (GDBusWorker *worker, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + + //g_debug ("in on_worker_closed: %s", error->message); + + CONNECTION_LOCK (connection); + if (!connection->priv->closed) + set_closed_unlocked (connection, remote_peer_vanished, error); + CONNECTION_UNLOCK (connection); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* Determines the biggest set of capabilities we can support on this connection */ +static GDBusCapabilityFlags +get_offered_capabilities_max (GDBusConnection *connection) +{ + GDBusCapabilityFlags ret; + ret = G_DBUS_CAPABILITY_FLAGS_NONE; +#ifdef G_OS_UNIX + if (G_IS_UNIX_CONNECTION (connection->priv->stream)) + { + ret |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; + } +#endif + return ret; +} + + +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (initable); + gboolean ret; + + /* This method needs to be idempotent to work with the singleton + * pattern. See the docs for g_initable_init(). We implement this by + * locking. + * + * Unfortunately we can't use the main lock since the on_worker_*() + * callbacks above needs the lock during initialization (for message + * bus connections we do a synchronous Hello() call on the bus). + */ + g_mutex_lock (connection->priv->init_lock); + + ret = FALSE; + + if (connection->priv->is_initialized) + { + if (connection->priv->stream != NULL) + ret = TRUE; + else + g_assert (connection->priv->initialization_error != NULL); + goto out; + } + g_assert (connection->priv->initialization_error == NULL); + + /* The user can pass multiple (but mutally exclusive) construct + * properties: + * + * - stream (of type GIOStream) + * - address (of type gchar*) + * + * At the end of the day we end up with a non-NULL GIOStream + * object in connection->priv->stream. + */ + if (connection->priv->address != NULL) + { + g_assert (connection->priv->stream == NULL); + + if ((connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) || + (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported flags encountered when constructing a client-side connection")); + goto out; + } + + connection->priv->stream = g_dbus_address_get_stream_sync (connection->priv->address, + NULL, /* TODO: out_guid */ + cancellable, + &connection->priv->initialization_error); + if (connection->priv->stream == NULL) + goto out; + } + else if (connection->priv->stream != NULL) + { + /* nothing to do */ + } + else + { + g_assert_not_reached (); + } + + /* Authenticate the connection */ + if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) + { + g_assert (!(connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT)); + g_assert (connection->priv->guid != NULL); + connection->priv->auth = _g_dbus_auth_new (connection->priv->stream); + if (!_g_dbus_auth_run_server (connection->priv->auth, + connection->priv->authentication_observer, + connection->priv->guid, + (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS), + get_offered_capabilities_max (connection), + &connection->priv->capabilities, + &connection->priv->crendentials, + cancellable, + &connection->priv->initialization_error)) + goto out; + } + else if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT) + { + g_assert (!(connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER)); + g_assert (connection->priv->guid == NULL); + connection->priv->auth = _g_dbus_auth_new (connection->priv->stream); + connection->priv->guid = _g_dbus_auth_run_client (connection->priv->auth, + get_offered_capabilities_max (connection), + &connection->priv->capabilities, + cancellable, + &connection->priv->initialization_error); + if (connection->priv->guid == NULL) + goto out; + } + + if (connection->priv->authentication_observer != NULL) + { + g_object_unref (connection->priv->authentication_observer); + connection->priv->authentication_observer = NULL; + } + + //g_output_stream_flush (G_SOCKET_CONNECTION (connection->priv->stream) + + //g_debug ("haz unix fd passing powers: %d", connection->priv->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); + + /* Hack used until + * + * https://bugzilla.gnome.org/show_bug.cgi?id=616458 + * + * has been resolved + */ + if (G_IS_SOCKET_CONNECTION (connection->priv->stream)) + { + g_socket_set_blocking (g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection->priv->stream)), FALSE); + } + + connection->priv->worker = _g_dbus_worker_new (connection->priv->stream, + connection->priv->capabilities, + on_worker_message_received, + on_worker_closed, + connection); + + /* if a bus connection, invoke org.freedesktop.DBus.Hello - this is how we're getting a name */ + if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + { + GVariant *hello_result; + const gchar *s; + + hello_result = g_dbus_connection_invoke_method_sync (connection, + "org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "Hello", + NULL, /* parameters */ + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* TODO: cancellable */ + &connection->priv->initialization_error); + if (hello_result == NULL) + goto out; + + g_variant_get (hello_result, "(s)", &s); + connection->priv->bus_unique_name = g_strdup (s); + g_variant_unref (hello_result); + //g_debug ("unique name is `%s'", connection->priv->bus_unique_name); + } + + connection->priv->is_initialized = TRUE; + + ret = TRUE; + out: + if (!ret) + { + g_assert (connection->priv->initialization_error != NULL); + g_propagate_error (error, g_error_copy (connection->priv->initialization_error)); + } + + g_mutex_unlock (connection->priv->init_lock); + + return ret; +} + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = initable_init; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +async_init_thread (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + GError *error = NULL; + + if (!g_initable_init (G_INITABLE (object), cancellable, &error)) + { + g_simple_async_result_set_from_error (res, error); + g_error_free (error); + } +} + +static void +async_initable_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + g_return_if_fail (G_IS_INITABLE (initable)); + + res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, + async_initable_init_async); + g_simple_async_result_run_in_thread (res, async_init_thread, + io_priority, cancellable); + g_object_unref (res); +} + +static gboolean +async_initable_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + return TRUE; /* Errors handled by base impl */ +} + +static void +async_initable_iface_init (GAsyncInitableIface *async_initable_iface) +{ + /* We basically just want to use GIO's default implementation - though that one is + * unfortunately broken, see #615111. So we copy-paste a fixed-up version. + */ + async_initable_iface->init_async = async_initable_init_async; + async_initable_iface->init_finish = async_initable_init_finish; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_new: + * @stream: A #GIOStream. + * @guid: The GUID to use if a authenticating as a server or %NULL. + * @flags: Flags describing how to make the connection. + * @authentication_observer: A #GDBusAuthObserver or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: The data to pass to @callback. + * + * Asynchronously sets up a D-Bus connection for exchanging D-Bus messages + * with the end represented by @stream. + * + * If %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER is set in @flags, + * @auth_observer (if not %NULL) is used to assist in the client + * authentication process. + * + * When the operation is finished, @callback will be invoked. You can + * then call g_dbus_connection_new_finish() to get the result of the + * operation. + * + * This is a asynchronous failable constructor. See + * g_dbus_connection_new_sync() for the synchronous + * version. + */ +void +g_dbus_connection_new (GIOStream *stream, + const gchar *guid, + GDBusConnectionFlags flags, + GDBusAuthObserver *authentication_observer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (G_IS_IO_STREAM (stream)); + g_async_initable_new_async (G_TYPE_DBUS_CONNECTION, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "stream", stream, + "guid", guid, + "flags", flags, + "authentication-observer", authentication_observer, + NULL); +} + +/** + * g_dbus_connection_new_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_new(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_new(). + * + * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + */ +GDBusConnection * +g_dbus_connection_new_finish (GAsyncResult *res, + GError **error) +{ + GObject *object; + GObject *source_object; + + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + 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_CONNECTION (object); + else + return NULL; +} + +/** + * g_dbus_connection_new_sync: + * @stream: A #GIOStream. + * @guid: The GUID to use if a authenticating as a server or %NULL. + * @flags: Flags describing how to make the connection. + * @authentication_observer: A #GDBusAuthObserver or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously sets up a D-Bus connection for exchanging D-Bus messages + * with the end represented by @stream. + * + * If %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER is set in @flags, + * @auth_observer (if not %NULL) is used to assist in the client + * authentication process. + * + * This is a synchronous failable constructor. See + * g_dbus_connection_new() for the asynchronous version. + * + * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + */ +GDBusConnection * +g_dbus_connection_new_sync (GIOStream *stream, + const gchar *guid, + GDBusConnectionFlags flags, + GDBusAuthObserver *authentication_observer, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_initable_new (G_TYPE_DBUS_CONNECTION, + cancellable, + error, + "stream", stream, + "guid", guid, + "flags", flags, + "authentication-observer", authentication_observer, + NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_new_for_address: + * @address: A D-Bus address. + * @flags: Flags describing how to make the connection. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: The data to pass to @callback. + * + * Asynchronously connects and sets up a D-Bus client connection for + * exchanging D-Bus messages with an endpoint specified by @address + * which must be in the D-Bus address format. + * + * This constructor can only be used to initiate client-side + * connections - use g_dbus_connection_new() if you need to act as the + * server. In particular, @flags cannot contain the + * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER or + * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS flags. + * + * When the operation is finished, @callback will be invoked. You can + * then call g_dbus_connection_new_finish() to get the result of the + * operation. + * + * This is a asynchronous failable constructor. See + * g_dbus_connection_new_for_address_sync() for the synchronous + * version. + */ +void +g_dbus_connection_new_for_address (const gchar *address, + GDBusConnectionFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (address != NULL); + g_async_initable_new_async (G_TYPE_DBUS_CONNECTION, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "address", address, + "flags", flags, + NULL); +} + +/** + * g_dbus_connection_new_for_address_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_new(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_new_for_address(). + * + * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + */ +GDBusConnection * +g_dbus_connection_new_for_address_finish (GAsyncResult *res, + GError **error) +{ + GObject *object; + GObject *source_object; + + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + 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_CONNECTION (object); + else + return NULL; +} + +/** + * g_dbus_connection_new_for_address_sync: + * @address: A D-Bus address. + * @flags: Flags describing how to make the connection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously connects and sets up a D-Bus client connection for + * exchanging D-Bus messages with an endpoint specified by @address + * which must be in the D-Bus address format. + * + * This constructor can only be used to initiate client-side + * connections - use g_dbus_connection_new_sync() if you need to act + * as the server. In particular, @flags cannot contain the + * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER or + * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS flags. + * + * This is a synchronous failable constructor. See + * g_dbus_connection_new_for_address() for the asynchronous version. + * + * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + */ +GDBusConnection * +g_dbus_connection_new_for_address_sync (const gchar *address, + GDBusConnectionFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_initable_new (G_TYPE_DBUS_CONNECTION, + cancellable, + error, + "address", address, + "flags", flags, + NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_set_exit_on_close: + * @connection: A #GDBusConnection. + * @exit_on_close: Whether the process should be terminated + * when @connection is closed by the remote peer. + * + * Sets whether the process should be terminated when @connection is + * closed by the remote peer. See #GDBusConnection:exit-on-close for + * more details. + */ +void +g_dbus_connection_set_exit_on_close (GDBusConnection *connection, + gboolean exit_on_close) +{ + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + connection->priv->exit_on_close = exit_on_close; +} + +/** + * g_dbus_connection_get_exit_on_close: + * @connection: A #GDBusConnection. + * + * Gets whether the process is terminated when @connection is + * closed by the remote peer. See + * #GDBusConnection:exit-on-close for more details. + * + * Returns: Whether the process is terminated when @connection is + * closed by the remote peer. + */ +gboolean +g_dbus_connection_get_exit_on_close (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + return connection->priv->exit_on_close; +} + +/** + * g_dbus_connection_get_guid: + * @connection: A #GDBusConnection. + * + * The GUID of the peer performing the role of server when + * authenticating. See #GDBusConnection:guid for more details. + * + * Returns: The GUID. Do not free this string, it is owned by + * @connection. + **/ +const gchar * +g_dbus_connection_get_guid (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + return connection->priv->guid; +} + +/** + * g_dbus_connection_get_unique_name: + * @connection: A #GDBusConnection. + * + * Gets the unique name of @connection as assigned by the message + * bus. This can also be used to figure out if @connection is a + * message bus connection. + * + * Returns: The unique name or %NULL if @connection is not a message + * bus connection. Do not free this string, it is owned by + * @connection. + **/ +const gchar * +g_dbus_connection_get_unique_name (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + return connection->priv->bus_unique_name; +} + +/** + * g_dbus_connection_get_peer_credentials: + * @connection: A #GDBusConnection. + * + * Gets the credentials of the authenticated peer. This will always + * return %NULL unless @connection acted as a server + * (e.g. %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER was passed) + * when set up and the client passed credentials as part of the + * authentication process. + * + * In a message bus setup, the message bus is always the server and + * each application is a client. So this method will always return + * %NULL for message bus clients. + * + * Returns: A #GCredentials or %NULL if not available. Do not free + * this object, it is owned by @connection. + */ +GCredentials * +g_dbus_connection_get_peer_credentials (GDBusConnection *connection) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + return connection->priv->crendentials; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static guint _global_filter_id = 1; + +/** + * g_dbus_connection_add_filter: + * @connection: A #GDBusConnection. + * @filter_function: A filter function. + * @user_data: User data to pass to @filter_function. + * @user_data_free_func: Function to free @user_data with when filter + * is removed or %NULL. + * + * Adds a message filter. Filters are handlers that are run on all + * incoming messages, prior to standard dispatch. Filters are run in + * the order that they were added. The same handler can be added as a + * filter more than once, in which case it will be run more than once. + * Filters added during a filter callback won't be run on the message + * being processed. + * + * Note that filters are run in a dedicated message handling thread so + * they can't block and, generally, can't do anything but signal a + * worker thread. Also note that filters are rarely needed - use API + * such as g_dbus_connection_send_message_with_reply(), + * g_dbus_connection_signal_subscribe() or + * g_dbus_connection_invoke_method() instead. + * + * Returns: A filter identifier that can be used with + * g_dbus_connection_remove_filter(). + */ +guint +g_dbus_connection_add_filter (GDBusConnection *connection, + GDBusMessageFilterFunction filter_function, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + FilterData *data; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (filter_function != NULL, 0); + + CONNECTION_LOCK (connection); + data = g_new0 (FilterData, 1); + data->id = _global_filter_id++; /* TODO: overflow etc. */ + data->filter_function = filter_function; + data->user_data = user_data; + data->user_data_free_func = user_data_free_func; + g_ptr_array_add (connection->priv->filters, data); + CONNECTION_UNLOCK (connection); + + return data->id; +} + +/* only called from finalize(), removes all filters */ +static void +purge_all_filters (GDBusConnection *connection) +{ + guint n; + for (n = 0; n < connection->priv->filters->len; n++) + { + FilterData *data = connection->priv->filters->pdata[n]; + if (data->user_data_free_func != NULL) + data->user_data_free_func (data->user_data); + g_free (data); + } +} + +void +g_dbus_connection_remove_filter (GDBusConnection *connection, + guint filter_id) +{ + guint n; + FilterData *to_destroy; + + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + CONNECTION_LOCK (connection); + to_destroy = NULL; + for (n = 0; n < connection->priv->filters->len; n++) + { + FilterData *data = connection->priv->filters->pdata[n]; + if (data->id == filter_id) + { + g_ptr_array_remove_index (connection->priv->filters, n); + to_destroy = data; + break; + } + } + CONNECTION_UNLOCK (connection); + + /* do free without holding lock */ + if (to_destroy != NULL) + { + if (to_destroy->user_data_free_func != NULL) + to_destroy->user_data_free_func (to_destroy->user_data); + g_free (to_destroy); + } + else + { + g_warning ("g_dbus_connection_remove_filter: No filter found for filter_id %d", filter_id); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + gchar *rule; + gchar *sender; + gchar *interface_name; + gchar *member; + gchar *object_path; + gchar *arg0; + GArray *subscribers; +} SignalData; + +typedef struct +{ + GDBusSignalCallback callback; + gpointer user_data; + GDestroyNotify user_data_free_func; + guint id; + GMainContext *context; +} SignalSubscriber; + +static void +signal_data_free (SignalData *data) +{ + g_free (data->rule); + g_free (data->sender); + g_free (data->interface_name); + g_free (data->member); + g_free (data->object_path); + g_free (data->arg0); + g_array_free (data->subscribers, TRUE); + g_free (data); +} + +static gchar * +args_to_rule (const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0) +{ + GString *rule; + + rule = g_string_new ("type='signal'"); + if (sender != NULL) + g_string_append_printf (rule, ",sender='%s'", sender); + if (interface_name != NULL) + g_string_append_printf (rule, ",interface='%s'", interface_name); + if (member != NULL) + g_string_append_printf (rule, ",member='%s'", member); + if (object_path != NULL) + g_string_append_printf (rule, ",path='%s'", object_path); + if (arg0 != NULL) + g_string_append_printf (rule, ",arg0='%s'", arg0); + + return g_string_free (rule, FALSE); +} + +static guint _global_subscriber_id = 1; +static guint _global_registration_id = 1; +static guint _global_subtree_registration_id = 1; + +/* ---------------------------------------------------------------------------------------------------- */ + +/* must hold lock when calling */ +static void +add_match_rule (GDBusConnection *connection, + const gchar *match_rule) +{ + GError *error; + GDBusMessage *message; + + message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "AddMatch"); + g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); + + error = NULL; + if (!g_dbus_connection_send_message_unlocked (connection, + message, + NULL, + &error)) + { + g_critical ("Error while sending AddMatch() message: %s", error->message); + g_error_free (error); + } + g_object_unref (message); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* must hold lock when calling */ +static void +remove_match_rule (GDBusConnection *connection, + const gchar *match_rule) +{ + GError *error; + GDBusMessage *message; + + message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "RemoveMatch"); + g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); + + error = NULL; + if (!g_dbus_connection_send_message_unlocked (connection, + message, + NULL, + &error)) + { + g_critical ("Error while sending RemoveMatch() message: %s", error->message); + g_error_free (error); + } + g_object_unref (message); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) +{ + return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 && + g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && + g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && + (g_strcmp0 (signal_data->member, "NameLost") == 0 || + g_strcmp0 (signal_data->member, "NameAcquired") == 0); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_signal_subscribe: + * @connection: A #GDBusConnection. + * @sender: Sender name to match on. Must be either org.freedesktop.DBus (for listening to signals from the message bus daemon) or a unique name or %NULL to listen from all senders. + * @interface_name: D-Bus interface name to match on or %NULL to match on all interfaces. + * @member: D-Bus signal name to match on or %NULL to match on all signals. + * @object_path: Object path to match on or %NULL to match on all object paths. + * @arg0: Contents of first string argument to match on or %NULL to match on all kinds of arguments. + * @callback: Callback to invoke when there is a signal matching the requested data. + * @user_data: User data to pass to @callback. + * @user_data_free_func: Function to free @user_data with when subscription is removed or %NULL. + * + * Subscribes to signals on @connection and invokes @callback with a + * whenever the signal is received. Note that @callback + * will be invoked in the thread-default main + * loop of the thread you are calling this method from. + * + * It is considered a programming error to use this function if @connection is closed. + * + * Note that if @sender is not org.freedesktop.DBus (for listening to signals from the + * message bus daemon), then it needs to be a unique bus name or %NULL (for listening to signals from any + * name) - you cannot pass a name like com.example.MyApp. + * Use e.g. g_bus_watch_name() to find the unique name for the owner of the name you are interested in. Also note + * that this function does not remove a subscription if @sender vanishes from the bus. You have to manually + * call g_dbus_connection_signal_unsubscribe() to remove a subscription. + * + * Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe(). + **/ +guint +g_dbus_connection_signal_subscribe (GDBusConnection *connection, + const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0, + GDBusSignalCallback callback, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + gchar *rule; + SignalData *signal_data; + SignalSubscriber subscriber; + GPtrArray *signal_data_array; + + /* Right now we abort if AddMatch() fails since it can only fail with the bus being in + * an OOM condition. We might want to change that but that would involve making + * g_dbus_connection_signal_subscribe() asynchronous and having the call sites + * handle that. And there's really no sensible way of handling this short of retrying + * to add the match rule... and then there's the little thing that, hey, maybe there's + * a reason the bus in an OOM condition. + * + * Doable, but not really sure it's worth it... + */ + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); + g_return_val_if_fail (sender == NULL || ((strcmp (sender, "org.freedesktop.DBus") == 0 || sender[0] == ':') && + (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)), 0); + g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), 0); + g_return_val_if_fail (member == NULL || g_dbus_is_member_name (member), 0); + g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (callback != NULL, 0); + + CONNECTION_LOCK (connection); + + rule = args_to_rule (sender, interface_name, member, object_path, arg0); + + if (sender == NULL) + sender = ""; + + subscriber.callback = callback; + subscriber.user_data = user_data; + subscriber.user_data_free_func = user_data_free_func; + subscriber.id = _global_subscriber_id++; /* TODO: overflow etc. */ + subscriber.context = g_main_context_get_thread_default (); + if (subscriber.context != NULL) + g_main_context_ref (subscriber.context); + + /* see if we've already have this rule */ + signal_data = g_hash_table_lookup (connection->priv->map_rule_to_signal_data, rule); + if (signal_data != NULL) + { + g_array_append_val (signal_data->subscribers, subscriber); + g_free (rule); + goto out; + } + + signal_data = g_new0 (SignalData, 1); + signal_data->rule = rule; + signal_data->sender = g_strdup (sender); + signal_data->interface_name = g_strdup (interface_name); + signal_data->member = g_strdup (member); + signal_data->object_path = g_strdup (object_path); + signal_data->arg0 = g_strdup (arg0); + signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); + g_array_append_val (signal_data->subscribers, subscriber); + + g_hash_table_insert (connection->priv->map_rule_to_signal_data, + signal_data->rule, + signal_data); + + /* Add the match rule to the bus... + * + * Avoid adding match rules for NameLost and NameAcquired messages - the bus will + * always send such messages to to us. + */ + if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + { + if (!is_signal_data_for_name_lost_or_acquired (signal_data)) + { + add_match_rule (connection, signal_data->rule); + } + } + + out: + g_hash_table_insert (connection->priv->map_id_to_signal_data, + GUINT_TO_POINTER (subscriber.id), + signal_data); + + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, + signal_data->sender); + if (signal_data_array == NULL) + { + signal_data_array = g_ptr_array_new (); + g_hash_table_insert (connection->priv->map_sender_to_signal_data_array, + g_strdup (signal_data->sender), + signal_data_array); + } + g_ptr_array_add (signal_data_array, signal_data); + + CONNECTION_UNLOCK (connection); + + return subscriber.id; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* must hold lock when calling this */ +static void +unsubscribe_id_internal (GDBusConnection *connection, + guint subscription_id, + GArray *out_removed_subscribers) +{ + SignalData *signal_data; + GPtrArray *signal_data_array; + guint n; + + signal_data = g_hash_table_lookup (connection->priv->map_id_to_signal_data, + GUINT_TO_POINTER (subscription_id)); + if (signal_data == NULL) + { + /* Don't warn here, we may have thrown all subscriptions out when the connection was closed */ + goto out; + } + + for (n = 0; n < signal_data->subscribers->len; n++) + { + SignalSubscriber *subscriber; + + subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, n)); + if (subscriber->id != subscription_id) + continue; + + g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_signal_data, + GUINT_TO_POINTER (subscription_id))); + g_array_append_val (out_removed_subscribers, *subscriber); + g_array_remove_index (signal_data->subscribers, n); + + if (signal_data->subscribers->len == 0) + g_warn_if_fail (g_hash_table_remove (connection->priv->map_rule_to_signal_data, signal_data->rule)); + + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, + signal_data->sender); + g_warn_if_fail (signal_data_array != NULL); + g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); + + if (signal_data_array->len == 0) + { + g_warn_if_fail (g_hash_table_remove (connection->priv->map_sender_to_signal_data_array, signal_data->sender)); + + /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ + if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + { + if (!is_signal_data_for_name_lost_or_acquired (signal_data)) + { + remove_match_rule (connection, signal_data->rule); + } + } + + signal_data_free (signal_data); + } + + goto out; + } + + g_assert_not_reached (); + + out: + ; +} + +/** + * g_dbus_connection_signal_unsubscribe: + * @connection: A #GDBusConnection. + * @subscription_id: A subscription id obtained from g_dbus_connection_signal_subscribe(). + * + * Unsubscribes from signals. + **/ +void +g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, + guint subscription_id) +{ + GArray *subscribers; + guint n; + + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); + + CONNECTION_LOCK (connection); + unsubscribe_id_internal (connection, + subscription_id, + subscribers); + CONNECTION_UNLOCK (connection); + + /* invariant */ + g_assert (subscribers->len == 0 || subscribers->len == 1); + + /* call GDestroyNotify without lock held */ + for (n = 0; n < subscribers->len; n++) + { + SignalSubscriber *subscriber; + subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); + if (subscriber->user_data_free_func != NULL) + subscriber->user_data_free_func (subscriber->user_data); + if (subscriber->context != NULL) + g_main_context_unref (subscriber->context); + } + + g_array_free (subscribers, TRUE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + guint subscription_id; + GDBusSignalCallback callback; + gpointer user_data; + GDBusMessage *message; + GDBusConnection *connection; + const gchar *sender; + const gchar *path; + const gchar *interface; + const gchar *member; +} SignalInstance; + +/* called on delivery thread (e.g. where g_dbus_connection_signal_subscribe() was called) with + * no locks held + */ +static gboolean +emit_signal_instance_in_idle_cb (gpointer data) +{ + SignalInstance *signal_instance = data; + GVariant *parameters; + gboolean has_subscription; + + parameters = g_dbus_message_get_body (signal_instance->message); + if (parameters == NULL) + { + parameters = g_variant_new ("()"); + g_variant_ref_sink (parameters); + } + else + { + g_variant_ref_sink (parameters); + } + +#if 0 + g_debug ("in emit_signal_instance_in_idle_cb (sender=%s path=%s interface=%s member=%s params=%s)", + signal_instance->sender, + signal_instance->path, + signal_instance->interface, + signal_instance->member, + g_variant_print (parameters, TRUE)); +#endif + + /* Careful here, don't do the callback if we no longer has the subscription */ + CONNECTION_LOCK (signal_instance->connection); + has_subscription = FALSE; + if (g_hash_table_lookup (signal_instance->connection->priv->map_id_to_signal_data, + GUINT_TO_POINTER (signal_instance->subscription_id)) != NULL) + has_subscription = TRUE; + CONNECTION_UNLOCK (signal_instance->connection); + + if (has_subscription) + { + signal_instance->callback (signal_instance->connection, + signal_instance->sender, + signal_instance->path, + signal_instance->interface, + signal_instance->member, + parameters, + signal_instance->user_data); + } + if (parameters != NULL) + g_variant_unref (parameters); + + return FALSE; +} + +static void +signal_instance_free (SignalInstance *signal_instance) +{ + g_object_unref (signal_instance->message); + g_object_unref (signal_instance->connection); + g_free (signal_instance); +} + +/* called in message handler thread WITH lock held */ +static void +schedule_callbacks (GDBusConnection *connection, + GPtrArray *signal_data_array, + GDBusMessage *message, + const gchar *sender) +{ + guint n, m; + const gchar *interface; + const gchar *member; + const gchar *path; + const gchar *arg0; + + interface = NULL; + member = NULL; + path = NULL; + arg0 = NULL; + + interface = g_dbus_message_get_interface (message); + member = g_dbus_message_get_member (message); + path = g_dbus_message_get_path (message); + arg0 = g_dbus_message_get_arg0 (message); + +#if 0 + g_debug ("sender = `%s'", sender); + g_debug ("interface = `%s'", interface); + g_debug ("member = `%s'", member); + g_debug ("path = `%s'", path); + g_debug ("arg0 = `%s'", arg0); +#endif + + /* TODO: if this is slow, then we can change signal_data_array into + * map_object_path_to_signal_data_array or something. + */ + for (n = 0; n < signal_data_array->len; n++) + { + SignalData *signal_data = signal_data_array->pdata[n]; + + if (signal_data->interface_name != NULL && g_strcmp0 (signal_data->interface_name, interface) != 0) + continue; + + if (signal_data->member != NULL && g_strcmp0 (signal_data->member, member) != 0) + continue; + + if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0) + continue; + + if (signal_data->arg0 != NULL && g_strcmp0 (signal_data->arg0, arg0) != 0) + continue; + + for (m = 0; m < signal_data->subscribers->len; m++) + { + SignalSubscriber *subscriber; + GSource *idle_source; + SignalInstance *signal_instance; + + subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, m)); + + signal_instance = g_new0 (SignalInstance, 1); + signal_instance->subscription_id = subscriber->id; + signal_instance->callback = subscriber->callback; + signal_instance->user_data = subscriber->user_data; + signal_instance->message = g_object_ref (message); + signal_instance->connection = g_object_ref (connection); + signal_instance->sender = sender; + signal_instance->path = path; + signal_instance->interface = interface; + signal_instance->member = member; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + emit_signal_instance_in_idle_cb, + signal_instance, + (GDestroyNotify) signal_instance_free); + g_source_attach (idle_source, subscriber->context); + g_source_unref (idle_source); + } + } +} + +/* called in message handler thread with lock held */ +static void +distribute_signals (GDBusConnection *connection, + GDBusMessage *message) +{ + GPtrArray *signal_data_array; + const gchar *sender; + + sender = g_dbus_message_get_sender (message); + + /* collect subcsribers that match on sender */ + if (sender != NULL) + { + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, sender); + if (signal_data_array != NULL) { + schedule_callbacks (connection, signal_data_array, message, sender); + } + } + + /* collect subcsribers not matching on sender */ + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, ""); + if (signal_data_array != NULL) + { + schedule_callbacks (connection, signal_data_array, message, sender); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* only called from finalize(), removes all subscriptions */ +static void +purge_all_signal_subscriptions (GDBusConnection *connection) +{ + GHashTableIter iter; + gpointer key; + GArray *ids; + GArray *subscribers; + guint n; + + ids = g_array_new (FALSE, FALSE, sizeof (guint)); + g_hash_table_iter_init (&iter, connection->priv->map_id_to_signal_data); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + guint subscription_id = GPOINTER_TO_UINT (key); + g_array_append_val (ids, subscription_id); + } + + subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); + for (n = 0; n < ids->len; n++) + { + guint subscription_id = g_array_index (ids, guint, n); + unsubscribe_id_internal (connection, + subscription_id, + subscribers); + } + g_array_free (ids, TRUE); + + /* call GDestroyNotify without lock held */ + for (n = 0; n < subscribers->len; n++) + { + SignalSubscriber *subscriber; + subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); + if (subscriber->user_data_free_func != NULL) + subscriber->user_data_free_func (subscriber->user_data); + if (subscriber->context != NULL) + g_main_context_unref (subscriber->context); + } + + g_array_free (subscribers, TRUE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct ExportedObject +{ + gchar *object_path; + GDBusConnection *connection; + + /* maps gchar* -> ExportedInterface* */ + GHashTable *map_if_name_to_ei; +}; + +/* only called with lock held */ +static void +exported_object_free (ExportedObject *eo) +{ + g_free (eo->object_path); + g_hash_table_unref (eo->map_if_name_to_ei); + g_free (eo); +} + +typedef struct +{ + ExportedObject *eo; + + guint id; + gchar *interface_name; + const GDBusInterfaceVTable *vtable; + const GDBusInterfaceInfo *introspection_data; + + GMainContext *context; + gpointer user_data; + GDestroyNotify user_data_free_func; +} ExportedInterface; + +/* called with lock held */ +static void +exported_interface_free (ExportedInterface *ei) +{ + if (ei->user_data_free_func != NULL) + { + /* TODO: push to thread-default mainloop */ + ei->user_data_free_func (ei->user_data); + } + if (ei->context != NULL) + { + g_main_context_unref (ei->context); + } + g_free (ei->interface_name); + g_free (ei); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GDBusConnection *connection; + GDBusMessage *message; + gpointer user_data; + const char *property_name; + const GDBusInterfaceVTable *vtable; + const GDBusInterfaceInfo *interface_info; + const GDBusPropertyInfo *property_info; +} PropertyData; + +static void +property_data_free (PropertyData *data) +{ + g_object_unref (data->connection); + g_object_unref (data->message); + g_free (data); +} + +/* called in thread where object was registered - no locks held */ +static gboolean +invoke_get_property_in_idle_cb (gpointer _data) +{ + PropertyData *data = _data; + GVariant *value; + GError *error; + GDBusMessage *reply; + + error = NULL; + value = data->vtable->get_property (data->connection, + g_dbus_message_get_sender (data->message), + g_dbus_message_get_path (data->message), + data->interface_info->name, + data->property_name, + &error, + data->user_data); + + + if (value != NULL) + { + g_assert_no_error (error); + + g_variant_ref_sink (value); + reply = g_dbus_message_new_method_reply (data->message); + g_dbus_message_set_body (reply, g_variant_new ("(v)", value)); + g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_variant_unref (value); + g_object_unref (reply); + } + else + { + gchar *dbus_error_name; + + g_assert (error != NULL); + + dbus_error_name = g_dbus_error_encode_gerror (error); + reply = g_dbus_message_new_method_error_literal (data->message, + dbus_error_name, + error->message); + g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_free (dbus_error_name); + g_error_free (error); + g_object_unref (reply); + } + + return FALSE; +} + +/* called in thread where object was registered - no locks held */ +static gboolean +invoke_set_property_in_idle_cb (gpointer _data) +{ + PropertyData *data = _data; + GError *error; + GDBusMessage *reply; + GVariant *value; + + error = NULL; + value = NULL; + + g_variant_get (g_dbus_message_get_body (data->message), + "(ssv)", + NULL, + NULL, + &value); + + /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the type + * of the given value is wrong + */ + if (g_strcmp0 (g_variant_get_type_string (value), data->property_info->signature) != 0) + { + reply = g_dbus_message_new_method_error (data->message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("Error setting property `%s': Expected type `%s' but got `%s'"), + data->property_info->name, + data->property_info->signature, + g_variant_get_type_string (value)); + goto out; + } + + if (!data->vtable->set_property (data->connection, + g_dbus_message_get_sender (data->message), + g_dbus_message_get_path (data->message), + data->interface_info->name, + data->property_name, + value, + &error, + data->user_data)) + { + gchar *dbus_error_name; + g_assert (error != NULL); + dbus_error_name = g_dbus_error_encode_gerror (error); + reply = g_dbus_message_new_method_error_literal (data->message, + dbus_error_name, + error->message); + g_free (dbus_error_name); + g_error_free (error); + } + else + { + reply = g_dbus_message_new_method_reply (data->message); + } + + out: + g_assert (reply != NULL); + g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_object_unref (reply); + + return FALSE; +} + +/* called with lock held */ +static gboolean +validate_and_maybe_schedule_property_getset (GDBusConnection *connection, + GDBusMessage *message, + gboolean is_get, + const GDBusInterfaceInfo *introspection_data, + const GDBusInterfaceVTable *vtable, + GMainContext *main_context, + gpointer user_data) +{ + gboolean handled; + const char *interface_name; + const char *property_name; + const GDBusPropertyInfo *property_info; + GSource *idle_source; + PropertyData *property_data; + GDBusMessage *reply; + + handled = FALSE; + + if (is_get) + g_variant_get (g_dbus_message_get_body (message), + "(ss)", + &interface_name, + &property_name); + else + g_variant_get (g_dbus_message_get_body (message), + "(ssv)", + &interface_name, + &property_name, + NULL); + + + if (is_get) + { + if (vtable == NULL || vtable->get_property == NULL) + goto out; + } + else + { + if (vtable == NULL || vtable->set_property == NULL) + goto out; + } + + /* Check that the property exists - if not fail with org.freedesktop.DBus.Error.InvalidArgs + */ + property_info = NULL; + + /* TODO: the cost of this is O(n) - it might be worth caching the result */ + property_info = g_dbus_interface_info_lookup_property (introspection_data, property_name); + if (property_info == NULL) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("No such property `%s'"), + property_name); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + if (is_get && !(property_info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("Property `%s' is not readable"), + property_name); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + else if (!is_get && !(property_info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("Property `%s' is not writable"), + property_name); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + /* ok, got the property info - call user code in an idle handler */ + property_data = g_new0 (PropertyData, 1); + property_data->connection = g_object_ref (connection); + property_data->message = g_object_ref (message); + property_data->user_data = user_data; + property_data->property_name = property_name; + property_data->vtable = vtable; + property_data->interface_info = introspection_data; + property_data->property_info = property_info; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + is_get ? invoke_get_property_in_idle_cb : invoke_set_property_in_idle_cb, + property_data, + (GDestroyNotify) property_data_free); + g_source_attach (idle_source, main_context); + g_source_unref (idle_source); + + handled = TRUE; + + out: + return handled; +} + +/* called with lock held */ +static gboolean +handle_getset_property (GDBusConnection *connection, + ExportedObject *eo, + GDBusMessage *message, + gboolean is_get) +{ + ExportedInterface *ei; + gboolean handled; + const char *interface_name; + const char *property_name; + + handled = FALSE; + + if (is_get) + g_variant_get (g_dbus_message_get_body (message), + "(ss)", + &interface_name, + &property_name); + else + g_variant_get (g_dbus_message_get_body (message), + "(ssv)", + &interface_name, + &property_name, + NULL); + + /* Fail with org.freedesktop.DBus.Error.InvalidArgs if there is + * no such interface registered + */ + ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); + if (ei == NULL) + { + GDBusMessage *reply; + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("No such interface `%s'"), + interface_name); + g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + handled = validate_and_maybe_schedule_property_getset (eo->connection, + message, + is_get, + ei->introspection_data, + ei->vtable, + ei->context, + ei->user_data); + out: + return handled; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GDBusConnection *connection; + GDBusMessage *message; + gpointer user_data; + const GDBusInterfaceVTable *vtable; + const GDBusInterfaceInfo *interface_info; +} PropertyGetAllData; + +static void +property_get_all_data_free (PropertyData *data) +{ + g_object_unref (data->connection); + g_object_unref (data->message); + g_free (data); +} + +/* called in thread where object was registered - no locks held */ +static gboolean +invoke_get_all_properties_in_idle_cb (gpointer _data) +{ + PropertyGetAllData *data = _data; + GVariantBuilder *builder; + GVariant *packed; + GVariant *result; + GError *error; + GDBusMessage *reply; + guint n; + + error = NULL; + + /* TODO: Right now we never fail this call - we just omit values if + * a get_property() call is failing. + * + * We could fail the whole call if just a single get_property() call + * returns an error. We need clarification in the D-Bus spec about this. + */ + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + for (n = 0; data->interface_info->properties != NULL && data->interface_info->properties[n] != NULL; n++) + { + const GDBusPropertyInfo *property_info = data->interface_info->properties[n]; + GVariant *value; + + if (!(property_info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)) + continue; + + value = data->vtable->get_property (data->connection, + g_dbus_message_get_sender (data->message), + g_dbus_message_get_path (data->message), + data->interface_info->name, + property_info->name, + NULL, + data->user_data); + + if (value == NULL) + continue; + + g_variant_ref_sink (value); + g_variant_builder_add (builder, + "{sv}", + property_info->name, + value); + g_variant_unref (value); + } + result = g_variant_builder_end (builder); + + builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value (builder, result); /* steals result since result is floating */ + packed = g_variant_builder_end (builder); + + reply = g_dbus_message_new_method_reply (data->message); + g_dbus_message_set_body (reply, packed); + g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_object_unref (reply); + + return FALSE; +} + +/* called with lock held */ +static gboolean +validate_and_maybe_schedule_property_get_all (GDBusConnection *connection, + GDBusMessage *message, + const GDBusInterfaceInfo *introspection_data, + const GDBusInterfaceVTable *vtable, + GMainContext *main_context, + gpointer user_data) +{ + gboolean handled; + const char *interface_name; + GSource *idle_source; + PropertyGetAllData *property_get_all_data; + + handled = FALSE; + + g_variant_get (g_dbus_message_get_body (message), + "(s)", + &interface_name); + + if (vtable == NULL || vtable->get_property == NULL) + goto out; + + /* ok, got the property info - call user in an idle handler */ + property_get_all_data = g_new0 (PropertyGetAllData, 1); + property_get_all_data->connection = g_object_ref (connection); + property_get_all_data->message = g_object_ref (message); + property_get_all_data->user_data = user_data; + property_get_all_data->vtable = vtable; + property_get_all_data->interface_info = introspection_data; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + invoke_get_all_properties_in_idle_cb, + property_get_all_data, + (GDestroyNotify) property_get_all_data_free); + g_source_attach (idle_source, main_context); + g_source_unref (idle_source); + + handled = TRUE; + + out: + return handled; +} + +/* called with lock held */ +static gboolean +handle_get_all_properties (GDBusConnection *connection, + ExportedObject *eo, + GDBusMessage *message) +{ + ExportedInterface *ei; + gboolean handled; + const char *interface_name; + + handled = FALSE; + + g_variant_get (g_dbus_message_get_body (message), + "(s)", + &interface_name); + + /* Fail with org.freedesktop.DBus.Error.InvalidArgs if there is + * no such interface registered + */ + ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); + if (ei == NULL) + { + GDBusMessage *reply; + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("No such interface"), + interface_name); + g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + handled = validate_and_maybe_schedule_property_get_all (eo->connection, + message, + ei->introspection_data, + ei->vtable, + ei->context, + ei->user_data); + out: + return handled; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +introspect_append_header (GString *s) +{ + g_string_append (s, + "\n" + "\n" + "\n"); +} + +static void +introspect_append_standard_interfaces (GString *s) +{ + g_string_append (s, + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); +} + +static void +maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHashTable *set) +{ + if (g_str_has_prefix (object_path, path) && strlen (object_path) >= path_len) + { + const gchar *begin; + const gchar *end; + gchar *s; + + begin = object_path + path_len; + end = strchr (begin, '/'); + + if (end != NULL) + { + s = g_strndup (begin, end - begin); + } + else + { + s = g_strdup (begin); + } + + if (g_hash_table_lookup (set, s) == NULL) + { + g_hash_table_insert (set, s, GUINT_TO_POINTER (1)); + } + else + { + g_free (s); + } + } +} + +/* TODO: we want a nicer public interface for this */ +static gchar ** +g_dbus_connection_list_registered_unlocked (GDBusConnection *connection, + const gchar *path) +{ + GPtrArray *p; + gchar **ret; + GHashTableIter hash_iter; + const gchar *object_path; + gsize path_len; + GHashTable *set; + GList *keys; + GList *l; + + CONNECTION_ENSURE_LOCK (connection); + + path_len = strlen (path); + if (path_len > 1) + path_len++; + + set = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_iter_init (&hash_iter, connection->priv->map_object_path_to_eo); + while (g_hash_table_iter_next (&hash_iter, (gpointer) &object_path, NULL)) + maybe_add_path (path, path_len, object_path, set); + + g_hash_table_iter_init (&hash_iter, connection->priv->map_object_path_to_es); + while (g_hash_table_iter_next (&hash_iter, (gpointer) &object_path, NULL)) + maybe_add_path (path, path_len, object_path, set); + + p = g_ptr_array_new (); + keys = g_hash_table_get_keys (set); + for (l = keys; l != NULL; l = l->next) + { + g_ptr_array_add (p, l->data); + } + g_hash_table_unref (set); + g_list_free (keys); + + g_ptr_array_add (p, NULL); + ret = (gchar **) g_ptr_array_free (p, FALSE); + return ret; +} + +static gchar ** +g_dbus_connection_list_registered (GDBusConnection *connection, + const gchar *path) +{ + gchar **ret; + CONNECTION_LOCK (connection); + ret = g_dbus_connection_list_registered_unlocked (connection, path); + CONNECTION_UNLOCK (connection); + return ret; +} + +/* called in message handler thread with lock held */ +static gboolean +handle_introspect (GDBusConnection *connection, + ExportedObject *eo, + GDBusMessage *message) +{ + guint n; + GString *s; + GDBusMessage *reply; + GHashTableIter hash_iter; + ExportedInterface *ei; + gchar **registered; + + /* first the header with the standard interfaces */ + s = g_string_new (NULL); + introspect_append_header (s); + introspect_append_standard_interfaces (s); + + /* then include the registered interfaces */ + g_hash_table_iter_init (&hash_iter, eo->map_if_name_to_ei); + while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &ei)) + { + g_dbus_interface_info_generate_xml (ei->introspection_data, 2, s); + } + + /* finally include nodes registered below us */ + registered = g_dbus_connection_list_registered_unlocked (connection, eo->object_path); + for (n = 0; registered != NULL && registered[n] != NULL; n++) + { + g_string_append_printf (s, " \n", registered[n]); + } + g_strfreev (registered); + g_string_append (s, "\n"); + + reply = g_dbus_message_new_method_reply (message); + g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + g_string_free (s, TRUE); + + return TRUE; +} + +/* called in thread where object was registered - no locks held */ +static gboolean +invoke_method_in_idle_cb (gpointer user_data) +{ + GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data); + GDBusInterfaceVTable *vtable; + + vtable = g_object_get_data (G_OBJECT (invocation), "g-dbus-interface-vtable"); + g_assert (vtable != NULL && vtable->method_call != NULL); + + vtable->method_call (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), + g_object_ref (invocation), + g_dbus_method_invocation_get_user_data (invocation)); + + return FALSE; +} + +/* called in message handler thread with lock held */ +static gboolean +validate_and_maybe_schedule_method_call (GDBusConnection *connection, + GDBusMessage *message, + const GDBusInterfaceInfo *introspection_data, + const GDBusInterfaceVTable *vtable, + GMainContext *main_context, + gpointer user_data) +{ + GDBusMethodInvocation *invocation; + const GDBusMethodInfo *method_info; + GDBusMessage *reply; + GVariant *parameters; + GSource *idle_source; + gboolean handled; + gchar *in_signature; + + handled = FALSE; + + /* TODO: the cost of this is O(n) - it might be worth caching the result */ + method_info = g_dbus_interface_info_lookup_method (introspection_data, g_dbus_message_get_member (message)); + + /* if the method doesn't exist, return the org.freedesktop.DBus.Error.UnknownMethod + * error to the caller + */ + if (method_info == NULL) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.UnknownMethod", + _("No such method `%s'"), + g_dbus_message_get_member (message)); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + /* Check that the incoming args are of the right type - if they are not, return + * the org.freedesktop.DBus.Error.InvalidArgs error to the caller + * + * TODO: might also be worth caching the combined signature. + */ + in_signature = _g_dbus_compute_complete_signature (method_info->in_args, FALSE); + if (g_strcmp0 (g_dbus_message_get_signature (message), in_signature) != 0) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("Signature of message, `%s', does not match expected signature `%s'"), + g_dbus_message_get_signature (message), + in_signature); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + g_free (in_signature); + handled = TRUE; + goto out; + } + g_free (in_signature); + + parameters = g_dbus_message_get_body (message); + if (parameters == NULL) + { + parameters = g_variant_new ("()"); + g_variant_ref_sink (parameters); + } + else + { + g_variant_ref (parameters); + } + + /* schedule the call in idle */ + invocation = g_dbus_method_invocation_new (g_dbus_message_get_sender (message), + g_dbus_message_get_path (message), + g_dbus_message_get_interface (message), + g_dbus_message_get_member (message), + method_info, + connection, + message, + parameters, + user_data); + g_variant_unref (parameters); + g_object_set_data (G_OBJECT (invocation), + "g-dbus-interface-vtable", + (gpointer) vtable); + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + invoke_method_in_idle_cb, + invocation, + g_object_unref); + g_source_attach (idle_source, main_context); + g_source_unref (idle_source); + + handled = TRUE; + + out: + + return handled; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* called in message handler thread with lock held */ +static gboolean +obj_message_func (GDBusConnection *connection, + ExportedObject *eo, + GDBusMessage *message) +{ + const gchar *interface_name; + const gchar *member; + const gchar *signature; + gboolean handled; + + handled = FALSE; + + interface_name = g_dbus_message_get_interface (message); + member = g_dbus_message_get_member (message); + signature = g_dbus_message_get_signature (message); + + /* see if we have an interface for handling this call */ + if (interface_name != NULL) + { + ExportedInterface *ei; + ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); + if (ei != NULL) + { + /* we do - invoke the handler in idle in the right thread */ + + /* handle no vtable or handler being present */ + if (ei->vtable == NULL || ei->vtable->method_call == NULL) + goto out; + + handled = validate_and_maybe_schedule_method_call (connection, + message, + ei->introspection_data, + ei->vtable, + ei->context, + ei->user_data); + goto out; + } + } + + if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Introspectable") == 0 && + g_strcmp0 (member, "Introspect") == 0 && + g_strcmp0 (signature, "") == 0) + { + handled = handle_introspect (connection, eo, message); + goto out; + } + else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0 && + g_strcmp0 (member, "Get") == 0 && + g_strcmp0 (signature, "ss") == 0) + { + handled = handle_getset_property (connection, eo, message, TRUE); + goto out; + } + else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0 && + g_strcmp0 (member, "Set") == 0 && + g_strcmp0 (signature, "ssv") == 0) + { + handled = handle_getset_property (connection, eo, message, FALSE); + goto out; + } + else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0 && + g_strcmp0 (member, "GetAll") == 0 && + g_strcmp0 (signature, "s") == 0) + { + handled = handle_get_all_properties (connection, eo, message); + goto out; + } + + out: + return handled; +} + +/** + * g_dbus_connection_register_object: + * @connection: A #GDBusConnection. + * @object_path: The object path to register at. + * @interface_name: The D-Bus interface to register. + * @introspection_data: Introspection data for the interface. + * @vtable: A #GDBusInterfaceVTable to call into or %NULL. + * @user_data: Data to pass to functions in @vtable. + * @user_data_free_func: Function to call when the object path is unregistered. + * @error: Return location for error or %NULL. + * + * Registers callbacks for exported objects at @object_path with the + * D-Bus interface @interface_name. + * + * Calls to functions in @vtable (and @user_data_free_func) will + * happen in the thread-default main + * loop of the thread you are calling this method from. + * + * Note that all #GVariant values passed to functions in @vtable will match + * the signature given in @introspection_data - if a remote caller passes + * incorrect values, the org.freedesktop.DBus.Error.InvalidArgs + * is returned to the remote caller. + * + * Additionally, if the remote caller attempts to invoke methods or + * access properties not mentioned in @introspection_data the + * org.freedesktop.DBus.Error.UnknownMethod resp. + * org.freedesktop.DBus.Error.InvalidArgs errors + * are returned to the caller. + * + * It is considered a programming error if the + * #GDBusInterfaceGetPropertyFunc function in @vtable returns a + * #GVariant of incorrect type. + * + * If an existing callback is already registered at @object_path and + * @interface_name, then @error is set to #G_IO_ERROR_EXISTS. + * + * See for an example of how to use this method. + * + * Returns: 0 if @error is set, otherwise a registration id (never 0) + * that can be used with g_dbus_connection_unregister_object() . + */ +guint +g_dbus_connection_register_object (GDBusConnection *connection, + const gchar *object_path, + const gchar *interface_name, + const GDBusInterfaceInfo *introspection_data, + const GDBusInterfaceVTable *vtable, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error) +{ + ExportedObject *eo; + ExportedInterface *ei; + guint ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); + g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), 0); + g_return_val_if_fail (introspection_data != NULL, 0); + g_return_val_if_fail (error == NULL || *error == NULL, 0); + + ret = 0; + + CONNECTION_LOCK (connection); + + eo = g_hash_table_lookup (connection->priv->map_object_path_to_eo, object_path); + if (eo == NULL) + { + eo = g_new0 (ExportedObject, 1); + eo->object_path = g_strdup (object_path); + eo->connection = connection; + eo->map_if_name_to_ei = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) exported_interface_free); + g_hash_table_insert (connection->priv->map_object_path_to_eo, eo->object_path, eo); + } + + ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); + if (ei != NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_EXISTS, + _("An object is already exported for the interface %s at %s"), + interface_name, + object_path); + goto out; + } + + ei = g_new0 (ExportedInterface, 1); + ei->id = _global_registration_id++; /* TODO: overflow etc. */ + ei->eo = eo; + ei->user_data = user_data; + ei->user_data_free_func = user_data_free_func; + ei->vtable = vtable; + ei->introspection_data = introspection_data; + ei->interface_name = g_strdup (interface_name); + ei->context = g_main_context_get_thread_default (); + if (ei->context != NULL) + g_main_context_ref (ei->context); + + g_hash_table_insert (eo->map_if_name_to_ei, + (gpointer) ei->interface_name, + ei); + g_hash_table_insert (connection->priv->map_id_to_ei, + GUINT_TO_POINTER (ei->id), + ei); + + ret = ei->id; + + out: + CONNECTION_UNLOCK (connection); + + return ret; +} + +/** + * g_dbus_connection_unregister_object: + * @connection: A #GDBusConnection. + * @registration_id: A registration id obtained from g_dbus_connection_register_object(). + * + * Unregisters an object. + * + * Returns: %TRUE if the object was unregistered, %FALSE otherwise. + */ +gboolean +g_dbus_connection_unregister_object (GDBusConnection *connection, + guint registration_id) +{ + ExportedInterface *ei; + ExportedObject *eo; + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + + ret = FALSE; + + CONNECTION_LOCK (connection); + + ei = g_hash_table_lookup (connection->priv->map_id_to_ei, + GUINT_TO_POINTER (registration_id)); + if (ei == NULL) + { + goto out; + } + + eo = ei->eo; + + g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_ei, GUINT_TO_POINTER (ei->id))); + g_warn_if_fail (g_hash_table_remove (eo->map_if_name_to_ei, ei->interface_name)); + /* unregister object path if we have no more exported interfaces */ + if (g_hash_table_size (eo->map_if_name_to_ei) == 0) + { + g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_eo, + eo->object_path)); + } + + ret = TRUE; + + out: + CONNECTION_UNLOCK (connection); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_emit_signal: + * @connection: A #GDBusConnection. + * @destination_bus_name: The unique bus name for the destination for the signal or %NULL to emit to all listeners. + * @object_path: Path of remote object. + * @interface_name: D-Bus interface to emit a signal on. + * @signal_name: The name of the signal to emit. + * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters. + * @error: Return location for error or %NULL. + * + * Emits a signal. + * + * This can only fail if @parameters is not compatible with the D-Bus protocol. + * + * Returns: %TRUE unless @error is set. + */ +gboolean +g_dbus_connection_emit_signal (GDBusConnection *connection, + const gchar *destination_bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + GError **error) +{ + GDBusMessage *message; + gboolean ret; + + message = NULL; + ret = FALSE; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (destination_bus_name == NULL || g_dbus_is_name (destination_bus_name), FALSE); + g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), FALSE); + g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), FALSE); + g_return_val_if_fail (signal_name != NULL && g_dbus_is_member_name (signal_name), FALSE); + g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), FALSE); + + message = g_dbus_message_new_signal (object_path, + interface_name, + signal_name); + + if (destination_bus_name != NULL) + { + g_dbus_message_set_header (message, + G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, + g_variant_new_string (destination_bus_name)); + } + + if (parameters != NULL) + g_dbus_message_set_body (message, parameters); + + ret = g_dbus_connection_send_message (connection, message, NULL, error); + g_object_unref (message); + + return ret; +} + +static void +add_invoke_method_flags (GDBusMessage *message, + GDBusInvokeMethodFlags flags) +{ + if (flags & G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START) + g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_AUTO_START); +} + +/** + * g_dbus_connection_invoke_method: + * @connection: A #GDBusConnection. + * @bus_name: A unique or well-known bus name or %NULL if @connection is not a message bus connection. + * @object_path: Path of remote object. + * @interface_name: D-Bus interface to invoke method on. + * @method_name: The name of the method to invoke. + * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. + * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't + * care about the result of the method invocation. + * @user_data: The data to pass to @callback. + * + * Asynchronously invokes the @method_name method on the + * @interface_name D-Bus interface on the remote object at + * @object_path owned by @bus_name. + * + * If @connection is closed then the operation will fail with + * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will + * fail with %G_IO_ERROR_CANCELLED. If @parameters contains a value + * not compatible with the D-Bus protocol, the operation fails with + * %G_IO_ERROR_INVALID_ARGUMENT. + * + * This is an asynchronous method. When the operation is finished, @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_connection_invoke_method_finish() to get the result of the operation. + * See g_dbus_connection_invoke_method_sync() for the synchronous version of this + * function. + */ +void +g_dbus_connection_invoke_method (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusMessage *message; + + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name)); + g_return_if_fail (object_path != NULL && g_variant_is_object_path (object_path)); + g_return_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name)); + g_return_if_fail (method_name != NULL && g_dbus_is_member_name (method_name)); + g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); + g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); + + message = g_dbus_message_new_method_call (bus_name, + object_path, + interface_name, + method_name); + add_invoke_method_flags (message, flags); + if (parameters != NULL) + g_dbus_message_set_body (message, parameters); + + g_dbus_connection_send_message_with_reply (connection, + message, + timeout_msec, + NULL, /* volatile guint32 *out_serial */ + cancellable, + callback, + user_data); + + if (message != NULL) + g_object_unref (message); +} + +static GVariant * +decode_method_reply (GDBusMessage *reply, GError **error) +{ + GVariant *result; + + result = NULL; + switch (g_dbus_message_get_type (reply)) + { + case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: + result = g_dbus_message_get_body (reply); + if (result == NULL) + { + result = g_variant_new ("()"); + g_variant_ref_sink (result); + } + else + { + g_variant_ref (result); + } + break; + case G_DBUS_MESSAGE_TYPE_ERROR: + g_dbus_message_to_gerror (reply, error); + break; + + default: + g_assert_not_reached (); + break; + } + + return result; +} + +/** + * g_dbus_connection_invoke_method_finish: + * @connection: A #GDBusConnection. + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_invoke_method(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_invoke_method(). + * + * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with + * return values. Free with g_variant_unref(). + */ +GVariant * +g_dbus_connection_invoke_method_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) +{ + GDBusMessage *reply; + GVariant *result; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + result = NULL; + + reply = g_dbus_connection_send_message_with_reply_finish (connection, res, error); + if (reply == NULL) + goto out; + + result = decode_method_reply (reply, error); + + g_object_unref (reply); + + out: + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_invoke_method_sync: + * @connection: A #GDBusConnection. + * @bus_name: A unique or well-known bus name. + * @object_path: Path of remote object. + * @interface_name: D-Bus interface to invoke method on. + * @method_name: The name of the method to invoke. + * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. + * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the @method_name method on the + * @interface_name D-Bus interface on the remote object at + * @object_path owned by @bus_name. + * + * If @connection is closed then the operation will fail with + * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the + * operation will fail with %G_IO_ERROR_CANCELLED. If @parameters + * contains a value not compatible with the D-Bus protocol, the operation + * fails with %G_IO_ERROR_INVALID_ARGUMENT. + * + * The calling thread is blocked until a reply is received. See + * g_dbus_connection_invoke_method() for the asynchronous version of + * this method. + * + * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with + * return values. Free with g_variant_unref(). + */ +GVariant * +g_dbus_connection_invoke_method_sync (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) +{ + GDBusMessage *message; + GDBusMessage *reply; + GVariant *result; + + message = NULL; + reply = NULL; + result = NULL; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (bus_name == NULL || g_dbus_is_name (bus_name), NULL); + g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), NULL); + g_return_val_if_fail (method_name != NULL && g_dbus_is_member_name (method_name), NULL); + g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); + g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); + + message = g_dbus_message_new_method_call (bus_name, + object_path, + interface_name, + method_name); + add_invoke_method_flags (message, flags); + if (parameters != NULL) + g_dbus_message_set_body (message, parameters); + + reply = g_dbus_connection_send_message_with_reply_sync (connection, + message, + timeout_msec, + NULL, /* volatile guint32 *out_serial */ + cancellable, + error); + + if (reply == NULL) + goto out; + + result = decode_method_reply (reply, error); + + out: + if (message != NULL) + g_object_unref (message); + if (reply != NULL) + g_object_unref (reply); + + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct ExportedSubtree +{ + guint id; + gchar *object_path; + GDBusConnection *connection; + const GDBusSubtreeVTable *vtable; + GDBusSubtreeFlags flags; + + GMainContext *context; + gpointer user_data; + GDestroyNotify user_data_free_func; +}; + +static void +exported_subtree_free (ExportedSubtree *es) +{ + if (es->user_data_free_func != NULL) + { + /* TODO: push to thread-default mainloop */ + es->user_data_free_func (es->user_data); + } + if (es->context != NULL) + { + g_main_context_unref (es->context); + } + g_free (es->object_path); + g_free (es); +} + +/* called without lock held */ +static gboolean +handle_subtree_introspect (GDBusConnection *connection, + ExportedSubtree *es, + GDBusMessage *message) +{ + GString *s; + gboolean handled; + GDBusMessage *reply; + gchar **children; + gboolean is_root; + const gchar *sender; + const gchar *requested_object_path; + const gchar *requested_node; + GPtrArray *interfaces; + guint n; + gchar **subnode_paths; + + handled = FALSE; + + requested_object_path = g_dbus_message_get_path (message); + sender = g_dbus_message_get_sender (message); + is_root = (g_strcmp0 (requested_object_path, es->object_path) == 0); + + s = g_string_new (NULL); + introspect_append_header (s); + + /* Strictly we don't need the children in dynamic mode, but we avoid the + * conditionals to preserve code clarity + */ + children = es->vtable->enumerate (es->connection, + sender, + es->object_path, + es->user_data); + + if (!is_root) + { + requested_node = strrchr (requested_object_path, '/') + 1; + + /* Assert existence of object if we are not dynamic */ + if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) && + !_g_strv_has_string ((const gchar * const *) children, requested_node)) + goto out; + } + else + { + requested_node = "/"; + } + + interfaces = es->vtable->introspect (es->connection, + sender, + es->object_path, + requested_node, + es->user_data); + if (interfaces != NULL) + { + if (interfaces->len > 0) + { + /* we're in business */ + introspect_append_standard_interfaces (s); + + for (n = 0; n < interfaces->len; n++) + { + const GDBusInterfaceInfo *interface_info = interfaces->pdata[n]; + g_dbus_interface_info_generate_xml (interface_info, 2, s); + } + } + g_ptr_array_unref (interfaces); + } + + /* then include entries from the Subtree for the root */ + if (is_root) + { + for (n = 0; children != NULL && children[n] != NULL; n++) + { + g_string_append_printf (s, " \n", children[n]); + } + } + + /* finally include nodes registered below us */ + subnode_paths = g_dbus_connection_list_registered (es->connection, requested_object_path); + for (n = 0; subnode_paths != NULL && subnode_paths[n] != NULL; n++) + { + g_string_append_printf (s, " \n", subnode_paths[n]); + } + g_strfreev (subnode_paths); + + g_string_append (s, "\n"); + + reply = g_dbus_message_new_method_reply (message); + g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); + g_dbus_connection_send_message (connection, reply, NULL, NULL); + g_object_unref (reply); + + handled = TRUE; + + out: + g_string_free (s, TRUE); + g_strfreev (children); + return handled; +} + +/* called without lock held */ +static gboolean +handle_subtree_method_invocation (GDBusConnection *connection, + ExportedSubtree *es, + GDBusMessage *message) +{ + gboolean handled;; + const gchar *sender; + const gchar *interface_name; + const gchar *member; + const gchar *signature; + const gchar *requested_object_path; + const gchar *requested_node; + gboolean is_root; + gchar **children; + const GDBusInterfaceInfo *introspection_data; + const GDBusInterfaceVTable *interface_vtable; + gpointer interface_user_data; + guint n; + GPtrArray *interfaces; + gboolean is_property_get; + gboolean is_property_set; + gboolean is_property_get_all; + + handled = FALSE; + interfaces = NULL; + + requested_object_path = g_dbus_message_get_path (message); + sender = g_dbus_message_get_sender (message); + interface_name = g_dbus_message_get_interface (message); + member = g_dbus_message_get_member (message); + signature = g_dbus_message_get_signature (message); + is_root = (g_strcmp0 (requested_object_path, es->object_path) == 0); + + is_property_get = FALSE; + is_property_set = FALSE; + is_property_get_all = FALSE; + if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0) + { + if (g_strcmp0 (member, "Get") == 0 && g_strcmp0 (signature, "ss") == 0) + { + is_property_get = TRUE; + } + else if (g_strcmp0 (member, "Set") == 0 && g_strcmp0 (signature, "ssv") == 0) + { + is_property_set = TRUE; + } + else if (g_strcmp0 (member, "GetAll") == 0 && g_strcmp0 (signature, "s") == 0) + { + is_property_get_all = TRUE; + } + } + + children = es->vtable->enumerate (es->connection, + sender, + es->object_path, + es->user_data); + + if (!is_root) + { + requested_node = strrchr (requested_object_path, '/') + 1; + + /* If not dynamic, skip if requested node is not part of children */ + if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) && + !_g_strv_has_string ((const gchar * const *) children, requested_node)) + goto out; + } + else + { + requested_node = "/"; + } + + /* get introspection data for the node */ + interfaces = es->vtable->introspect (es->connection, + sender, + requested_object_path, + requested_node, + es->user_data); + g_assert (interfaces != NULL); + introspection_data = NULL; + for (n = 0; n < interfaces->len; n++) + { + const GDBusInterfaceInfo *id_n = (const GDBusInterfaceInfo *) interfaces->pdata[n]; + if (g_strcmp0 (id_n->name, interface_name) == 0) + { + introspection_data = id_n; + } + } + + /* dispatch the call if the user wants to handle it */ + if (introspection_data != NULL) + { + /* figure out where to dispatch the method call */ + interface_user_data = NULL; + interface_vtable = es->vtable->dispatch (es->connection, + sender, + es->object_path, + interface_name, + requested_node, + &interface_user_data, + es->user_data); + if (interface_vtable == NULL) + goto out; + + CONNECTION_LOCK (connection); + handled = validate_and_maybe_schedule_method_call (es->connection, + message, + introspection_data, + interface_vtable, + es->context, + interface_user_data); + CONNECTION_UNLOCK (connection); + } + /* handle org.freedesktop.DBus.Properties interface if not explicitly handled */ + else if (is_property_get || is_property_set || is_property_get_all) + { + if (is_property_get) + g_variant_get (g_dbus_message_get_body (message), "(ss)", &interface_name, NULL); + else if (is_property_set) + g_variant_get (g_dbus_message_get_body (message), "(ssv)", &interface_name, NULL, NULL); + else if (is_property_get_all) + g_variant_get (g_dbus_message_get_body (message), "(s)", &interface_name, NULL, NULL); + else + g_assert_not_reached (); + + /* see if the object supports this interface at all */ + for (n = 0; n < interfaces->len; n++) + { + const GDBusInterfaceInfo *id_n = (const GDBusInterfaceInfo *) interfaces->pdata[n]; + if (g_strcmp0 (id_n->name, interface_name) == 0) + { + introspection_data = id_n; + } + } + + /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the user-code + * claims it won't support the interface + */ + if (introspection_data == NULL) + { + GDBusMessage *reply; + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("No such interface `%s'"), + interface_name); + g_dbus_connection_send_message (es->connection, reply, NULL, NULL); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + /* figure out where to dispatch the property get/set/getall calls */ + interface_user_data = NULL; + interface_vtable = es->vtable->dispatch (es->connection, + sender, + es->object_path, + interface_name, + requested_node, + &interface_user_data, + es->user_data); + if (interface_vtable == NULL) + goto out; + + if (is_property_get || is_property_set) + { + CONNECTION_LOCK (connection); + handled = validate_and_maybe_schedule_property_getset (es->connection, + message, + is_property_get, + introspection_data, + interface_vtable, + es->context, + interface_user_data); + CONNECTION_UNLOCK (connection); + } + else if (is_property_get_all) + { + CONNECTION_LOCK (connection); + handled = validate_and_maybe_schedule_property_get_all (es->connection, + message, + introspection_data, + interface_vtable, + es->context, + interface_user_data); + CONNECTION_UNLOCK (connection); + } + } + + out: + if (interfaces != NULL) + g_ptr_array_unref (interfaces); + g_strfreev (children); + return handled; +} + +typedef struct +{ + GDBusMessage *message; + ExportedSubtree *es; +} SubtreeDeferredData; + +static void +subtree_deferred_data_free (SubtreeDeferredData *data) +{ + g_object_unref (data->message); + g_free (data); +} + +/* called without lock held in the thread where the caller registered the subtree */ +static gboolean +process_subtree_vtable_message_in_idle_cb (gpointer _data) +{ + SubtreeDeferredData *data = _data; + gboolean handled; + + handled = FALSE; + + if (g_strcmp0 (g_dbus_message_get_interface (data->message), "org.freedesktop.DBus.Introspectable") == 0 && + g_strcmp0 (g_dbus_message_get_member (data->message), "Introspect") == 0 && + g_strcmp0 (g_dbus_message_get_signature (data->message), "") == 0) + { + handled = handle_subtree_introspect (data->es->connection, + data->es, + data->message); + } + else + { + handled = handle_subtree_method_invocation (data->es->connection, + data->es, + data->message); + } + + + if (!handled) + { + CONNECTION_LOCK (data->es->connection); + handled = handle_generic_unlocked (data->es->connection, data->message); + CONNECTION_UNLOCK (data->es->connection); + } + + /* if we couldn't handle the request, just bail with the UnknownMethod error */ + if (!handled) + { + GDBusMessage *reply; + reply = g_dbus_message_new_method_error (data->message, + "org.freedesktop.DBus.Error.UnknownMethod", + _("Method `%s' on interface `%s' with signature `%s' does not exist"), + g_dbus_message_get_member (data->message), + g_dbus_message_get_interface (data->message), + g_dbus_message_get_signature (data->message)); + g_dbus_connection_send_message (data->es->connection, reply, NULL, NULL); + g_object_unref (reply); + } + + return FALSE; +} + +/* called in message handler thread with lock held */ +static gboolean +subtree_message_func (GDBusConnection *connection, + ExportedSubtree *es, + GDBusMessage *message) +{ + GSource *idle_source; + SubtreeDeferredData *data; + + data = g_new0 (SubtreeDeferredData, 1); + data->message = g_object_ref (message); + data->es = es; + + /* defer this call to an idle handler in the right thread */ + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_HIGH); + g_source_set_callback (idle_source, + process_subtree_vtable_message_in_idle_cb, + data, + (GDestroyNotify) subtree_deferred_data_free); + g_source_attach (idle_source, es->context); + g_source_unref (idle_source); + + /* since we own the entire subtree, handlers for objects not in the subtree have been + * tried already by libdbus-1 - so we just need to ensure that we're always going + * to reply to the message + */ + return TRUE; +} + +/** + * g_dbus_connection_register_subtree: + * @connection: A #GDBusConnection. + * @object_path: The object path to register the subtree at. + * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree. + * @flags: Flags used to fine tune the behavior of the subtree. + * @user_data: Data to pass to functions in @vtable. + * @user_data_free_func: Function to call when the subtree is unregistered. + * @error: Return location for error or %NULL. + * + * Registers a whole subtree of dynamic objects. + * + * The @enumerate and @introspection functions in @vtable are used to + * convey, to remote callers, what nodes exist in the subtree rooted + * by @object_path. + * + * When handling remote calls into any node in the subtree, first the + * @enumerate function is used to check if the node exists. If the node exists + * or the #G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is set + * the @introspection function is used to check if the node supports the + * requested method. If so, the @dispatch function is used to determine + * where to dispatch the call. The collected #GDBusInterfaceVTable and + * #gpointer will be used to call into the interface vtable for processing + * the request. + * + * All calls into user-provided code will be invoked in the thread-default main + * loop of the thread you are calling this method from. + * + * If an existing subtree is already registered at @object_path or + * then @error is set to #G_IO_ERROR_EXISTS. + * + * Note that it is valid to register regular objects (using + * g_dbus_connection_register_object()) in a subtree registered with + * g_dbus_connection_register_subtree() - if so, the subtree handler + * is tried as the last resort. One way to think about a subtree + * handler is to consider it a fallback handler + * for object paths not registered via g_dbus_connection_register_object() + * or other bindings. + * + * See for an example of how to use this method. + * + * Returns: 0 if @error is set, otherwise a subtree registration id (never 0) + * that can be used with g_dbus_connection_unregister_subtree() . + */ +guint +g_dbus_connection_register_subtree (GDBusConnection *connection, + const gchar *object_path, + const GDBusSubtreeVTable *vtable, + GDBusSubtreeFlags flags, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error) +{ + guint ret; + ExportedSubtree *es; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (vtable != NULL, 0); + g_return_val_if_fail (error == NULL || *error == NULL, 0); + + ret = 0; + + CONNECTION_LOCK (connection); + + es = g_hash_table_lookup (connection->priv->map_object_path_to_es, object_path); + if (es != NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_EXISTS, + _("A subtree is already exported for %s"), + object_path); + goto out; + } + + es = g_new0 (ExportedSubtree, 1); + es->object_path = g_strdup (object_path); + es->connection = connection; + + es->vtable = vtable; + es->flags = flags; + es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */ + es->user_data = user_data; + es->user_data_free_func = user_data_free_func; + es->context = g_main_context_get_thread_default (); + if (es->context != NULL) + g_main_context_ref (es->context); + + g_hash_table_insert (connection->priv->map_object_path_to_es, es->object_path, es); + g_hash_table_insert (connection->priv->map_id_to_es, + GUINT_TO_POINTER (es->id), + es); + + ret = es->id; + + out: + CONNECTION_UNLOCK (connection); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_unregister_subtree: + * @connection: A #GDBusConnection. + * @registration_id: A subtree registration id obtained from g_dbus_connection_register_subtree(). + * + * Unregisters a subtree. + * + * Returns: %TRUE if the subtree was unregistered, %FALSE otherwise. + */ +gboolean +g_dbus_connection_unregister_subtree (GDBusConnection *connection, + guint registration_id) +{ + ExportedSubtree *es; + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + + ret = FALSE; + + CONNECTION_LOCK (connection); + + es = g_hash_table_lookup (connection->priv->map_id_to_es, + GUINT_TO_POINTER (registration_id)); + if (es == NULL) + { + goto out; + } + + g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_es, GUINT_TO_POINTER (es->id))); + g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_es, es->object_path)); + + ret = TRUE; + + out: + CONNECTION_UNLOCK (connection); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* must be called with lock held */ +static void +handle_generic_ping_unlocked (GDBusConnection *connection, + const gchar *object_path, + GDBusMessage *message) +{ + GDBusMessage *reply; + reply = g_dbus_message_new_method_reply (message); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); +} + +/* must be called with lock held */ +static void +handle_generic_get_machine_id_unlocked (GDBusConnection *connection, + const gchar *object_path, + GDBusMessage *message) +{ + GDBusMessage *reply; + + reply = NULL; + if (connection->priv->machine_id == NULL) + { + GError *error; + error = NULL; + /* TODO: use PACKAGE_LOCALSTATEDIR ? */ + if (!g_file_get_contents ("/var/lib/dbus/machine-id", + &connection->priv->machine_id, + NULL, + &error)) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.Failed", + _("Unable to load /var/lib/dbus/machine-id: %s"), + error->message); + g_error_free (error); + } + else + { + g_strstrip (connection->priv->machine_id); + /* TODO: validate value */ + } + } + + if (reply == NULL) + { + reply = g_dbus_message_new_method_reply (message); + g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->priv->machine_id)); + } + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); +} + +/* must be called with lock held */ +static void +handle_generic_introspect_unlocked (GDBusConnection *connection, + const gchar *object_path, + GDBusMessage *message) +{ + guint n; + GString *s; + gchar **registered; + GDBusMessage *reply; + + /* first the header */ + s = g_string_new (NULL); + introspect_append_header (s); + + registered = g_dbus_connection_list_registered_unlocked (connection, object_path); + for (n = 0; registered != NULL && registered[n] != NULL; n++) + { + g_string_append_printf (s, " \n", registered[n]); + } + g_strfreev (registered); + g_string_append (s, "\n"); + + reply = g_dbus_message_new_method_reply (message); + g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); + g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_object_unref (reply); + g_string_free (s, TRUE); +} + +/* must be called with lock held */ +static gboolean +handle_generic_unlocked (GDBusConnection *connection, + GDBusMessage *message) +{ + gboolean handled; + const gchar *interface_name; + const gchar *member; + const gchar *signature; + const gchar *path; + + CONNECTION_ENSURE_LOCK (connection); + + handled = FALSE; + + interface_name = g_dbus_message_get_interface (message); + member = g_dbus_message_get_member (message); + signature = g_dbus_message_get_signature (message); + path = g_dbus_message_get_path (message); + + if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Introspectable") == 0 && + g_strcmp0 (member, "Introspect") == 0 && + g_strcmp0 (signature, "") == 0) + { + handle_generic_introspect_unlocked (connection, path, message); + handled = TRUE; + } + else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Peer") == 0 && + g_strcmp0 (member, "Ping") == 0 && + g_strcmp0 (signature, "") == 0) + { + handle_generic_ping_unlocked (connection, path, message); + handled = TRUE; + } + else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Peer") == 0 && + g_strcmp0 (member, "GetMachineId") == 0 && + g_strcmp0 (signature, "") == 0) + { + handle_generic_get_machine_id_unlocked (connection, path, message); + handled = TRUE; + } + + return handled; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* called in message handler thread with lock held */ +static void +distribute_method_call (GDBusConnection *connection, + GDBusMessage *message) +{ + ExportedObject *eo; + ExportedSubtree *es; + const gchar *object_path; + const gchar *interface_name; + const gchar *member; + const gchar *signature; + const gchar *path; + gchar *subtree_path; + gchar *needle; + + g_assert (g_dbus_message_get_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL); + + interface_name = g_dbus_message_get_interface (message); + member = g_dbus_message_get_member (message); + signature = g_dbus_message_get_signature (message); + path = g_dbus_message_get_path (message); + subtree_path = g_strdup (path); + needle = strrchr (subtree_path, '/'); + if (needle != NULL && needle != subtree_path) + { + *needle = '\0'; + } + else + { + g_free (subtree_path); + subtree_path = NULL; + } + +#if 0 + g_debug ("interface = `%s'", interface_name); + g_debug ("member = `%s'", member); + g_debug ("signature = `%s'", signature); + g_debug ("path = `%s'", path); + g_debug ("subtree_path = `%s'", subtree_path != NULL ? subtree_path : "N/A"); +#endif + + object_path = g_dbus_message_get_path (message); + g_assert (object_path != NULL); + + eo = g_hash_table_lookup (connection->priv->map_object_path_to_eo, object_path); + if (eo != NULL) + { + if (obj_message_func (connection, eo, message)) + goto out; + } + + es = g_hash_table_lookup (connection->priv->map_object_path_to_es, object_path); + if (es != NULL) + { + if (subtree_message_func (connection, es, message)) + goto out; + } + + if (subtree_path != NULL) + { + es = g_hash_table_lookup (connection->priv->map_object_path_to_es, subtree_path); + if (es != NULL) + { + if (subtree_message_func (connection, es, message)) + goto out; + } + } + + if (handle_generic_unlocked (connection, message)) + goto out; + + /* if we end up here, the message has not been not handled */ + + out: + g_free (subtree_path); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusConnection ** +message_bus_get_singleton (GBusType bus_type, + GError **error) +{ + GDBusConnection **ret; + const gchar *starter_bus; + + ret = NULL; + + switch (bus_type) + { + default: + g_assert_not_reached (); + break; + + case G_BUS_TYPE_SESSION: + ret = &the_session_bus; + break; + + case G_BUS_TYPE_SYSTEM: + ret = &the_system_bus; + break; + + case G_BUS_TYPE_STARTER: + starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE"); + if (g_strcmp0 (starter_bus, "session") == 0) + { + ret = message_bus_get_singleton (G_BUS_TYPE_SESSION, error); + goto out; + } + else if (g_strcmp0 (starter_bus, "system") == 0) + { + ret = message_bus_get_singleton (G_BUS_TYPE_SYSTEM, error); + goto out; + } + else + { + if (starter_bus != NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable" + " - unknown value `%s'"), + starter_bus); + } + else + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment " + "variable is not set")); + } + } + break; + } + out: + return ret; +} + +static GDBusConnection * +get_uninitialized_connection (GBusType bus_type, + GCancellable *cancellable, + GError **error) +{ + GDBusConnection **singleton; + GDBusConnection *ret; + + ret = NULL; + + G_LOCK (message_bus_lock); + singleton = message_bus_get_singleton (bus_type, error); + if (singleton == NULL) + goto out; + + if (*singleton == NULL) + { + gchar *address; + address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error); + if (address == NULL) + goto out; + ret = *singleton = g_object_new (G_TYPE_DBUS_CONNECTION, + "address", address, + "flags", G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + "exit-on-close", TRUE, + NULL); + } + else + { + ret = g_object_ref (*singleton); + } + + g_assert (ret != NULL); + + out: + G_UNLOCK (message_bus_lock); + return ret; +} + +/** + * g_bus_get_sync: + * @bus_type: A #GBusType. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously connects to the message bus specified by @bus_type. + * Note that the returned object may shared with other callers, + * e.g. if two separate parts of a process calls this function with + * the same @bus_type, they will share the same object. + * + * This is a synchronous failable function. See g_bus_get() and + * g_bus_get_finish() for the asynchronous version. + * + * The returned object is a singleton, that is, shared with other + * callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the + * event that you need a private message bus connection, use + * g_dbus_address_get_for_bus() and + * g_dbus_connection_new_for_address(). + * + * Note that the returned #GDBusConnection object will (usually) have + * the #GDBusConnection:exit-on-close property set to %TRUE. + * + * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + */ +GDBusConnection * +g_bus_get_sync (GBusType bus_type, + GCancellable *cancellable, + GError **error) +{ + GDBusConnection *connection; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + connection = get_uninitialized_connection (bus_type, cancellable, error); + if (connection == NULL) + goto out; + + if (!g_initable_init (G_INITABLE (connection), cancellable, error)) + { + g_object_unref (connection); + connection = NULL; + } + + out: + return connection; +} + +static void +bus_get_async_initable_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error; + + error = NULL; + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source_object), + res, + &error)) + { + g_assert (error != NULL); + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + g_object_unref (source_object); + } + else + { + g_simple_async_result_set_op_res_gpointer (simple, + source_object, + g_object_unref); + } + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); +} + +/** + * g_bus_get: + * @bus_type: A #GBusType. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: The data to pass to @callback. + * + * Asynchronously connects to the message bus specified by @bus_type. + * + * When the operation is finished, @callback will be invoked. You can + * then call g_bus_get_finish() to get the result of the operation. + * + * This is a asynchronous failable function. See g_bus_get_sync() for + * the synchronous version. + */ +void +g_bus_get (GBusType bus_type, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusConnection *connection; + GSimpleAsyncResult *simple; + GError *error; + + simple = g_simple_async_result_new (NULL, + callback, + user_data, + g_bus_get); + + error = NULL; + connection = get_uninitialized_connection (bus_type, cancellable, &error); + if (connection == NULL) + { + g_assert (error != NULL); + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + } + else + { + g_async_initable_init_async (G_ASYNC_INITABLE (connection), + G_PRIORITY_DEFAULT, + cancellable, + bus_get_async_initable_cb, + simple); + } +} + +/** + * g_bus_get_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_bus_get(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_bus_get(). + * + * The returned object is a singleton, that is, shared with other + * callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the + * event that you need a private message bus connection, use + * g_dbus_address_get_for_bus() and + * g_dbus_connection_new_for_address(). + * + * Note that the returned #GDBusConnection object will (usually) have + * the #GDBusConnection:exit-on-close property set to %TRUE. + * + * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + */ +GDBusConnection * +g_bus_get_finish (GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GObject *object; + GDBusConnection *ret; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_bus_get); + + ret = NULL; + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + object = g_simple_async_result_get_op_res_gpointer (simple); + g_assert (object != NULL); + ret = g_object_ref (G_DBUS_CONNECTION (object)); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h new file mode 100644 index 000000000..4eeed48b4 --- /dev/null +++ b/gio/gdbusconnection.h @@ -0,0 +1,467 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_CONNECTION_H__ +#define __G_DBUS_CONNECTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_CONNECTION (g_dbus_connection_get_type ()) +#define G_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_CONNECTION, GDBusConnection)) +#define G_DBUS_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_CONNECTION, GDBusConnectionClass)) +#define G_DBUS_CONNECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_CONNECTION, GDBusConnectionClass)) +#define G_IS_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_CONNECTION)) +#define G_IS_DBUS_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_CONNECTION)) + +typedef struct _GDBusConnectionClass GDBusConnectionClass; +typedef struct _GDBusConnectionPrivate GDBusConnectionPrivate; + +/** + * GDBusConnection: + * + * The #GDBusConnection structure contains only private data and + * should only be accessed using the provided API. + */ +struct _GDBusConnection +{ + /*< private >*/ + GObject parent_instance; + GDBusConnectionPrivate *priv; +}; + +/** + * GDBusConnectionClass: + * @closed: Signal class handler for the #GDBusConnection::closed signal. + * + * Class structure for #GDBusConnection. + */ +struct _GDBusConnectionClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + /* Signals */ + void (*closed) (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error); + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +GType g_dbus_connection_get_type (void) G_GNUC_CONST; + +/* ---------------------------------------------------------------------------------------------------- */ + +void g_bus_get (GBusType bus_type, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusConnection *g_bus_get_finish (GAsyncResult *res, + GError **error); +GDBusConnection *g_bus_get_sync (GBusType bus_type, + GCancellable *cancellable, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ + +void g_dbus_connection_new (GIOStream *stream, + const gchar *guid, + GDBusConnectionFlags flags, + GDBusAuthObserver *auth_observer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusConnection *g_dbus_connection_new_finish (GAsyncResult *res, + GError **error); +GDBusConnection *g_dbus_connection_new_sync (GIOStream *stream, + const gchar *guid, + GDBusConnectionFlags flags, + GDBusAuthObserver *auth_observer, + GCancellable *cancellable, + GError **error); + +void g_dbus_connection_new_for_address (const gchar *address, + GDBusConnectionFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusConnection *g_dbus_connection_new_for_address_finish (GAsyncResult *res, + GError **error); +GDBusConnection *g_dbus_connection_new_for_address_sync (const gchar *address, + GDBusConnectionFlags flags, + GCancellable *cancellable, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean g_dbus_connection_is_closed (GDBusConnection *connection); +void g_dbus_connection_close (GDBusConnection *connection); +GIOStream *g_dbus_connection_get_stream (GDBusConnection *connection); +const gchar *g_dbus_connection_get_guid (GDBusConnection *connection); +const gchar *g_dbus_connection_get_unique_name (GDBusConnection *connection); +GCredentials *g_dbus_connection_get_peer_credentials (GDBusConnection *connection); +gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection); +void g_dbus_connection_set_exit_on_close (GDBusConnection *connection, + gboolean exit_on_close); +GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection); +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean g_dbus_connection_send_message (GDBusConnection *connection, + GDBusMessage *message, + volatile guint32 *out_serial, + GError **error); +void g_dbus_connection_send_message_with_reply (GDBusConnection *connection, + GDBusMessage *message, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusMessage *g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error); +GDBusMessage *g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, + GDBusMessage *message, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean g_dbus_connection_emit_signal (GDBusConnection *connection, + const gchar *destination_bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + GError **error); +void g_dbus_connection_invoke_method (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GVariant *g_dbus_connection_invoke_method_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error); +GVariant *g_dbus_connection_invoke_method_sync (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ + + +/** + * GDBusInterfaceMethodCallFunc: + * @connection: A #GDBusConnection. + * @sender: The unique bus name of the remote caller. + * @object_path: The object path that the method was invoked on. + * @interface_name: The D-Bus interface name the method was invoked on. + * @method_name: The name of the method that was invoked. + * @parameters: A #GVariant tuple with parameters. + * @invocation: A #GDBusMethodInvocation object that can be used to return a value or error. + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object(). + * + * The type of the @method_call function in #GDBusInterfaceVTable. + */ +typedef void (*GDBusInterfaceMethodCallFunc) (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data); + +/** + * GDBusInterfaceGetPropertyFunc: + * @connection: A #GDBusConnection. + * @sender: The unique bus name of the remote caller. + * @object_path: The object path that the method was invoked on. + * @interface_name: The D-Bus interface name for the property. + * @property_name: The name of the property to get the value of. + * @error: Return location for error. + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object(). + * + * The type of the @get_property function in #GDBusInterfaceVTable. + * + * Returns: A newly-allocated #GVariant with the value for @property_name or %NULL if @error is set. + */ +typedef GVariant *(*GDBusInterfaceGetPropertyFunc) (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data); + +/** + * GDBusInterfaceSetPropertyFunc: + * @connection: A #GDBusConnection. + * @sender: The unique bus name of the remote caller. + * @object_path: The object path that the method was invoked on. + * @interface_name: The D-Bus interface name for the property. + * @property_name: The name of the property to get the value of. + * @value: The value to set the property to. + * @error: Return location for error. + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object(). + * + * The type of the @set_property function in #GDBusInterfaceVTable. + * + * Returns: %TRUE if the property was set to @value, %FALSE if @error is set. + */ +typedef gboolean (*GDBusInterfaceSetPropertyFunc) (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data); + +/** + * GDBusInterfaceVTable: + * @method_call: Function for handling incoming method calls. + * @get_property: Function for getting a property. + * @set_property: Function for setting a property. + * + * Virtual table for handling properties and method calls for a D-Bus + * interface. + * + * If you want to handle getting/setting D-Bus properties asynchronously, simply + * register an object with the org.freedesktop.DBus.Properties + * D-Bus interface using g_dbus_connection_register_object(). + */ +struct _GDBusInterfaceVTable +{ + GDBusInterfaceMethodCallFunc method_call; + GDBusInterfaceGetPropertyFunc get_property; + GDBusInterfaceSetPropertyFunc set_property; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +guint g_dbus_connection_register_object (GDBusConnection *connection, + const gchar *object_path, + const gchar *interface_name, + const GDBusInterfaceInfo *introspection_data, + const GDBusInterfaceVTable *vtable, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error); +gboolean g_dbus_connection_unregister_object (GDBusConnection *connection, + guint registration_id); + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * GDBusSubtreeEnumerateFunc: + * @connection: A #GDBusConnection. + * @sender: The unique bus name of the remote caller. + * @object_path: The object path that was registered with g_dbus_connection_register_subtree(). + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree(). + * + * The type of the @enumerate function in #GDBusSubtreeVTable. + * + * Returns: A newly allocated array of strings for node names that are children of @object_path. + */ +typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data); + +/** + * GDBusSubtreeIntrospectFunc: + * @connection: A #GDBusConnection. + * @sender: The unique bus name of the remote caller. + * @object_path: The object path that was registered with g_dbus_connection_register_subtree(). + * @node: A node that is a child of @object_path (relative to @object_path) or / for the root of the subtree. + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree(). + * + * The type of the @introspect function in #GDBusSubtreeVTable. + * + * Returns: A newly-allocated #GPtrArray with pointers to #GDBusInterfaceInfo describing + * the interfaces implemented by @node. + */ +typedef GPtrArray *(*GDBusSubtreeIntrospectFunc) (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data); + +/** + * GDBusSubtreeDispatchFunc: + * @connection: A #GDBusConnection. + * @sender: The unique bus name of the remote caller. + * @object_path: The object path that was registered with g_dbus_connection_register_subtree(). + * @interface_name: The D-Bus interface name that the method call or property access is for. + * @node: A node that is a child of @object_path (relative to @object_path) or / for the root of the subtree. + * @out_user_data: Return location for user data to pass to functions in the returned #GDBusInterfaceVTable (never %NULL). + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree(). + * + * The type of the @dispatch function in #GDBusSubtreeVTable. + * + * Returns: A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods. + */ +typedef const GDBusInterfaceVTable * (*GDBusSubtreeDispatchFunc) (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data); + +/** + * GDBusSubtreeVTable: + * @enumerate: Function for enumerating child nodes. + * @introspect: Function for introspecting a child node. + * @dispatch: Function for dispatching a remote call on a child node. + * + * Virtual table for handling subtrees registered with g_dbus_connection_register_subtree(). + */ +struct _GDBusSubtreeVTable +{ + GDBusSubtreeEnumerateFunc enumerate; + GDBusSubtreeIntrospectFunc introspect; + GDBusSubtreeDispatchFunc dispatch; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +guint g_dbus_connection_register_subtree (GDBusConnection *connection, + const gchar *object_path, + const GDBusSubtreeVTable *vtable, + GDBusSubtreeFlags flags, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error); +gboolean g_dbus_connection_unregister_subtree (GDBusConnection *connection, + guint registration_id); + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * GDBusSignalCallback: + * @connection: A #GDBusConnection. + * @sender_name: The unique bus name of the sender of the signal. + * @object_path: The object path that the signal was emitted on. + * @interface_name: The name of the signal. + * @signal_name: The name of the signal. + * @parameters: A #GVariant tuple with parameters for the signal. + * @user_data: User data passed when subscribing to the signal. + * + * Signature for callback function used in g_dbus_connection_signal_subscribe(). + */ +typedef void (*GDBusSignalCallback) (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + +guint g_dbus_connection_signal_subscribe (GDBusConnection *connection, + const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0, + GDBusSignalCallback callback, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, + guint subscription_id); + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * GDBusMessageFilterFunction: + * @connection: A #GDBusConnection. + * @message: A #GDBusMessage. + * @user_data: User data passed when adding the filter. + * + * Signature for function used in g_dbus_connection_add_filter(). + * + * Returns: %TRUE if the filter handled @message, %FALSE to let other + * handlers run. + */ +typedef gboolean (*GDBusMessageFilterFunction) (GDBusConnection *connection, + GDBusMessage *message, + gpointer user_data); + +guint g_dbus_connection_add_filter (GDBusConnection *connection, + GDBusMessageFilterFunction filter_function, + gpointer user_data, + GDestroyNotify user_data_free_func); + +void g_dbus_connection_remove_filter (GDBusConnection *connection, + guint filter_id); + +/* ---------------------------------------------------------------------------------------------------- */ + + +G_END_DECLS + +#endif /* __G_DBUS_CONNECTION_H__ */ diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c new file mode 100644 index 000000000..3b0e080fc --- /dev/null +++ b/gio/gdbuserror.c @@ -0,0 +1,847 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include + +#include "gdbuserror.h" +#include "gioenums.h" +#include "gioenumtypes.h" +#include "gioerror.h" +#include "gdbusprivate.h" + +/** + * SECTION:gdbuserror + * @title: GDBusError + * @short_description: Mapping D-Bus errors to and from #GError + * @include: gdbus/gdbus.h + * + * All facilities that return errors from remote methods (such as + * g_dbus_connection_invoke_method_sync()) use #GError to represent + * both D-Bus errors (e.g. errors returned from the other peer) and + * locally in-process generated errors. + * + * To check if a returned #GError is an error from a remote peer, use + * g_dbus_error_is_remote_error(). To get the actual D-Bus error name, + * use g_dbus_error_get_remote_error(). Before presenting an error, + * always use g_dbus_error_strip_remote_error(). + * + * In addition, facilities used to return errors to a remote peer also + * use #GError. See g_dbus_method_invocation_return_error() for + * discussion about how the D-Bus error name is set. + * + * Applications can associate a #GError error domain with a set of D-Bus errors in order to + * automatically map from D-Bus errors to #GError and back. This + * is typically done in the function returning the #GQuark for the + * error domain: + * Error Registration + * /* foo-bar-error.h: */ + * + * #define FOO_BAR_ERROR (foo_bar_error_quark ()) + * GQuark foo_bar_error_quark (void); + * + * typedef enum + * { + * FOO_BAR_ERROR_FAILED, + * FOO_BAR_ERROR_ANOTHER_ERROR, + * FOO_BAR_ERROR_SOME_THIRD_ERROR, + * } FooBarError; + * + * /* foo-bar-error.c: */ + * + * static const GDBusErrorEntry foo_bar_error_entries[] = + * { + * {FOO_BAR_ERROR_FAILED, "org.project.Foo.Bar.Error.Failed"}, + * {FOO_BAR_ERROR_ANOTHER_ERROR, "org.project.Foo.Bar.Error.AnotherError"}, + * {FOO_BAR_ERROR_SOME_THIRD_ERROR, "org.project.Foo.Bar.Error.SomeThirdError"}, + * }; + * + * GQuark + * foo_bar_error_quark (void) + * { + * static volatile gsize quark_volatile = 0; + * g_dbus_error_register_error_domain ("foo-bar-error-quark", + * &quark_volatile, + * foo_bar_error_entries, + * G_N_ELEMENTS (foo_bar_error_entries)); + * G_STATIC_ASSERT (G_N_ELEMENTS (foo_bar_error_entries) - 1 == FOO_BAR_ERROR_SOME_THIRD_ERROR); + * return (GQuark) quark_volatile; + * } + * + * With this setup, a D-Bus peer can transparently pass e.g. %FOO_BAR_ERROR_ANOTHER_ERROR and + * other peers will see the D-Bus error name org.project.Foo.Bar.Error.AnotherError. + * If the other peer is using GDBus, the peer will see also %FOO_BAR_ERROR_ANOTHER_ERROR instead + * of %G_IO_ERROR_DBUS_ERROR. Note that GDBus clients can still recover + * org.project.Foo.Bar.Error.AnotherError using g_dbus_error_get_remote_error(). + * + * Note that errors in the %G_DBUS_ERROR error domain is intended only + * for returning errors from a remote message bus process. Errors + * generated locally in-process by e.g. #GDBusConnection is from the + * %G_IO_ERROR domain. + */ + +static const GDBusErrorEntry g_dbus_error_entries[] = +{ + {G_DBUS_ERROR_FAILED, "org.freedesktop.DBus.Error.Failed"}, + {G_DBUS_ERROR_NO_MEMORY, "org.freedesktop.DBus.Error.NoMemory"}, + {G_DBUS_ERROR_SERVICE_UNKNOWN, "org.freedesktop.DBus.Error.ServiceUnknown"}, + {G_DBUS_ERROR_NAME_HAS_NO_OWNER, "org.freedesktop.DBus.Error.NameHasNoOwner"}, + {G_DBUS_ERROR_NO_REPLY, "org.freedesktop.DBus.Error.NoReply"}, + {G_DBUS_ERROR_IO_ERROR, "org.freedesktop.DBus.Error.IOError"}, + {G_DBUS_ERROR_BAD_ADDRESS, "org.freedesktop.DBus.Error.BadAddress"}, + {G_DBUS_ERROR_NOT_SUPPORTED, "org.freedesktop.DBus.Error.NotSupported"}, + {G_DBUS_ERROR_LIMITS_EXCEEDED, "org.freedesktop.DBus.Error.LimitsExceeded"}, + {G_DBUS_ERROR_ACCESS_DENIED, "org.freedesktop.DBus.Error.AccessDenied"}, + {G_DBUS_ERROR_AUTH_FAILED, "org.freedesktop.DBus.Error.AuthFailed"}, + {G_DBUS_ERROR_NO_SERVER, "org.freedesktop.DBus.Error.NoServer"}, + {G_DBUS_ERROR_TIMEOUT, "org.freedesktop.DBus.Error.Timeout"}, + {G_DBUS_ERROR_NO_NETWORK, "org.freedesktop.DBus.Error.NoNetwork"}, + {G_DBUS_ERROR_ADDRESS_IN_USE, "org.freedesktop.DBus.Error.AddressInUse"}, + {G_DBUS_ERROR_DISCONNECTED, "org.freedesktop.DBus.Error.Disconnected"}, + {G_DBUS_ERROR_INVALID_ARGS, "org.freedesktop.DBus.Error.InvalidArgs"}, + {G_DBUS_ERROR_FILE_NOT_FOUND, "org.freedesktop.DBus.Error.FileNotFound"}, + {G_DBUS_ERROR_FILE_EXISTS, "org.freedesktop.DBus.Error.FileExists"}, + {G_DBUS_ERROR_UNKNOWN_METHOD, "org.freedesktop.DBus.Error.UnknownMethod"}, + {G_DBUS_ERROR_TIMED_OUT, "org.freedesktop.DBus.Error.TimedOut"}, + {G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "org.freedesktop.DBus.Error.MatchRuleNotFound"}, + {G_DBUS_ERROR_MATCH_RULE_INVALID, "org.freedesktop.DBus.Error.MatchRuleInvalid"}, + {G_DBUS_ERROR_SPAWN_EXEC_FAILED, "org.freedesktop.DBus.Error.Spawn.ExecFailed"}, + {G_DBUS_ERROR_SPAWN_FORK_FAILED, "org.freedesktop.DBus.Error.Spawn.ForkFailed"}, + {G_DBUS_ERROR_SPAWN_CHILD_EXITED, "org.freedesktop.DBus.Error.Spawn.ChildExited"}, + {G_DBUS_ERROR_SPAWN_CHILD_SIGNALED, "org.freedesktop.DBus.Error.Spawn.ChildSignaled"}, + {G_DBUS_ERROR_SPAWN_FAILED, "org.freedesktop.DBus.Error.Spawn.Failed"}, + {G_DBUS_ERROR_SPAWN_SETUP_FAILED, "org.freedesktop.DBus.Error.Spawn.FailedToSetup"}, + {G_DBUS_ERROR_SPAWN_CONFIG_INVALID, "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"}, + {G_DBUS_ERROR_SPAWN_SERVICE_INVALID, "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"}, + {G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND, "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"}, + {G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID, "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"}, + {G_DBUS_ERROR_SPAWN_FILE_INVALID, "org.freedesktop.DBus.Error.Spawn.FileInvalid"}, + {G_DBUS_ERROR_SPAWN_NO_MEMORY, "org.freedesktop.DBus.Error.Spawn.NoMemory"}, + {G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "org.freedesktop.DBus.Error.UnixProcessIdUnknown"}, + {G_DBUS_ERROR_INVALID_SIGNATURE, "org.freedesktop.DBus.Error.InvalidSignature"}, + {G_DBUS_ERROR_INVALID_FILE_CONTENT, "org.freedesktop.DBus.Error.InvalidFileContent"}, + {G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"}, + {G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN, "org.freedesktop.DBus.Error.AdtAuditDataUnknown"}, + {G_DBUS_ERROR_OBJECT_PATH_IN_USE, "org.freedesktop.DBus.Error.ObjectPathInUse"}, +}; + +GQuark +g_dbus_error_quark (void) +{ + static volatile gsize quark_volatile = 0; + g_dbus_error_register_error_domain ("g-dbus-error-quark", + &quark_volatile, + g_dbus_error_entries, + G_N_ELEMENTS (g_dbus_error_entries)); + G_STATIC_ASSERT (G_N_ELEMENTS (g_dbus_error_entries) - 1 == G_DBUS_ERROR_OBJECT_PATH_IN_USE); + return (GQuark) quark_volatile; +} + +/** + * g_dbus_error_register_error_domain: + * @error_domain_quark_name: The error domain name. + * @quark_volatile: A pointer where to store the #GQuark. + * @entries: A pointer to @num_entries #GDBusErrorEntry struct items. + * @num_entries: Number of items to register. + * + * Helper function for associating a #GError error domain with D-Bus error names. + */ +void +g_dbus_error_register_error_domain (const gchar *error_domain_quark_name, + volatile gsize *quark_volatile, + const GDBusErrorEntry *entries, + guint num_entries) +{ + g_return_if_fail (error_domain_quark_name != NULL); + g_return_if_fail (quark_volatile != NULL); + g_return_if_fail (entries != NULL); + g_return_if_fail (num_entries > 0); + + if (g_once_init_enter (quark_volatile)) + { + guint n; + GQuark quark; + + quark = g_quark_from_static_string (error_domain_quark_name); + + for (n = 0; n < num_entries; n++) + { + g_warn_if_fail (g_dbus_error_register_error (quark, + entries[n].error_code, + entries[n].dbus_error_name)); + } + g_once_init_leave (quark_volatile, quark); + } +} + +static gboolean +_g_dbus_error_decode_gerror (const gchar *dbus_name, + GQuark *out_error_domain, + gint *out_error_code) +{ + gboolean ret; + guint n; + GString *s; + gchar *domain_quark_string; + + ret = FALSE; + s = NULL; + + if (g_str_has_prefix (dbus_name, "org.gtk.GDBus.UnmappedGError.Quark._")) + { + s = g_string_new (NULL); + + for (n = sizeof "org.gtk.GDBus.UnmappedGError.Quark._" - 1; + dbus_name[n] != '.' && dbus_name[n] != '\0'; + n++) + { + if (g_ascii_isalnum (dbus_name[n])) + { + g_string_append_c (s, dbus_name[n]); + } + else if (dbus_name[n] == '_') + { + guint nibble_top; + guint nibble_bottom; + + n++; + + nibble_top = dbus_name[n]; + if (nibble_top >= '0' && nibble_top <= '9') + nibble_top -= '0'; + else if (nibble_top >= 'a' && nibble_top <= 'f') + nibble_top -= ('a' - 10); + else + goto not_mapped; + + n++; + + nibble_bottom = dbus_name[n]; + if (nibble_bottom >= '0' && nibble_bottom <= '9') + nibble_bottom -= '0'; + else if (nibble_bottom >= 'a' && nibble_bottom <= 'f') + nibble_bottom -= ('a' - 10); + else + goto not_mapped; + + g_string_append_c (s, (nibble_top<<4) | nibble_bottom); + } + else + { + goto not_mapped; + } + } + + if (!g_str_has_prefix (dbus_name + n, ".Code")) + goto not_mapped; + + domain_quark_string = g_string_free (s, FALSE); + s = NULL; + + if (out_error_domain != NULL) + *out_error_domain = g_quark_from_string (domain_quark_string); + g_free (domain_quark_string); + + if (out_error_code != NULL) + *out_error_code = atoi (dbus_name + n + sizeof ".Code" - 1); + + ret = TRUE; + } + + not_mapped: + + if (s != NULL) + g_string_free (s, TRUE); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GQuark error_domain; + gint error_code; +} QuarkCodePair; + +static guint +quark_code_pair_hash_func (const QuarkCodePair *pair) +{ + gint val; + val = pair->error_domain + pair->error_code; + return g_int_hash (&val); +} + +static gboolean +quark_code_pair_equal_func (const QuarkCodePair *a, + const QuarkCodePair *b) +{ + return (a->error_domain == b->error_domain) && (a->error_code == b->error_code); +} + +typedef struct +{ + QuarkCodePair pair; + gchar *dbus_error_name; +} RegisteredError; + +static void +registered_error_free (RegisteredError *re) +{ + g_free (re->dbus_error_name); + g_free (re); +} + +G_LOCK_DEFINE_STATIC (error_lock); + +/* maps from QuarkCodePair* -> RegisteredError* */ +static GHashTable *quark_code_pair_to_re = NULL; + +/* maps from gchar* -> RegisteredError* */ +static GHashTable *dbus_error_name_to_re = NULL; + +/** + * g_dbus_error_register_error: + * @error_domain: A #GQuark for a error domain. + * @error_code: An error code. + * @dbus_error_name: A D-Bus error name. + * + * Creates an association to map between @dbus_error_name and + * #GErrors specified by @error_domain and @error_code. + * + * This is typically done in the routine that returns the #GQuark for + * an error domain. + * + * Returns: %TRUE if the association was created, %FALSE if it already + * exists. + */ +gboolean +g_dbus_error_register_error (GQuark error_domain, + gint error_code, + const gchar *dbus_error_name) +{ + gboolean ret; + QuarkCodePair pair; + RegisteredError *re; + + g_return_val_if_fail (dbus_error_name != NULL, FALSE); + + ret = FALSE; + + G_LOCK (error_lock); + + if (quark_code_pair_to_re == NULL) + { + g_assert (dbus_error_name_to_re == NULL); /* check invariant */ + quark_code_pair_to_re = g_hash_table_new ((GHashFunc) quark_code_pair_hash_func, + (GEqualFunc) quark_code_pair_equal_func); + dbus_error_name_to_re = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) registered_error_free); + } + + if (g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name) != NULL) + goto out; + + pair.error_domain = error_domain; + pair.error_code = error_code; + + if (g_hash_table_lookup (quark_code_pair_to_re, &pair) != NULL) + goto out; + + re = g_new0 (RegisteredError, 1); + re->pair = pair; + re->dbus_error_name = g_strdup (dbus_error_name); + + g_hash_table_insert (quark_code_pair_to_re, &(re->pair), re); + g_hash_table_insert (dbus_error_name_to_re, re->dbus_error_name, re); + + ret = TRUE; + + out: + G_UNLOCK (error_lock); + return ret; +} + +/** + * g_dbus_error_unregister_error: + * @error_domain: A #GQuark for a error domain. + * @error_code: An error code. + * @dbus_error_name: A D-Bus error name. + * + * Destroys an association previously set up with g_dbus_error_register_error(). + * + * Returns: %TRUE if the association was destroyed, %FALSE if it wasn't found. + */ +gboolean +g_dbus_error_unregister_error (GQuark error_domain, + gint error_code, + const gchar *dbus_error_name) +{ + gboolean ret; + RegisteredError *re; + guint hash_size; + + g_return_val_if_fail (dbus_error_name != NULL, FALSE); + + ret = FALSE; + + G_LOCK (error_lock); + + if (dbus_error_name_to_re == NULL) + { + g_assert (quark_code_pair_to_re == NULL); /* check invariant */ + goto out; + } + + re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name); + if (re == NULL) + { + QuarkCodePair pair; + pair.error_domain = error_domain; + pair.error_code = error_code; + g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &pair) == NULL); /* check invariant */ + goto out; + } + g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &(re->pair)) == re); /* check invariant */ + + g_warn_if_fail (g_hash_table_remove (quark_code_pair_to_re, &(re->pair))); + g_warn_if_fail (g_hash_table_remove (dbus_error_name_to_re, re)); + + /* destroy hashes if empty */ + hash_size = g_hash_table_size (dbus_error_name_to_re); + if (hash_size == 0) + { + g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == 0); /* check invariant */ + + g_hash_table_unref (dbus_error_name_to_re); + dbus_error_name_to_re = NULL; + g_hash_table_unref (quark_code_pair_to_re); + quark_code_pair_to_re = NULL; + } + else + { + g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == hash_size); /* check invariant */ + } + + out: + G_UNLOCK (error_lock); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_error_is_remote_error: + * @error: A #GError. + * + * Checks if @error represents an error received via D-Bus from a remote peer. If so, + * use g_dbus_error_get_remote_error() to get the name of the error. + * + * Returns: %TRUE if @error represents an error from a remote peer, + * %FALSE otherwise. + */ +gboolean +g_dbus_error_is_remote_error (const GError *error) +{ + g_return_val_if_fail (error != NULL, FALSE); + return g_str_has_prefix (error->message, "GDBus.Error:"); +} + + +/** + * g_dbus_error_get_remote_error: + * @error: A #GError. + * + * Gets the D-Bus error name used for @error, if any. + * + * This function is guaranteed to return a D-Bus error name for all #GErrors returned from + * functions handling remote method calls (e.g. g_dbus_connection_invoke_method_finish()) + * unless g_dbus_error_strip_remote_error() has been used on @error. + * + * Returns: An allocated string or %NULL if the D-Bus error name could not be found. Free with g_free(). + */ +gchar * +g_dbus_error_get_remote_error (const GError *error) +{ + RegisteredError *re; + gchar *ret; + + g_return_val_if_fail (error != NULL, NULL); + + /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */ + _g_dbus_initialize (); + + ret = NULL; + + G_LOCK (error_lock); + + re = NULL; + if (quark_code_pair_to_re != NULL) + { + QuarkCodePair pair; + pair.error_domain = error->domain; + pair.error_code = error->code; + g_assert (dbus_error_name_to_re != NULL); /* check invariant */ + re = g_hash_table_lookup (quark_code_pair_to_re, &pair); + } + + if (re != NULL) + { + ret = g_strdup (re->dbus_error_name); + } + else + { + if (g_str_has_prefix (error->message, "GDBus.Error:")) + { + const gchar *begin; + const gchar *end; + begin = error->message + sizeof ("GDBus.Error:") -1; + end = strstr (begin, ":"); + if (end != NULL && end[1] == ' ') + { + ret = g_strndup (begin, end - begin); + } + } + } + + G_UNLOCK (error_lock); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_error_new_for_dbus_error: + * @dbus_error_name: D-Bus error name. + * @dbus_error_message: D-Bus error message. + * + * Creates a #GError based on the contents of @dbus_error_name and + * @dbus_error_message. + * + * Errors registered with g_dbus_error_register_error() will be looked + * up using @dbus_error_name and if a match is found, the error domain + * and code is used. Applications can use g_dbus_error_get_remote_error() + * to recover @dbus_error_name. + * + * If a match against a registered error is not found and the D-Bus + * error name is in a form as returned by g_dbus_error_encode_gerror() + * the error domain and code encoded in the name is used to + * create the #GError. Also, @dbus_error_name is added to the error message + * such that it can be recovered with g_dbus_error_get_remote_error(). + * + * Otherwise, a #GError with the error code %G_IO_ERROR_DBUS_ERROR + * in the #G_IO_ERROR error domain is returned. Also, @dbus_error_name is + * added to the error message such that it can be recovered with + * g_dbus_error_get_remote_error(). + * + * In all three cases, @dbus_error_name can always be recovered from the + * returned #GError using the g_dbus_error_get_remote_error() function + * (unless g_dbus_error_strip_remote_error() hasn't been used on the returned error). + * + * This function is typically only used in object mappings to prepare + * #GError instances for applications. Regular applications should not use + * it. + * + * Returns: An allocated #GError. Free with g_error_free(). + */ +GError * +g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name, + const gchar *dbus_error_message) +{ + GError *error; + RegisteredError *re; + + g_return_val_if_fail (dbus_error_name != NULL, NULL); + g_return_val_if_fail (dbus_error_message != NULL, NULL); + + /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */ + _g_dbus_initialize (); + + G_LOCK (error_lock); + + re = NULL; + if (dbus_error_name_to_re != NULL) + { + g_assert (quark_code_pair_to_re != NULL); /* check invariant */ + re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name); + } + + if (re != NULL) + { + error = g_error_new (re->pair.error_domain, + re->pair.error_code, + "GDBus.Error:%s: %s", + dbus_error_name, + dbus_error_message); + } + else + { + GQuark error_domain = 0; + gint error_code = 0; + + if (_g_dbus_error_decode_gerror (dbus_error_name, + &error_domain, + &error_code)) + { + error = g_error_new (error_domain, + error_code, + "GDBus.Error:%s: %s", + dbus_error_name, + dbus_error_message); + } + else + { + error = g_error_new (G_IO_ERROR, + G_IO_ERROR_DBUS_ERROR, + "GDBus.Error:%s: %s", + dbus_error_name, + dbus_error_message); + } + } + + G_UNLOCK (error_lock); + return error; +} + +/** + * g_dbus_error_set_dbus_error: + * @error: A pointer to a #GError or %NULL. + * @dbus_error_name: D-Bus error name. + * @dbus_error_message: D-Bus error message. + * @format: printf()-style format to prepend to @dbus_error_message or %NULL. + * @...: Arguments for @format. + * + * Does nothing if @error is %NULL. Otherwise sets *@error to + * a new #GError created with g_dbus_error_new_for_dbus_error() + * with @dbus_error_message prepend with @format (unless %NULL). + */ +void +g_dbus_error_set_dbus_error (GError **error, + const gchar *dbus_error_name, + const gchar *dbus_error_message, + const gchar *format, + ...) +{ + g_return_if_fail (error == NULL || *error == NULL); + g_return_if_fail (dbus_error_name != NULL); + g_return_if_fail (dbus_error_message != NULL); + + if (error == NULL) + return; + + if (format == NULL) + { + *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message); + } + else + { + va_list var_args; + va_start (var_args, format); + g_dbus_error_set_dbus_error_valist (error, + dbus_error_name, + dbus_error_message, + format, + var_args); + va_end (var_args); + } +} + +/** + * g_dbus_error_set_dbus_error_valist: + * @error: A pointer to a #GError or %NULL. + * @dbus_error_name: D-Bus error name. + * @dbus_error_message: D-Bus error message. + * @format: printf()-style format to prepend to @dbus_error_message or %NULL. + * @var_args: Arguments for @format. + * + * Like g_dbus_error_set_dbus_error() but intended for language bindings. + */ +void +g_dbus_error_set_dbus_error_valist (GError **error, + const gchar *dbus_error_name, + const gchar *dbus_error_message, + const gchar *format, + va_list var_args) +{ + g_return_if_fail (error == NULL || *error == NULL); + g_return_if_fail (dbus_error_name != NULL); + g_return_if_fail (dbus_error_message != NULL); + + if (error == NULL) + return; + + if (format != NULL) + { + gchar *message; + gchar *s; + message = g_strdup_vprintf (format, var_args); + s = g_strdup_printf ("%s: %s", message, dbus_error_message); + *error = g_dbus_error_new_for_dbus_error (dbus_error_name, s); + g_free (s); + g_free (message); + } + else + { + *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message); + } +} + +/** + * g_dbus_error_strip_remote_error: + * @error: A #GError. + * + * Looks for extra information in the error message used to recover + * the D-Bus error name and strips it if found. If stripped, the + * message field in @error will correspond exactly to what was + * received on the wire. + * + * This is typically used when presenting errors to the end user. + * + * Returns: %TRUE if information was stripped, %FALSE otherwise. + */ +gboolean +g_dbus_error_strip_remote_error (GError *error) +{ + gboolean ret; + + g_return_val_if_fail (error != NULL, FALSE); + + ret = FALSE; + + if (g_str_has_prefix (error->message, "GDBus.Error:")) + { + const gchar *begin; + const gchar *end; + gchar *new_message; + + begin = error->message + sizeof ("GDBus.Error:") -1; + end = strstr (begin, ":"); + if (end != NULL && end[1] == ' ') + { + new_message = g_strdup (end + 2); + g_free (error->message); + error->message = new_message; + ret = TRUE; + } + } + + return ret; +} + +/** + * g_dbus_error_encode_gerror: + * @error: A #GError. + * + * Creates a D-Bus error name to use for @error. If @error matches + * a registered error (cf. g_dbus_error_register_error()), the corresponding + * D-Bus error name will be returned. + * + * Otherwise the a name of the form + * org.gtk.GDBus.UnmappedGError.Quark._ESCAPED_QUARK_NAME.Code_ERROR_CODE + * will be used. This allows other GDBus applications to map the error + * on the wire back to a #GError using g_dbus_error_new_for_dbus_error(). + * + * This function is typically only used in object mappings to put a + * #GError on the wire. Regular applications should not use it. + * + * Returns: A D-Bus error name (never %NULL). Free with g_free(). + */ +gchar * +g_dbus_error_encode_gerror (const GError *error) +{ + RegisteredError *re; + gchar *error_name; + + g_return_val_if_fail (error != NULL, NULL); + + /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */ + _g_dbus_initialize (); + + error_name = NULL; + + G_LOCK (error_lock); + re = NULL; + if (quark_code_pair_to_re != NULL) + { + QuarkCodePair pair; + pair.error_domain = error->domain; + pair.error_code = error->code; + g_assert (dbus_error_name_to_re != NULL); /* check invariant */ + re = g_hash_table_lookup (quark_code_pair_to_re, &pair); + } + if (re != NULL) + { + error_name = g_strdup (re->dbus_error_name); + G_UNLOCK (error_lock); + } + else + { + const gchar *domain_as_string; + GString *s; + guint n; + + G_UNLOCK (error_lock); + + /* We can't make a lot of assumptions about what domain_as_string + * looks like and D-Bus is extremely picky about error names so + * hex-encode it for transport across the wire. + */ + domain_as_string = g_quark_to_string (error->domain); + s = g_string_new ("org.gtk.GDBus.UnmappedGError.Quark._"); + for (n = 0; domain_as_string[n] != 0; n++) + { + gint c = domain_as_string[n]; + if (g_ascii_isalnum (c)) + { + g_string_append_c (s, c); + } + else + { + guint nibble_top; + guint nibble_bottom; + g_string_append_c (s, '_'); + nibble_top = ((int) domain_as_string[n]) >> 4; + nibble_bottom = ((int) domain_as_string[n]) & 0x0f; + if (nibble_top < 10) + nibble_top += '0'; + else + nibble_top += 'a' - 10; + if (nibble_bottom < 10) + nibble_bottom += '0'; + else + nibble_bottom += 'a' - 10; + g_string_append_c (s, nibble_top); + g_string_append_c (s, nibble_bottom); + } + } + g_string_append_printf (s, ".Code%d", error->code); + error_name = g_string_free (s, FALSE); + } + + return error_name; +} diff --git a/gio/gdbuserror.h b/gio/gdbuserror.h new file mode 100644 index 000000000..d15724cf5 --- /dev/null +++ b/gio/gdbuserror.h @@ -0,0 +1,92 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_ERROR_H__ +#define __G_DBUS_ERROR_H__ + +#include + +G_BEGIN_DECLS + +/** + * G_DBUS_ERROR: + * + * Error domain for errors generated by a remote message bus. Errors + * in this domain will be from the #GDBusError enumeration. See + * #GError for more information on error domains. + * + * Note that errors in this error domain is intended only for + * returning errors from a remote message bus process. Errors + * generated locally in-process by e.g. #GDBusConnection is from the + * %G_IO_ERROR domain. + */ +#define G_DBUS_ERROR g_dbus_error_quark() + +GQuark g_dbus_error_quark (void); + +/* Used by applications to check, get and strip the D-Bus error name */ +gboolean g_dbus_error_is_remote_error (const GError *error); +gchar *g_dbus_error_get_remote_error (const GError *error); +gboolean g_dbus_error_strip_remote_error (GError *error); + +/** + * GDBusErrorEntry: + * @error_code: An error code. + * @dbus_error_name: The D-Bus error name to associate with @error_code. + * + * Struct used in g_dbus_error_register_error_domain(). + */ +struct _GDBusErrorEntry +{ + gint error_code; + const gchar *dbus_error_name; +}; + +gboolean g_dbus_error_register_error (GQuark error_domain, + gint error_code, + const gchar *dbus_error_name); +gboolean g_dbus_error_unregister_error (GQuark error_domain, + gint error_code, + const gchar *dbus_error_name); +void g_dbus_error_register_error_domain (const gchar *error_domain_quark_name, + volatile gsize *quark_volatile, + const GDBusErrorEntry *entries, + guint num_entries); + +/* Only used by object mappings to map back and forth to GError */ +GError *g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name, + const gchar *dbus_error_message); +void g_dbus_error_set_dbus_error (GError **error, + const gchar *dbus_error_name, + const gchar *dbus_error_message, + const gchar *format, + ...); +void g_dbus_error_set_dbus_error_valist (GError **error, + const gchar *dbus_error_name, + const gchar *dbus_error_message, + const gchar *format, + va_list var_args); +gchar *g_dbus_error_encode_gerror (const GError *error); + +G_END_DECLS + +#endif /* __G_DBUS_ERROR_H__ */ diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c new file mode 100644 index 000000000..63b945e0e --- /dev/null +++ b/gio/gdbusintrospection.c @@ -0,0 +1,2009 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include + +#include "gdbusintrospection.h" + +/** + * SECTION:gdbusintrospection + * @title: Introspection XML + * @short_description: Parse and Generate Introspection XML + * @include: gdbus/gdbus.h + * + * Various data structures and convenience routines to parse and + * generate D-Bus introspection XML. + */ + +/* ---------------------------------------------------------------------------------------------------- */ + +/* See also https://bugzilla.gnome.org/show_bug.cgi?id=449565 ... */ +#define _MY_DEFINE_BOXED_TYPE(TypeName, type_name) \ + GType \ + type_name##_get_type (void) \ + { \ + static volatile gsize type_volatile = 0; \ + if (g_once_init_enter (&type_volatile)) \ + { \ + GType type = g_boxed_type_register_static (g_intern_static_string (#TypeName), \ + (GBoxedCopyFunc) type_name##_ref, \ + (GBoxedFreeFunc) type_name##_unref); \ + g_once_init_leave (&type_volatile, type); \ + } \ + return (GType) type_volatile; \ + } + +_MY_DEFINE_BOXED_TYPE (GDBusNodeInfo, g_dbus_node_info); +_MY_DEFINE_BOXED_TYPE (GDBusInterfaceInfo, g_dbus_interface_info); +_MY_DEFINE_BOXED_TYPE (GDBusMethodInfo, g_dbus_method_info); +_MY_DEFINE_BOXED_TYPE (GDBusPropertyInfo, g_dbus_property_info); +_MY_DEFINE_BOXED_TYPE (GDBusArgInfo, g_dbus_arg_info); +_MY_DEFINE_BOXED_TYPE (GDBusAnnotationInfo, g_dbus_annotation_info); + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + /* stuff we are currently collecting */ + GPtrArray *args; + GPtrArray *out_args; + GPtrArray *methods; + GPtrArray *signals; + GPtrArray *properties; + GPtrArray *interfaces; + GPtrArray *nodes; + GPtrArray *annotations; + + /* A list of GPtrArray's containing annotations */ + GSList *annotations_stack; + + /* A list of GPtrArray's containing interfaces */ + GSList *interfaces_stack; + + /* A list of GPtrArray's containing nodes */ + GSList *nodes_stack; + + /* Whether the direction was "in" for last parsed arg */ + gboolean last_arg_was_in; + + /* Number of args currently being collected; used for assigning + * names to args without a "name" attribute + */ + guint num_args; + +} ParseData; + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_node_info_ref: + * @info: A #GDBusNodeInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusNodeInfo * +g_dbus_node_info_ref (GDBusNodeInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/** + * g_dbus_interface_info_ref: + * @info: A #GDBusInterfaceInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusInterfaceInfo * +g_dbus_interface_info_ref (GDBusInterfaceInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/** + * g_dbus_method_info_ref: + * @info: A #GDBusMethodInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusMethodInfo * +g_dbus_method_info_ref (GDBusMethodInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/** + * g_dbus_signal_info_ref: + * @info: A #GDBusSignalInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusSignalInfo * +g_dbus_signal_info_ref (GDBusSignalInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/** + * g_dbus_property_info_ref: + * @info: A #GDBusPropertyInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusPropertyInfo * +g_dbus_property_info_ref (GDBusPropertyInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/** + * g_dbus_arg_info_ref: + * @info: A #GDBusArgInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusArgInfo * +g_dbus_arg_info_ref (GDBusArgInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/** + * g_dbus_node_info_ref: + * @info: A #GDBusNodeInfo + * + * If @info is statically allocated does nothing. Otherwise increases + * the reference count. + * + * Returns: The same @info. + */ +GDBusAnnotationInfo * +g_dbus_annotation_info_ref (GDBusAnnotationInfo *info) +{ + if (info->ref_count == -1) + return info; + g_atomic_int_inc (&info->ref_count); + return info; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +free_null_terminated_array (gpointer array, GDestroyNotify unref_func) +{ + guint n; + gpointer *p = array; + if (p == NULL) + return; + for (n = 0; p[n] != NULL; n++) + unref_func (p[n]); + g_free (p); +} + +/** + * g_dbus_annotation_info_unref: + * @info: A #GDBusAnnotationInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_annotation_info_unref (GDBusAnnotationInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->key); + g_free (info->value); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/** + * g_dbus_arg_info_unref: + * @info: A #GDBusArgInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_arg_info_unref (GDBusArgInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->name); + g_free (info->signature); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/** + * g_dbus_method_info_unref: + * @info: A #GDBusMethodInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_method_info_unref (GDBusMethodInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->name); + free_null_terminated_array (info->in_args, (GDestroyNotify) g_dbus_arg_info_unref); + free_null_terminated_array (info->out_args, (GDestroyNotify) g_dbus_arg_info_unref); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/** + * g_dbus_signal_info_unref: + * @info: A #GDBusSignalInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_signal_info_unref (GDBusSignalInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->name); + free_null_terminated_array (info->args, (GDestroyNotify) g_dbus_arg_info_unref); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/** + * g_dbus_property_info_unref: + * @info: A #GDBusPropertyInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_property_info_unref (GDBusPropertyInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->name); + g_free (info->signature); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/** + * g_dbus_interface_info_unref: + * @info: A #GDBusInterfaceInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_interface_info_unref (GDBusInterfaceInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->name); + free_null_terminated_array (info->methods, (GDestroyNotify) g_dbus_method_info_unref); + free_null_terminated_array (info->signals, (GDestroyNotify) g_dbus_signal_info_unref); + free_null_terminated_array (info->properties, (GDestroyNotify) g_dbus_property_info_unref); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/** + * g_dbus_node_info_unref: + * @info: A #GDBusNodeInfo. + * + * If @info is statically allocated, does nothing. Otherwise decreases + * the reference count of @info. When its reference count drops to 0, + * the memory used is freed. + */ +void +g_dbus_node_info_unref (GDBusNodeInfo *info) +{ + if (info->ref_count == -1) + return; + if (g_atomic_int_dec_and_test (&info->ref_count)) + { + g_free (info->path); + free_null_terminated_array (info->interfaces, (GDestroyNotify) g_dbus_interface_info_unref); + free_null_terminated_array (info->nodes, (GDestroyNotify) g_dbus_node_info_unref); + free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref); + g_free (info); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_dbus_annotation_info_set (ParseData *data, + GDBusAnnotationInfo *info, + const gchar *key, + const gchar *value, + GDBusAnnotationInfo **embedded_annotations) +{ + info->ref_count = 1; + + if (key != NULL) + info->key = g_strdup (key); + + if (value != NULL) + info->value = g_strdup (value); + + if (embedded_annotations != NULL) + info->annotations = embedded_annotations; +} + +static void +g_dbus_arg_info_set (ParseData *data, + GDBusArgInfo *info, + const gchar *name, + const gchar *signature, + GDBusAnnotationInfo **annotations) +{ + info->ref_count = 1; + + /* name may be NULL - TODO: compute name? */ + if (name != NULL) + info->name = g_strdup (name); + + if (signature != NULL) + info->signature = g_strdup (signature); + + if (annotations != NULL) + info->annotations = annotations; +} + +static void +g_dbus_method_info_set (ParseData *data, + GDBusMethodInfo *info, + const gchar *name, + guint in_num_args, + GDBusArgInfo **in_args, + guint out_num_args, + GDBusArgInfo **out_args, + GDBusAnnotationInfo **annotations) +{ + info->ref_count = 1; + + if (name != NULL) + info->name = g_strdup (name); + + if (in_num_args != 0) + { + //info->in_num_args = in_num_args; + info->in_args = in_args; + } + + if (out_num_args != 0) + { + //info->out_num_args = out_num_args; + info->out_args = out_args; + } + + if (annotations != NULL) + info->annotations = annotations; +} + +static void +g_dbus_signal_info_set (ParseData *data, + GDBusSignalInfo *info, + const gchar *name, + guint num_args, + GDBusArgInfo **args, + GDBusAnnotationInfo **annotations) +{ + info->ref_count = 1; + + if (name != NULL) + info->name = g_strdup (name); + + if (num_args != 0) + { + //info->num_args = num_args; + info->args = args; + } + + if (annotations != NULL) + { + info->annotations = annotations; + } +} + +static void +g_dbus_property_info_set (ParseData *data, + GDBusPropertyInfo *info, + const gchar *name, + const gchar *signature, + GDBusPropertyInfoFlags flags, + GDBusAnnotationInfo **annotations) +{ + info->ref_count = 1; + + if (name != NULL) + info->name = g_strdup (name); + + if (flags != G_DBUS_PROPERTY_INFO_FLAGS_NONE) + info->flags = flags; + + if (signature != NULL) + { + info->signature = g_strdup (signature); + } + + if (annotations != NULL) + { + info->annotations = annotations; + } +} + +static void +g_dbus_interface_info_set (ParseData *data, + GDBusInterfaceInfo *info, + const gchar *name, + guint num_methods, + GDBusMethodInfo **methods, + guint num_signals, + GDBusSignalInfo **signals, + guint num_properties, + GDBusPropertyInfo **properties, + GDBusAnnotationInfo **annotations) +{ + info->ref_count = 1; + + if (name != NULL) + { + info->name = g_strdup (name); + } + + if (num_methods != 0) + { + //info->num_methods = num_methods; + info->methods = methods; + } + + if (num_signals != 0) + { + //info->num_signals = num_signals; + info->signals = signals; + } + + if (num_properties != 0) + { + //info->num_properties = num_properties; + info->properties = properties; + } + + if (annotations != NULL) + { + info->annotations = annotations; + } +} + +static void +g_dbus_node_info_set (ParseData *data, + GDBusNodeInfo *info, + const gchar *path, + guint num_interfaces, + GDBusInterfaceInfo **interfaces, + guint num_nodes, + GDBusNodeInfo **nodes, + GDBusAnnotationInfo **annotations) +{ + info->ref_count = 1; + + if (path != NULL) + { + info->path = g_strdup (path); + /* TODO: relative / absolute path snafu */ + } + + if (num_interfaces != 0) + { + //info->num_interfaces = num_interfaces; + info->interfaces = interfaces; + } + + if (num_nodes != 0) + { + //info->num_nodes = num_nodes; + info->nodes = nodes; + } + + if (annotations != NULL) + { + info->annotations = annotations; + } + +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_dbus_annotation_info_generate_xml (const GDBusAnnotationInfo *info, + guint indent, + GString *string_builder) +{ + guint n; + + g_string_append_printf (string_builder, "%*skey, + info->value); + + if (info->annotations == NULL) + { + g_string_append (string_builder, "/>\n"); + } + else + { + g_string_append (string_builder, ">\n"); + + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], + indent + 2, + string_builder); + + g_string_append_printf (string_builder, "%*s\n", + indent, ""); + } + +} + +static void +g_dbus_arg_info_generate_xml (const GDBusArgInfo *info, + guint indent, + const gchar *extra_attributes, + GString *string_builder) +{ + guint n; + + g_string_append_printf (string_builder, "%*ssignature); + + if (info->name != NULL) + g_string_append_printf (string_builder, " name=\"%s\"", info->name); + + if (extra_attributes != NULL) + g_string_append_printf (string_builder, " %s", extra_attributes); + + if (info->annotations == NULL) + { + g_string_append (string_builder, "/>\n"); + } + else + { + g_string_append (string_builder, ">\n"); + + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], + indent + 2, + string_builder); + + g_string_append_printf (string_builder, "%*s\n", indent, ""); + } + +} + +static void +g_dbus_method_info_generate_xml (const GDBusMethodInfo *info, + guint indent, + GString *string_builder) +{ + guint n; + + g_string_append_printf (string_builder, "%*sname); + + if (info->annotations == NULL && info->in_args == NULL && info->out_args == NULL) + { + g_string_append (string_builder, "/>\n"); + } + else + { + g_string_append (string_builder, ">\n"); + + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], + indent + 2, + string_builder); + + for (n = 0; info->in_args != NULL && info->in_args[n] != NULL; n++) + g_dbus_arg_info_generate_xml (info->in_args[n], + indent + 2, + "direction=\"in\"", + string_builder); + + for (n = 0; info->out_args != NULL && info->out_args[n] != NULL; n++) + g_dbus_arg_info_generate_xml (info->out_args[n], + indent + 2, + "direction=\"out\"", + string_builder); + + g_string_append_printf (string_builder, "%*s\n", indent, ""); + } +} + +static void +g_dbus_signal_info_generate_xml (const GDBusSignalInfo *info, + guint indent, + GString *string_builder) +{ + guint n; + + g_string_append_printf (string_builder, "%*sname); + + if (info->annotations == NULL && info->args == NULL) + { + g_string_append (string_builder, "/>\n"); + } + else + { + g_string_append (string_builder, ">\n"); + + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], + indent + 2, + string_builder); + + for (n = 0; info->args != NULL && info->args[n] != NULL; n++) + g_dbus_arg_info_generate_xml (info->args[n], + indent + 2, + NULL, + string_builder); + + g_string_append_printf (string_builder, "%*s\n", indent, ""); + } +} + +static void +g_dbus_property_info_generate_xml (const GDBusPropertyInfo *info, + guint indent, + GString *string_builder) +{ + guint n; + const gchar *access_string; + + if ((info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) && + (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) + { + access_string = "readwrite"; + } + else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + access_string = "read"; + } + else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE) + { + access_string = "write"; + } + else + { + g_assert_not_reached (); + } + + g_string_append_printf (string_builder, "%*ssignature, + info->name, + access_string); + + if (info->annotations == NULL) + { + g_string_append (string_builder, "/>\n"); + } + else + { + g_string_append (string_builder, ">\n"); + + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], + indent + 2, + string_builder); + + g_string_append_printf (string_builder, "%*s\n", indent, ""); + } + +} + +/** + * g_dbus_interface_info_generate_xml: + * @info: A #GDBusNodeInfo + * @indent: Indentation level. + * @string_builder: A #GString to to append XML data to. + * + * Appends an XML representation of @info (and its children) to @string_builder. + * + * This function is typically used for generating introspection XML + * documents at run-time for handling the + * org.freedesktop.DBus.Introspectable.Introspect + * method. + */ +void +g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info, + guint indent, + GString *string_builder) +{ + guint n; + + g_string_append_printf (string_builder, "%*s\n", + indent, "", + info->name); + + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], + indent + 2, + string_builder); + + for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++) + g_dbus_method_info_generate_xml (info->methods[n], + indent + 2, + string_builder); + + for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++) + g_dbus_signal_info_generate_xml (info->signals[n], + indent + 2, + string_builder); + + for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++) + g_dbus_property_info_generate_xml (info->properties[n], + indent + 2, + string_builder); + + g_string_append_printf (string_builder, "%*s\n", indent, ""); +} + +/** + * g_dbus_node_info_generate_xml: + * @node_info: A #GDBusNodeInfo. + * @indent: Indentation level. + * @string_builder: A #GString to to append XML data to. + * + * Appends an XML representation of @node_info (and its children) to @string_builder. + * + * This function is typically used for generating introspection XML documents at run-time for + * handling the org.freedesktop.DBus.Introspectable.Introspect method. + */ +void +g_dbus_node_info_generate_xml (const GDBusNodeInfo *node_info, + guint indent, + GString *string_builder) +{ + guint n; + + g_string_append_printf (string_builder, "%*spath != NULL) + g_string_append_printf (string_builder, " name=\"%s\"", node_info->path); + + if (node_info->interfaces == NULL && node_info->nodes == NULL && node_info->annotations == NULL) + { + g_string_append (string_builder, "/>\n"); + } + else + { + g_string_append (string_builder, ">\n"); + + for (n = 0; node_info->annotations != NULL && node_info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (node_info->annotations[n], + indent + 2, + string_builder); + + for (n = 0; node_info->interfaces != NULL && node_info->interfaces[n] != NULL; n++) + g_dbus_interface_info_generate_xml (node_info->interfaces[n], + indent + 2, + string_builder); + + for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++) + g_dbus_node_info_generate_xml (node_info->nodes[n], + indent + 2, + string_builder); + + g_string_append_printf (string_builder, "%*s\n", indent, ""); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAnnotationInfo ** +parse_data_steal_annotations (ParseData *data, guint *out_num_elements) +{ + GDBusAnnotationInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->annotations->len; + if (data->annotations == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->annotations, NULL); + ret = (GDBusAnnotationInfo **) g_ptr_array_free (data->annotations, FALSE); + } + data->annotations = g_ptr_array_new (); + return ret; +} + +static GDBusArgInfo ** +parse_data_steal_args (ParseData *data, guint *out_num_elements) +{ + GDBusArgInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->args->len; + if (data->args == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->args, NULL); + ret = (GDBusArgInfo **) g_ptr_array_free (data->args, FALSE); + } + data->args = g_ptr_array_new (); + return ret; +} + +static GDBusArgInfo ** +parse_data_steal_out_args (ParseData *data, guint *out_num_elements) +{ + GDBusArgInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->out_args->len; + if (data->out_args == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->out_args, NULL); + ret = (GDBusArgInfo **) g_ptr_array_free (data->out_args, FALSE); + } + data->out_args = g_ptr_array_new (); + return ret; +} + +static GDBusMethodInfo ** +parse_data_steal_methods (ParseData *data, guint *out_num_elements) +{ + GDBusMethodInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->methods->len; + if (data->methods == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->methods, NULL); + ret = (GDBusMethodInfo **) g_ptr_array_free (data->methods, FALSE); + } + data->methods = g_ptr_array_new (); + return ret; +} + +static GDBusSignalInfo ** +parse_data_steal_signals (ParseData *data, guint *out_num_elements) +{ + GDBusSignalInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->signals->len; + if (data->signals == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->signals, NULL); + ret = (GDBusSignalInfo **) g_ptr_array_free (data->signals, FALSE); + } + data->signals = g_ptr_array_new (); + return ret; +} + +static GDBusPropertyInfo ** +parse_data_steal_properties (ParseData *data, guint *out_num_elements) +{ + GDBusPropertyInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->properties->len; + if (data->properties == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->properties, NULL); + ret = (GDBusPropertyInfo **) g_ptr_array_free (data->properties, FALSE); + } + data->properties = g_ptr_array_new (); + return ret; +} + +static GDBusInterfaceInfo ** +parse_data_steal_interfaces (ParseData *data, guint *out_num_elements) +{ + GDBusInterfaceInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->interfaces->len; + if (data->interfaces == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->interfaces, NULL); + ret = (GDBusInterfaceInfo **) g_ptr_array_free (data->interfaces, FALSE); + } + data->interfaces = g_ptr_array_new (); + return ret; +} + +static GDBusNodeInfo ** +parse_data_steal_nodes (ParseData *data, guint *out_num_elements) +{ + GDBusNodeInfo **ret; + if (out_num_elements != NULL) + *out_num_elements = data->nodes->len; + if (data->nodes == NULL) + ret = NULL; + else + { + g_ptr_array_add (data->nodes, NULL); + ret = (GDBusNodeInfo **) g_ptr_array_free (data->nodes, FALSE); + } + data->nodes = g_ptr_array_new (); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +parse_data_free_annotations (ParseData *data) +{ + if (data->annotations == NULL) + return; + g_ptr_array_foreach (data->annotations, (GFunc) g_dbus_annotation_info_unref, NULL); + g_ptr_array_free (data->annotations, TRUE); + data->annotations = NULL; +} + +static void +parse_data_free_args (ParseData *data) +{ + if (data->args == NULL) + return; + g_ptr_array_foreach (data->args, (GFunc) g_dbus_arg_info_unref, NULL); + g_ptr_array_free (data->args, TRUE); + data->args = NULL; +} + +static void +parse_data_free_out_args (ParseData *data) +{ + if (data->out_args == NULL) + return; + g_ptr_array_foreach (data->out_args, (GFunc) g_dbus_arg_info_unref, NULL); + g_ptr_array_free (data->out_args, TRUE); + data->out_args = NULL; +} + +static void +parse_data_free_methods (ParseData *data) +{ + if (data->methods == NULL) + return; + g_ptr_array_foreach (data->methods, (GFunc) g_dbus_method_info_unref, NULL); + g_ptr_array_free (data->methods, TRUE); + data->methods = NULL; +} + +static void +parse_data_free_signals (ParseData *data) +{ + if (data->signals == NULL) + return; + g_ptr_array_foreach (data->signals, (GFunc) g_dbus_signal_info_unref, NULL); + g_ptr_array_free (data->signals, TRUE); + data->signals = NULL; +} + +static void +parse_data_free_properties (ParseData *data) +{ + if (data->properties == NULL) + return; + g_ptr_array_foreach (data->properties, (GFunc) g_dbus_property_info_unref, NULL); + g_ptr_array_free (data->properties, TRUE); + data->properties = NULL; +} + +static void +parse_data_free_interfaces (ParseData *data) +{ + if (data->interfaces == NULL) + return; + g_ptr_array_foreach (data->interfaces, (GFunc) g_dbus_interface_info_unref, NULL); + g_ptr_array_free (data->interfaces, TRUE); + data->interfaces = NULL; +} + +static void +parse_data_free_nodes (ParseData *data) +{ + if (data->nodes == NULL) + return; + g_ptr_array_foreach (data->nodes, (GFunc) g_dbus_node_info_unref, NULL); + g_ptr_array_free (data->nodes, TRUE); + data->nodes = NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAnnotationInfo * +parse_data_get_annotation (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1)); + return data->annotations->pdata[data->annotations->len - 1]; +} + +static GDBusArgInfo * +parse_data_get_arg (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1)); + return data->args->pdata[data->args->len - 1]; +} + +static GDBusArgInfo * +parse_data_get_out_arg (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1)); + return data->out_args->pdata[data->out_args->len - 1]; +} + +static GDBusMethodInfo * +parse_data_get_method (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1)); + return data->methods->pdata[data->methods->len - 1]; +} + +static GDBusSignalInfo * +parse_data_get_signal (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1)); + return data->signals->pdata[data->signals->len - 1]; +} + +static GDBusPropertyInfo * +parse_data_get_property (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1)); + return data->properties->pdata[data->properties->len - 1]; +} + +static GDBusInterfaceInfo * +parse_data_get_interface (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1)); + return data->interfaces->pdata[data->interfaces->len - 1]; +} + +static GDBusNodeInfo * +parse_data_get_node (ParseData *data, gboolean create_new) +{ + if (create_new) + g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1)); + return data->nodes->pdata[data->nodes->len - 1]; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static ParseData * +parse_data_new (void) +{ + ParseData *data; + + data = g_new0 (ParseData, 1); + + /* initialize arrays */ + parse_data_steal_annotations (data, NULL); + parse_data_steal_args (data, NULL); + parse_data_steal_out_args (data, NULL); + parse_data_steal_methods (data, NULL); + parse_data_steal_signals (data, NULL); + parse_data_steal_properties (data, NULL); + parse_data_steal_interfaces (data, NULL); + parse_data_steal_nodes (data, NULL); + + return data; +} + +static void +parse_data_free (ParseData *data) +{ + GSList *l; + + /* free stack of annotation arrays */ + for (l = data->annotations_stack; l != NULL; l = l->next) + { + GPtrArray *annotations = l->data; + g_ptr_array_foreach (annotations, (GFunc) g_dbus_annotation_info_unref, NULL); + g_ptr_array_free (annotations, TRUE); + } + g_slist_free (data->annotations_stack); + + /* free stack of interface arrays */ + for (l = data->interfaces_stack; l != NULL; l = l->next) + { + GPtrArray *interfaces = l->data; + g_ptr_array_foreach (interfaces, (GFunc) g_dbus_interface_info_unref, NULL); + g_ptr_array_free (interfaces, TRUE); + } + g_slist_free (data->interfaces_stack); + + /* free stack of node arrays */ + for (l = data->nodes_stack; l != NULL; l = l->next) + { + GPtrArray *nodes = l->data; + g_ptr_array_foreach (nodes, (GFunc) g_dbus_node_info_unref, NULL); + g_ptr_array_free (nodes, TRUE); + } + g_slist_free (data->nodes_stack); + + /* free arrays (data->annotations, data->interfaces and data->nodes have been freed above) */ + parse_data_free_args (data); + parse_data_free_out_args (data); + parse_data_free_methods (data); + parse_data_free_signals (data); + parse_data_free_properties (data); + + g_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseData *data = user_data; + GSList *stack; + const gchar *name; + const gchar *type; + const gchar *access; + const gchar *direction; + const gchar *value; + + name = NULL; + type = NULL; + access = NULL; + direction = NULL; + value = NULL; + + stack = (GSList *) g_markup_parse_context_get_element_stack (context); + + /* ---------------------------------------------------------------------------------------------------- */ + if (strcmp (element_name, "node") == 0) + { + if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0)) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be top-level or embedded in other elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name, + /* some hand-written introspection XML documents use this */ + G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns:doc", NULL, + G_MARKUP_COLLECT_INVALID)) + goto out; + + g_dbus_node_info_set (data, + parse_data_get_node (data, TRUE), + name, + 0, NULL, + 0, NULL, + NULL); + + /* push the currently retrieved interfaces and nodes on the stack and prepare new arrays */ + data->interfaces_stack = g_slist_prepend (data->interfaces_stack, data->interfaces); + data->interfaces = NULL; + parse_data_steal_interfaces (data, NULL); + + data->nodes_stack = g_slist_prepend (data->nodes_stack, data->nodes); + data->nodes = NULL; + parse_data_steal_nodes (data, NULL); + + } + /* ---------------------------------------------------------------------------------------------------- */ + else if (strcmp (element_name, "interface") == 0) + { + if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "node") != 0) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be embedded in elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_INVALID)) + goto out; + + g_dbus_interface_info_set (data, + parse_data_get_interface (data, TRUE), + name, + 0, NULL, + 0, NULL, + 0, NULL, + NULL); + + } + /* ---------------------------------------------------------------------------------------------------- */ + else if (strcmp (element_name, "method") == 0) + { + if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be embedded in elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_INVALID)) + goto out; + + g_dbus_method_info_set (data, + parse_data_get_method (data, TRUE), + name, + 0, NULL, + 0, NULL, + NULL); + + data->num_args = 0; + + } + /* ---------------------------------------------------------------------------------------------------- */ + else if (strcmp (element_name, "signal") == 0) + { + if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be embedded in elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_INVALID)) + goto out; + + g_dbus_signal_info_set (data, + parse_data_get_signal (data, TRUE), + name, + 0, NULL, + NULL); + + data->num_args = 0; + + } + /* ---------------------------------------------------------------------------------------------------- */ + else if (strcmp (element_name, "property") == 0) + { + GDBusPropertyInfoFlags flags; + + if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be embedded in elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_STRING, "type", &type, + G_MARKUP_COLLECT_STRING, "access", &access, + G_MARKUP_COLLECT_INVALID)) + goto out; + + if (strcmp (access, "read") == 0) + flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE; + else if (strcmp (access, "write") == 0) + flags = G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; + else if (strcmp (access, "readwrite") == 0) + flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; + else + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unknown value '%s' of access attribute for element ", + access); + goto out; + } + + g_dbus_property_info_set (data, + parse_data_get_property (data, TRUE), + name, + type, + flags, + NULL); + + } + /* ---------------------------------------------------------------------------------------------------- */ + else if (strcmp (element_name, "arg") == 0) + { + gboolean is_in; + gchar *name_to_use; + + if (g_slist_length (stack) < 2 || + (strcmp (stack->next->data, "method") != 0 && + strcmp (stack->next->data, "signal") != 0)) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be embedded in or elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name, + G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "direction", &direction, + G_MARKUP_COLLECT_STRING, "type", &type, + G_MARKUP_COLLECT_INVALID)) + goto out; + + is_in = FALSE; + if (direction != NULL) + { + if (strcmp (direction, "in") == 0) + is_in = TRUE; + else if (strcmp (direction, "out") == 0) + is_in = FALSE; + else + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unknown value '%s' of direction attribute", + direction); + goto out; + } + } + + if (is_in && strcmp (stack->next->data, "signal") == 0) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Only direction 'out' is allowed for elements embedded in "); + goto out; + } + + if (name == NULL) + name_to_use = g_strdup_printf ("arg_%d", data->num_args); + else + name_to_use = g_strdup (name); + data->num_args++; + + if (is_in) + { + g_dbus_arg_info_set (data, + parse_data_get_arg (data, TRUE), + name_to_use, + type, + NULL); + data->last_arg_was_in = TRUE; + } + else + { + g_dbus_arg_info_set (data, + parse_data_get_out_arg (data, TRUE), + name_to_use, + type, + NULL); + data->last_arg_was_in = FALSE; + + } + + g_free (name_to_use); + } + /* ---------------------------------------------------------------------------------------------------- */ + else if (strcmp (element_name, "annotation") == 0) + { + if (g_slist_length (stack) < 2 || + (strcmp (stack->next->data, "node") != 0 && + strcmp (stack->next->data, "interface") != 0 && + strcmp (stack->next->data, "signal") != 0 && + strcmp (stack->next->data, "method") != 0 && + strcmp (stack->next->data, "property") != 0 && + strcmp (stack->next->data, "arg") != 0 && + strcmp (stack->next->data, "annotation") != 0)) + { + g_set_error_literal (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " elements can only be embedded in , , , , , or elements"); + goto out; + } + + if (!g_markup_collect_attributes (element_name, + attribute_names, + attribute_values, + error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_STRING, "value", &value, + G_MARKUP_COLLECT_INVALID)) + goto out; + + g_dbus_annotation_info_set (data, + parse_data_get_annotation (data, TRUE), + name, + value, + NULL); + } + /* ---------------------------------------------------------------------------------------------------- */ + else + { + /* don't bail on unknown elements; just ignore them */ + } + /* ---------------------------------------------------------------------------------------------------- */ + + /* push the currently retrieved annotations on the stack and prepare a new one */ + data->annotations_stack = g_slist_prepend (data->annotations_stack, data->annotations); + data->annotations = NULL; + parse_data_steal_annotations (data, NULL); + + out: + ; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusAnnotationInfo ** +steal_annotations (ParseData *data) +{ + return parse_data_steal_annotations (data, NULL); +} + + +static void +parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseData *data = user_data; + gboolean have_popped_annotations; + + have_popped_annotations = FALSE; + + if (strcmp (element_name, "node") == 0) + { + guint num_nodes; + guint num_interfaces; + GDBusNodeInfo **nodes; + GDBusInterfaceInfo **interfaces; + + nodes = parse_data_steal_nodes (data, &num_nodes); + interfaces = parse_data_steal_interfaces (data, &num_interfaces); + + /* destroy the nodes, interfaces for scope we're exiting and and pop the nodes, interfaces from the + * scope we're reentering + */ + parse_data_free_interfaces (data); + data->interfaces = (GPtrArray *) data->interfaces_stack->data; + data->interfaces_stack = g_slist_remove (data->interfaces_stack, data->interfaces_stack->data); + + parse_data_free_nodes (data); + data->nodes = (GPtrArray *) data->nodes_stack->data; + data->nodes_stack = g_slist_remove (data->nodes_stack, data->nodes_stack->data); + + g_dbus_node_info_set (data, + parse_data_get_node (data, FALSE), + NULL, + num_interfaces, + interfaces, + num_nodes, + nodes, + steal_annotations (data)); + + } + else if (strcmp (element_name, "interface") == 0) + { + guint num_methods; + guint num_signals; + guint num_properties; + GDBusMethodInfo **methods; + GDBusSignalInfo **signals; + GDBusPropertyInfo **properties; + + methods = parse_data_steal_methods (data, &num_methods); + signals = parse_data_steal_signals (data, &num_signals); + properties = parse_data_steal_properties (data, &num_properties); + + g_dbus_interface_info_set (data, + parse_data_get_interface (data, FALSE), + NULL, + num_methods, + methods, + num_signals, + signals, + num_properties, + properties, + steal_annotations (data)); + + } + else if (strcmp (element_name, "method") == 0) + { + guint in_num_args; + guint out_num_args; + GDBusArgInfo **in_args; + GDBusArgInfo **out_args; + + in_args = parse_data_steal_args (data, &in_num_args); + out_args = parse_data_steal_out_args (data, &out_num_args); + + g_dbus_method_info_set (data, + parse_data_get_method (data, FALSE), + NULL, + in_num_args, + in_args, + out_num_args, + out_args, + steal_annotations (data)); + } + else if (strcmp (element_name, "signal") == 0) + { + guint num_args; + GDBusArgInfo **args; + + args = parse_data_steal_out_args (data, &num_args); + + g_dbus_signal_info_set (data, + parse_data_get_signal (data, FALSE), + NULL, + num_args, + args, + steal_annotations (data)); + } + else if (strcmp (element_name, "property") == 0) + { + g_dbus_property_info_set (data, + parse_data_get_property (data, FALSE), + NULL, + NULL, + G_DBUS_PROPERTY_INFO_FLAGS_NONE, + steal_annotations (data)); + } + else if (strcmp (element_name, "arg") == 0) + { + g_dbus_arg_info_set (data, + data->last_arg_was_in ? parse_data_get_arg (data, FALSE) : parse_data_get_out_arg (data, FALSE), + NULL, + NULL, + steal_annotations (data)); + } + else if (strcmp (element_name, "annotation") == 0) + { + GDBusAnnotationInfo **embedded_annotations; + + embedded_annotations = steal_annotations (data); + + /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */ + parse_data_free_annotations (data); + data->annotations = (GPtrArray *) data->annotations_stack->data; + data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data); + + have_popped_annotations = TRUE; + + g_dbus_annotation_info_set (data, + parse_data_get_annotation (data, FALSE), + NULL, + NULL, + embedded_annotations); + } + else + { + /* don't bail on unknown elements; just ignore them */ + } + + if (!have_popped_annotations) + { + /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */ + parse_data_free_annotations (data); + data->annotations = (GPtrArray *) data->annotations_stack->data; + data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +parser_error (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + gint line_number; + gint char_number; + + g_markup_parse_context_get_position (context, &line_number, &char_number); + + g_prefix_error (&error, "%d:%d: ", + line_number, + char_number); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_node_info_new_for_xml: + * @xml_data: Valid D-Bus introspection XML. + * @error: Return location for error. + * + * Parses @xml_data and returns a #GDBusNodeInfo representing the data. + * + * Returns: A #GDBusNodeInfo structure or %NULL if @error is set. Free + * with g_dbus_node_info_unref(). + */ +GDBusNodeInfo * +g_dbus_node_info_new_for_xml (const gchar *xml_data, + GError **error) +{ + GDBusNodeInfo *ret; + GMarkupParseContext *context; + GMarkupParser *parser; + guint num_nodes; + ParseData *data; + + ret = NULL; + parser = NULL; + context = NULL; + + parser = g_new0 (GMarkupParser, 1); + parser->start_element = parser_start_element; + parser->end_element = parser_end_element; + parser->error = parser_error; + + data = parse_data_new (); + context = g_markup_parse_context_new (parser, + 0, + data, + (GDestroyNotify) parse_data_free); + + if (!g_markup_parse_context_parse (context, + xml_data, + strlen (xml_data), + error)) + goto out; + + GDBusNodeInfo **ughret; + ughret = parse_data_steal_nodes (data, &num_nodes); + + if (num_nodes != 1) + { + guint n; + + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Expected a single node in introspection XML, found %d", + num_nodes); + + /* clean up */ + for (n = 0; n < num_nodes; n++) + { + for (n = 0; n < num_nodes; n++) + g_dbus_node_info_unref (&(ret[n])); + } + g_free (ret); + ret = NULL; + } + + ret = ughret[0]; + g_free (ughret); + + out: + if (parser != NULL) + g_free (parser); + if (context != NULL) + g_markup_parse_context_free (context); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_annotation_info_lookup: + * @annotations: A %NULL-terminated array of annotations or %NULL. + * @name: The name of the annotation to look up. + * + * Looks up the value of an annotation. + * + * This cost of this function is O(n) in number of annotations. + * + * Returns: The value or %NULL if not found. Do not free, it is owned by @annotations. + */ +const gchar * +g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations, + const gchar *name) +{ + guint n; + const gchar *ret; + + ret = NULL; + for (n = 0; annotations != NULL && annotations[n]->key != NULL; n++) + { + if (g_strcmp0 (annotations[n]->key, name) == 0) + { + ret = annotations[n]->value; + goto out; + } + } + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_interface_info_lookup_method: + * @info: A #GDBusInterfaceInfo. + * @name: A D-Bus method name (typically in CamelCase) + * + * Looks up information about a method. + * + * This cost of this function is O(n) in number of methods. + * + * Returns: A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info. + **/ +const GDBusMethodInfo * +g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info, + const gchar *name) +{ + guint n; + const GDBusMethodInfo *result; + + for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++) + { + const GDBusMethodInfo *i = info->methods[n]; + + if (g_strcmp0 (i->name, name) == 0) + { + result = i; + goto out; + } + } + + result = NULL; + + out: + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_interface_info_lookup_signal: + * @info: A #GDBusInterfaceInfo. + * @name: A D-Bus signal name (typically in CamelCase) + * + * Looks up information about a signal. + * + * This cost of this function is O(n) in number of signals. + * + * Returns: A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info. + **/ +const GDBusSignalInfo * +g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info, + const gchar *name) +{ + guint n; + const GDBusSignalInfo *result; + + for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++) + { + const GDBusSignalInfo *i = info->signals[n]; + + if (g_strcmp0 (i->name, name) == 0) + { + result = i; + goto out; + } + } + + result = NULL; + + out: + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_interface_info_lookup_property: + * @info: A #GDBusInterfaceInfo. + * @name: A D-Bus property name (typically in CamelCase). + * + * Looks up information about a property. + * + * This cost of this function is O(n) in number of properties. + * + * Returns: A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info. + **/ +const GDBusPropertyInfo * +g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info, + const gchar *name) +{ + guint n; + const GDBusPropertyInfo *result; + + for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++) + { + const GDBusPropertyInfo *i = info->properties[n]; + + if (g_strcmp0 (i->name, name) == 0) + { + result = i; + goto out; + } + } + + result = NULL; + + out: + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_node_info_lookup_interface: + * @node_info: A #GDBusNodeInfo. + * @name: A D-Bus interface name. + * + * Looks up information about an interface. + * + * This cost of this function is O(n) in number of interfaces. + * + * Returns: A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @node_info. + **/ +const GDBusInterfaceInfo * +g_dbus_node_info_lookup_interface (const GDBusNodeInfo *node_info, + const gchar *name) +{ + guint n; + const GDBusInterfaceInfo *result; + + for (n = 0; node_info->interfaces != NULL && node_info->interfaces[n] != NULL; n++) + { + const GDBusInterfaceInfo *i = node_info->interfaces[n]; + + if (g_strcmp0 (i->name, name) == 0) + { + result = i; + goto out; + } + } + + result = NULL; + + out: + return result; +} diff --git a/gio/gdbusintrospection.h b/gio/gdbusintrospection.h new file mode 100644 index 000000000..7aea8d9cb --- /dev/null +++ b/gio/gdbusintrospection.h @@ -0,0 +1,255 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_INTROSPECTION_H__ +#define __G_DBUS_INTROSPECTION_H__ + +#include + +G_BEGIN_DECLS + +/** + * GDBusAnnotationInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @key: The name of the annotation, e.g. "org.freedesktop.DBus.Deprecated". + * @value: The value of the annotation. + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about an annotation. + */ +struct _GDBusAnnotationInfo +{ + volatile gint ref_count; + gchar *key; + gchar *value; + GDBusAnnotationInfo **annotations; +}; + +/** + * GDBusArgInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @name: Name of the argument, e.g. @unix_user_id. + * @signature: D-Bus signature of the argument (a single complete type). + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about an argument for a method or a signal. + */ +struct _GDBusArgInfo +{ + volatile gint ref_count; + gchar *name; + gchar *signature; + GDBusAnnotationInfo **annotations; +}; + +/** + * GDBusMethodInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @name: The name of the D-Bus method, e.g. @RequestName. + * @in_args: A pointer to a %NULL-terminated array of pointers to #GDBusArgInfo structures or %NULL if there are no in arguments. + * @out_args: A pointer to a %NULL-terminated array of pointers to #GDBusArgInfo structures or %NULL if there are no out arguments. + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about a method on an D-Bus interface. + */ +struct _GDBusMethodInfo +{ + volatile gint ref_count; + gchar *name; + GDBusArgInfo **in_args; + GDBusArgInfo **out_args; + GDBusAnnotationInfo **annotations; +}; + +/** + * GDBusSignalInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @name: The name of the D-Bus signal, e.g. "NameOwnerChanged". + * @args: A pointer to a %NULL-terminated array of pointers to #GDBusArgInfo structures or %NULL if there are no arguments. + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about a signal on a D-Bus interface. + */ +struct _GDBusSignalInfo +{ + volatile gint ref_count; + gchar *name; + GDBusArgInfo **args; + GDBusAnnotationInfo **annotations; +}; + +/** + * GDBusPropertyInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @name: The name of the D-Bus property, e.g. "SupportedFilesystems". + * @signature: The D-Bus signature of the property (a single complete type). + * @flags: Access control flags for the property. + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about a D-Bus property on a D-Bus interface. + */ +struct _GDBusPropertyInfo +{ + volatile gint ref_count; + gchar *name; + gchar *signature; + GDBusPropertyInfoFlags flags; + GDBusAnnotationInfo **annotations; +}; + +/** + * GDBusInterfaceInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @name: The name of the D-Bus interface, e.g. "org.freedesktop.DBus.Properties". + * @methods: A pointer to a %NULL-terminated array of pointers to #GDBusMethodInfo structures or %NULL if there are no methods. + * @signals: A pointer to a %NULL-terminated array of pointers to #GDBusSignalInfo structures or %NULL if there are no signals. + * @properties: A pointer to a %NULL-terminated array of pointers to #GDBusPropertyInfo structures or %NULL if there are no properties. + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about a D-Bus interface. + */ +struct _GDBusInterfaceInfo +{ + volatile gint ref_count; + gchar *name; + GDBusMethodInfo **methods; + GDBusSignalInfo **signals; + GDBusPropertyInfo **properties; + GDBusAnnotationInfo **annotations; +}; + +/** + * GDBusNodeInfo: + * @ref_count: The reference count or -1 if statically allocated. + * @path: The path of the node or %NULL if omitted. Note that this may be a relative path. See the D-Bus specification for more details. + * @interfaces: A pointer to a %NULL-terminated array of pointers to #GDBusInterfaceInfo structures or %NULL if there are no interfaces. + * @nodes: A pointer to a %NULL-terminated array of pointers to #GDBusNodeInfo structures or %NULL if there are no nodes. + * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. + * + * Information about nodes in a remote object hierarchy. + */ +struct _GDBusNodeInfo +{ + volatile gint ref_count; + gchar *path; + GDBusInterfaceInfo **interfaces; + GDBusNodeInfo **nodes; + GDBusAnnotationInfo **annotations; +}; + +const gchar *g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations, + const gchar *name); +const GDBusMethodInfo *g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info, + const gchar *name); +const GDBusSignalInfo *g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info, + const gchar *name); +const GDBusPropertyInfo *g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info, + const gchar *name); +void g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info, + guint indent, + GString *string_builder); + +GDBusNodeInfo *g_dbus_node_info_new_for_xml (const gchar *xml_data, + GError **error); +const GDBusInterfaceInfo *g_dbus_node_info_lookup_interface (const GDBusNodeInfo *info, + const gchar *name); +void g_dbus_node_info_generate_xml (const GDBusNodeInfo *info, + guint indent, + GString *string_builder); + +GDBusNodeInfo *g_dbus_node_info_ref (GDBusNodeInfo *info); +GDBusInterfaceInfo *g_dbus_interface_info_ref (GDBusInterfaceInfo *info); +GDBusMethodInfo *g_dbus_method_info_ref (GDBusMethodInfo *info); +GDBusSignalInfo *g_dbus_signal_info_ref (GDBusSignalInfo *info); +GDBusPropertyInfo *g_dbus_property_info_ref (GDBusPropertyInfo *info); +GDBusArgInfo *g_dbus_arg_info_ref (GDBusArgInfo *info); +GDBusAnnotationInfo *g_dbus_annotation_info_ref (GDBusAnnotationInfo *info); + +void g_dbus_node_info_unref (GDBusNodeInfo *info); +void g_dbus_interface_info_unref (GDBusInterfaceInfo *info); +void g_dbus_method_info_unref (GDBusMethodInfo *info); +void g_dbus_signal_info_unref (GDBusSignalInfo *info); +void g_dbus_property_info_unref (GDBusPropertyInfo *info); +void g_dbus_arg_info_unref (GDBusArgInfo *info); +void g_dbus_annotation_info_unref (GDBusAnnotationInfo *info); + + +/** + * G_TYPE_DBUS_NODE_INFO: + * + * The #GType for a boxed type holding a #GDBusNodeInfo. + */ +#define G_TYPE_DBUS_NODE_INFO (g_dbus_node_info_get_type ()) + +/** + * G_TYPE_DBUS_INTERFACE_INFO: + * + * The #GType for a boxed type holding a #GDBusInterfaceInfo. + */ +#define G_TYPE_DBUS_INTERFACE_INFO (g_dbus_interface_info_get_type ()) + +/** + * G_TYPE_DBUS_METHOD_INFO: + * + * The #GType for a boxed type holding a #GDBusMethodInfo. + */ +#define G_TYPE_DBUS_METHOD_INFO (g_dbus_method_info_get_type ()) + +/** + * G_TYPE_DBUS_SIGNAL_INFO: + * + * The #GType for a boxed type holding a #GDBusSignalInfo. + */ +#define G_TYPE_DBUS_SIGNAL_INFO (g_dbus_signal_info_get_type ()) + +/** + * G_TYPE_DBUS_PROPERTY_INFO: + * + * The #GType for a boxed type holding a #GDBusPropertyInfo. + */ +#define G_TYPE_DBUS_PROPERTY_INFO (g_dbus_property_info_get_type ()) + +/** + * G_TYPE_DBUS_ARG_INFO: + * + * The #GType for a boxed type holding a #GDBusArgInfo. + */ +#define G_TYPE_DBUS_ARG_INFO (g_dbus_arg_info_get_type ()) + +/** + * G_TYPE_DBUS_ANNOTATION_INFO: + * + * The #GType for a boxed type holding a #GDBusAnnotationInfo. + */ +#define G_TYPE_DBUS_ANNOTATION_INFO (g_dbus_annotation_info_get_type ()) + +GType g_dbus_node_info_get_type (void) G_GNUC_CONST; +GType g_dbus_interface_info_get_type (void) G_GNUC_CONST; +GType g_dbus_method_info_get_type (void) G_GNUC_CONST; +GType g_dbus_signal_info_get_type (void) G_GNUC_CONST; +GType g_dbus_property_info_get_type (void) G_GNUC_CONST; +GType g_dbus_arg_info_get_type (void) G_GNUC_CONST; +GType g_dbus_annotation_info_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __G_DBUS_INTROSPECTION_H__ */ diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c new file mode 100644 index 000000000..668b1c856 --- /dev/null +++ b/gio/gdbusmessage.c @@ -0,0 +1,2421 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include "gdbusutils.h" +#include "gdbusmessage.h" +#include "gdbuserror.h" +#include "gioenumtypes.h" +#include "ginputstream.h" +#include "gdatainputstream.h" +#include "gmemoryinputstream.h" +#include "goutputstream.h" +#include "gdataoutputstream.h" +#include "gmemoryoutputstream.h" +#include "gseekable.h" +#include "gioerror.h" + +#ifdef G_OS_UNIX +#include +#include +#include +#include +#endif + + +/** + * SECTION:gdbusmessage + * @short_description: D-Bus Message + * @include: gdbus/gdbus.h + * + * A type for representing D-Bus messages that can be sent or received + * on a #GDBusConnection. + */ + +struct _GDBusMessagePrivate +{ + GDBusMessageType type; + GDBusMessageFlags flags; + guchar major_protocol_version; + guint32 serial; + GHashTable *headers; + GVariant *body; +#ifdef G_OS_UNIX + GUnixFDList *fd_list; +#endif +}; + +#define g_dbus_message_get_type g_dbus_message_get_gtype +G_DEFINE_TYPE (GDBusMessage, g_dbus_message, G_TYPE_OBJECT); +#undef g_dbus_message_get_type + +static void +g_dbus_message_finalize (GObject *object) +{ + GDBusMessage *message = G_DBUS_MESSAGE (object); + + if (message->priv->headers != NULL) + g_hash_table_unref (message->priv->headers); + if (message->priv->body != NULL) + g_variant_unref (message->priv->body); +#ifdef G_OS_UNIX + if (message->priv->fd_list != NULL) + g_object_unref (message->priv->fd_list); +#endif + + if (G_OBJECT_CLASS (g_dbus_message_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_message_parent_class)->finalize (object); +} + +static void +g_dbus_message_class_init (GDBusMessageClass *klass) +{ + GObjectClass *gobject_class; + + g_type_class_add_private (klass, sizeof (GDBusMessagePrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_message_finalize; +} + +static void +g_dbus_message_init (GDBusMessage *message) +{ + message->priv = G_TYPE_INSTANCE_GET_PRIVATE (message, G_TYPE_DBUS_MESSAGE, GDBusMessagePrivate); + + message->priv->headers = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) g_variant_unref); +} + +/** + * g_dbus_message_new: + * + * Creates a new empty #GDBusMessage. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new (void) +{ + return g_object_new (G_TYPE_DBUS_MESSAGE, NULL); +} + +/** + * g_dbus_message_new_method_call: + * @name: A valid D-Bus name or %NULL. + * @path: A valid object path. + * @interface: A valid D-Bus interface name or %NULL. + * @method: A valid method name. + * + * Creates a new #GDBusMessage for a method call. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_method_call (const gchar *name, + const gchar *path, + const gchar *interface, + const gchar *method) +{ + GDBusMessage *message; + + g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL); + g_return_val_if_fail (g_variant_is_object_path (path), NULL); + g_return_val_if_fail (g_dbus_is_member_name (method), NULL); + g_return_val_if_fail (interface == NULL || g_dbus_is_interface_name (interface), NULL); + + message = g_dbus_message_new (); + message->priv->type = G_DBUS_MESSAGE_TYPE_METHOD_CALL; + + if (name != NULL) + g_dbus_message_set_destination (message, name); + g_dbus_message_set_path (message, path); + g_dbus_message_set_member (message, method); + if (interface != NULL) + g_dbus_message_set_interface (message, interface); + + return message; +} + +/** + * g_dbus_message_new_signal: + * @path: A valid object path. + * @interface: A valid D-Bus interface name or %NULL. + * @signal: A valid signal name. + * + * Creates a new #GDBusMessage for a signal emission. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_signal (const gchar *path, + const gchar *interface, + const gchar *signal) +{ + GDBusMessage *message; + + g_return_val_if_fail (g_variant_is_object_path (path), NULL); + g_return_val_if_fail (g_dbus_is_member_name (signal), NULL); + g_return_val_if_fail (interface == NULL || g_dbus_is_interface_name (interface), NULL); + + message = g_dbus_message_new (); + message->priv->type = G_DBUS_MESSAGE_TYPE_SIGNAL; + message->priv->flags = G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED; + + g_dbus_message_set_path (message, path); + g_dbus_message_set_member (message, signal); + + if (interface != NULL) + g_dbus_message_set_interface (message, interface); + + return message; +} + + +/** + * g_dbus_message_new_method_reply: + * @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to + * create a reply message to. + * + * Creates a new #GDBusMessage that is a reply to @method_call_message. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_method_reply (GDBusMessage *method_call_message) +{ + GDBusMessage *message; + const gchar *sender; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (method_call_message), NULL); + g_return_val_if_fail (g_dbus_message_get_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL); + g_return_val_if_fail (g_dbus_message_get_serial (method_call_message) != 0, NULL); + + message = g_dbus_message_new (); + message->priv->type = G_DBUS_MESSAGE_TYPE_METHOD_RETURN; + message->priv->flags = G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED; + + g_dbus_message_set_reply_serial (message, g_dbus_message_get_serial (method_call_message)); + sender = g_dbus_message_get_sender (method_call_message); + if (sender != NULL) + g_dbus_message_set_destination (message, sender); + + return message; +} + +/** + * g_dbus_message_new_method_error: + * @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to + * create a reply message to. + * @error_name: A valid D-Bus error name. + * @error_message_format: The D-Bus error message in a printf() format. + * @...: Arguments for @error_message_format. + * + * Creates a new #GDBusMessage that is an error reply to @method_call_message. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_method_error (GDBusMessage *method_call_message, + const gchar *error_name, + const gchar *error_message_format, + ...) +{ + GDBusMessage *ret; + va_list var_args; + + va_start (var_args, error_message_format); + ret = g_dbus_message_new_method_error_valist (method_call_message, + error_name, + error_message_format, + var_args); + va_end (var_args); + + return ret; +} + +/** + * g_dbus_message_new_method_error_literal: + * @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to + * create a reply message to. + * @error_name: A valid D-Bus error name. + * @error_message: The D-Bus error message. + * + * Creates a new #GDBusMessage that is an error reply to @method_call_message. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message, + const gchar *error_name, + const gchar *error_message) +{ + GDBusMessage *message; + const gchar *sender; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (method_call_message), NULL); + g_return_val_if_fail (g_dbus_message_get_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL); + g_return_val_if_fail (g_dbus_message_get_serial (method_call_message) != 0, NULL); + g_return_val_if_fail (g_dbus_is_name (error_name), NULL); + g_return_val_if_fail (error_message != NULL, NULL); + + message = g_dbus_message_new (); + message->priv->type = G_DBUS_MESSAGE_TYPE_ERROR; + message->priv->flags = G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED; + + g_dbus_message_set_reply_serial (message, g_dbus_message_get_serial (method_call_message)); + g_dbus_message_set_error_name (message, error_name); + g_dbus_message_set_body (message, g_variant_new ("(s)", error_message)); + + sender = g_dbus_message_get_sender (method_call_message); + if (sender != NULL) + g_dbus_message_set_destination (message, sender); + + return message; +} + +/** + * g_dbus_message_new_method_error_valist: + * @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to + * create a reply message to. + * @error_name: A valid D-Bus error name. + * @error_message_format: The D-Bus error message in a printf() format. + * @var_args: Arguments for @error_message_format. + * + * Like g_dbus_message_new_method_error() but intended for language bindings. + * + * Returns: A #GDBusMessage. Free with g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_method_error_valist (GDBusMessage *method_call_message, + const gchar *error_name, + const gchar *error_message_format, + va_list var_args) +{ + GDBusMessage *ret; + gchar *error_message; + error_message = g_strdup_vprintf (error_message_format, var_args); + ret = g_dbus_message_new_method_error_literal (method_call_message, + error_name, + error_message); + g_free (error_message); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: need GI annotations to specify that any guchar value goes for the type */ + +/** + * g_dbus_message_get_type: + * @message: A #GDBusMessage. + * + * Gets the type of @message. + * + * Returns: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration). + */ +GDBusMessageType +g_dbus_message_get_type (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), G_DBUS_MESSAGE_TYPE_INVALID); + return message->priv->type; +} + +/** + * g_dbus_message_set_type: + * @message: A #GDBusMessage. + * @type: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration). + * + * Sets @message to be of @type. + */ +void +g_dbus_message_set_type (GDBusMessage *message, + GDBusMessageType type) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (type >=0 && type < 256); + message->priv->type = type; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: need GI annotations to specify that any guchar value goes for flags */ + +/** + * g_dbus_message_get_flags: + * @message: A #GDBusMessage. + * + * Gets the flags for @message. + * + * Returns: Flags that are set (typically values from the #GDBusMessageFlags enumeration bitwise ORed together). + */ +GDBusMessageFlags +g_dbus_message_get_flags (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), G_DBUS_MESSAGE_FLAGS_NONE); + return message->priv->flags; +} + +/** + * g_dbus_message_set_flags: + * @message: A #GDBusMessage. + * @flags: Flags for @message that are set (typically values from the #GDBusMessageFlags + * enumeration bitwise ORed together). + * + * Sets the flags to set on @message. + */ +void +g_dbus_message_set_flags (GDBusMessage *message, + GDBusMessageFlags flags) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (flags >=0 && flags < 256); + message->priv->flags = flags; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_serial: + * @message: A #GDBusMessage. + * + * Gets the serial for @message. + * + * Returns: A #guint32. + */ +guint32 +g_dbus_message_get_serial (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0); + return message->priv->serial; +} + +/** + * g_dbus_message_set_serial: + * @message: A #GDBusMessage. + * @serial: A #guint32. + * + * Sets the serial for @message. + */ +void +g_dbus_message_set_serial (GDBusMessage *message, + guint32 serial) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + message->priv->serial = serial; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: need GI annotations to specify that any guchar value goes for header_field */ + +/** + * g_dbus_message_get_header: + * @message: A #GDBusMessage. + * @header_field: A 8-bit unsigned integer (typically a value from the #GDBusMessageHeaderField enumeration) + * + * Gets a header field on @message. + * + * Returns: A #GVariant with the value if the header was found, %NULL + * otherwise. Do not free, it is owned by @message. + */ +GVariant * +g_dbus_message_get_header (GDBusMessage *message, + GDBusMessageHeaderField header_field) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail (header_field >=0 && header_field < 256, NULL); + return g_hash_table_lookup (message->priv->headers, GUINT_TO_POINTER (header_field)); +} + +/** + * g_dbus_message_set_header: + * @message: A #GDBusMessage. + * @header_field: A 8-bit unsigned integer (typically a value from the #GDBusMessageHeaderField enumeration) + * @value: A #GVariant to set the header field or %NULL to clear the header field. + * + * Sets a header field on @message. + * + * If @value is floating, @message assumes ownership of @value. + */ +void +g_dbus_message_set_header (GDBusMessage *message, + GDBusMessageHeaderField header_field, + GVariant *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (header_field >=0 && header_field < 256); + if (value == NULL) + { + g_hash_table_remove (message->priv->headers, GUINT_TO_POINTER (header_field)); + } + else + { + g_hash_table_insert (message->priv->headers, GUINT_TO_POINTER (header_field), g_variant_ref_sink (value)); + } +} + +/** + * g_dbus_message_get_header_fields: + * @message: A #GDBusMessage. + * + * Gets an array of all header fields on @message that are set. + * + * Returns: An array of header fields terminated by + * %G_DBUS_MESSAGE_HEADER_FIELD_INVALID. Each element is a + * #guchar. Free with g_free(). + */ +guchar * +g_dbus_message_get_header_fields (GDBusMessage *message) +{ + GList *keys; + guchar *ret; + guint num_keys; + GList *l; + guint n; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + + keys = g_hash_table_get_keys (message->priv->headers); + num_keys = g_list_length (keys); + ret = g_new (guchar, num_keys + 1); + for (l = keys, n = 0; l != NULL; l = l->next, n++) + ret[n] = GPOINTER_TO_UINT (l->data); + g_assert (n == num_keys); + ret[n] = G_DBUS_MESSAGE_HEADER_FIELD_INVALID; + g_list_free (keys); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_body: + * @message: A #GDBusMessage. + * + * Gets the body of a message. + * + * Returns: A #GVariant or %NULL if the body is empty. Do not free, it is owned by @message. + */ +GVariant * +g_dbus_message_get_body (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return message->priv->body; +} + +/** + * g_dbus_message_set_body: + * @message: A #GDBusMessage. + * @body: Either %NULL or a #GVariant that is a tuple. + * + * Sets the body @message. As a side-effect the + * %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field is set to the + * type string of @body (or cleared if @body is %NULL). + * + * If @body is floating, @message assumes ownership of @body. + */ +void +g_dbus_message_set_body (GDBusMessage *message, + GVariant *body) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE_TUPLE)); + + if (message->priv->body != NULL) + g_variant_unref (message->priv->body); + if (body == NULL) + { + message->priv->body = NULL; + g_dbus_message_set_signature (message, NULL); + } + else + { + const gchar *type_string; + gsize type_string_len; + gchar *signature; + + message->priv->body = g_variant_ref_sink (body); + + type_string = g_variant_get_type_string (body); + type_string_len = strlen (type_string); + g_assert (type_string_len >= 2); + signature = g_strndup (type_string + 1, type_string_len - 2); + g_dbus_message_set_signature (message, signature); + g_free (signature); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_UNIX +/** + * g_dbus_message_get_unix_fd_list: + * @message: A #GDBusMessage. + * + * Gets the UNIX file descriptors associated with @message, if any. + * + * This method is only available on UNIX. + * + * Returns: A #GUnixFDList or %NULL if no file descriptors are + * associated. Do not free, this object is owned by @message. + */ +GUnixFDList * +g_dbus_message_get_unix_fd_list (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return message->priv->fd_list; +} + +/** + * g_dbus_message_set_unix_fd_list: + * @message: A #GDBusMessage. + * @fd_list: A #GUnixFDList or %NULL. + * + * Sets the UNIX file descriptors associated with @message. As a + * side-effect the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header + * field is set to the number of fds in @fd_list (or cleared if + * @fd_list is %NULL). + * + * This method is only available on UNIX. + */ +void +g_dbus_message_set_unix_fd_list (GDBusMessage *message, + GUnixFDList *fd_list) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list)); + if (message->priv->fd_list != NULL) + g_object_unref (message->priv->fd_list); + if (fd_list != NULL) + { + message->priv->fd_list = g_object_ref (fd_list); + g_dbus_message_set_num_unix_fds (message, g_unix_fd_list_get_length (fd_list)); + } + else + { + message->priv->fd_list = NULL; + g_dbus_message_set_num_unix_fds (message, 0); + } +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +ensure_input_padding (GMemoryInputStream *mis, + gsize padding_size, + GError **error) +{ + gsize offset; + gsize wanted_offset; + + offset = g_seekable_tell (G_SEEKABLE (mis)); + wanted_offset = ((offset + padding_size - 1) / padding_size) * padding_size; + + return g_seekable_seek (G_SEEKABLE (mis), wanted_offset, G_SEEK_SET, NULL, error); +} + +static gchar * +read_string (GMemoryInputStream *mis, + GDataInputStream *dis, + gsize len, + GError **error) +{ + GString *s; + gchar buf[256]; + gsize remaining; + guchar nul; + GError *local_error; + + s = g_string_new (NULL); + + remaining = len; + while (remaining > 0) + { + gsize to_read; + gssize num_read; + + to_read = MIN (remaining, sizeof (buf)); + num_read = g_input_stream_read (G_INPUT_STREAM (mis), + buf, + to_read, + NULL, + error); + if (num_read < 0) + goto fail; + if (num_read == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Wanted to read %" G_GSIZE_FORMAT " bytes but got EOF"), + to_read); + goto fail; + } + + remaining -= num_read; + g_string_append_len (s, buf, num_read); + } + + local_error = NULL; + nul = g_data_input_stream_read_byte (dis, NULL, &local_error); + if (local_error != NULL) + { + g_propagate_error (error, local_error); + goto fail; + } + if (nul != '\0') + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Expected NUL byte after the string `%s' but found `%c' (%d)"), + s->str, nul, nul); + goto fail; + } + + return g_string_free (s, FALSE); + + fail: + g_string_free (s, TRUE); + return NULL; +} + +static GVariant * +parse_value_from_blob (GMemoryInputStream *mis, + GDataInputStream *dis, + const GVariantType *type, + GError **error) +{ + GVariant *ret; + GError *local_error; + + local_error = NULL; + if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) + { + gboolean v; + if (!ensure_input_padding (mis, 4, &local_error)) + goto fail; + v = g_data_input_stream_read_uint32 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_boolean (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE)) + { + guchar v; + v = g_data_input_stream_read_byte (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_byte (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) + { + gint16 v; + if (!ensure_input_padding (mis, 2, &local_error)) + goto fail; + v = g_data_input_stream_read_int16 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_int16 (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) + { + guint16 v; + if (!ensure_input_padding (mis, 2, &local_error)) + goto fail; + v = g_data_input_stream_read_uint16 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_uint16 (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + { + gint32 v; + if (!ensure_input_padding (mis, 4, &local_error)) + goto fail; + v = g_data_input_stream_read_int32 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_int32 (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) + { + guint32 v; + if (!ensure_input_padding (mis, 4, &local_error)) + goto fail; + v = g_data_input_stream_read_uint32 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_uint32 (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) + { + gint64 v; + if (!ensure_input_padding (mis, 8, &local_error)) + goto fail; + v = g_data_input_stream_read_int64 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_int64 (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) + { + guint64 v; + if (!ensure_input_padding (mis, 8, &local_error)) + goto fail; + v = g_data_input_stream_read_uint64 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + ret = g_variant_new_uint64 (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) + { + guint64 v; + gdouble *encoded; + if (!ensure_input_padding (mis, 8, &local_error)) + goto fail; + v = g_data_input_stream_read_uint64 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + /* TODO: hmm */ + encoded = (gdouble *) &v; + ret = g_variant_new_double (*encoded); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) + { + guint32 len; + gchar *v; + if (!ensure_input_padding (mis, 4, &local_error)) + goto fail; + len = g_data_input_stream_read_uint32 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + v = read_string (mis, dis, (gsize) len, &local_error); + if (v == NULL) + goto fail; + ret = g_variant_new_string (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) + { + guint32 len; + gchar *v; + if (!ensure_input_padding (mis, 4, &local_error)) + goto fail; + len = g_data_input_stream_read_uint32 (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + v = read_string (mis, dis, (gsize) len, &local_error); + if (v == NULL) + goto fail; + if (!g_variant_is_object_path (v)) + { + g_set_error (&local_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Parsed value `%s' is not a valid D-Bus object path"), + v); + goto fail; + } + ret = g_variant_new_object_path (v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE)) + { + guchar len; + gchar *v; + len = g_data_input_stream_read_byte (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + v = read_string (mis, dis, (gsize) len, &local_error); + if (v == NULL) + goto fail; + if (!g_variant_is_signature (v)) + { + g_set_error (&local_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Parsed value `%s' is not a valid D-Bus signature"), + v); + goto fail; + } + ret = g_variant_new_signature (v); + } + else if (g_variant_type_is_array (type)) + { + guint32 array_len; + goffset offset; + goffset target; + const GVariantType *element_type; + GVariantBuilder *builder; + + if (!ensure_input_padding (mis, 4, &local_error)) + goto fail; + array_len = g_data_input_stream_read_uint32 (dis, NULL, &local_error); + + if (array_len > (2<<26)) + { + g_set_error (&local_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Encountered array of length %" G_GUINT32_FORMAT " bytes. Maximum length is 2<<26 bytes."), + array_len); + goto fail; + } + + builder = g_variant_builder_new (type); + element_type = g_variant_type_element (type); + + /* TODO: optimize array of primitive types */ + + offset = g_seekable_tell (G_SEEKABLE (mis)); + target = offset + array_len; + while (offset < target) + { + GVariant *item; + item = parse_value_from_blob (mis, dis, element_type, &local_error); + if (item == NULL) + { + g_variant_builder_unref (builder); + goto fail; + } + g_variant_builder_add_value (builder, item); + offset = g_seekable_tell (G_SEEKABLE (mis)); + } + + ret = g_variant_builder_end (builder); + } + else if (g_variant_type_is_dict_entry (type)) + { + const GVariantType *key_type; + const GVariantType *value_type; + GVariant *key; + GVariant *value; + + if (!ensure_input_padding (mis, 8, &local_error)) + goto fail; + + key_type = g_variant_type_key (type); + key = parse_value_from_blob (mis, dis, key_type, &local_error); + if (key == NULL) + goto fail; + + value_type = g_variant_type_value (type); + value = parse_value_from_blob (mis, dis, value_type, &local_error); + if (value == NULL) + { + g_variant_unref (key); + goto fail; + } + ret = g_variant_new_dict_entry (key, value); + } + else if (g_variant_type_is_tuple (type)) + { + const GVariantType *element_type; + GVariantBuilder *builder; + + if (!ensure_input_padding (mis, 8, &local_error)) + goto fail; + + builder = g_variant_builder_new (type); + element_type = g_variant_type_first (type); + while (element_type != NULL) + { + GVariant *item; + item = parse_value_from_blob (mis, dis, element_type, &local_error); + if (item == NULL) + { + g_variant_builder_unref (builder); + goto fail; + } + g_variant_builder_add_value (builder, item); + + element_type = g_variant_type_next (element_type); + } + ret = g_variant_builder_end (builder); + } + else if (g_variant_type_is_variant (type)) + { + guchar siglen; + gchar *sig; + GVariantType *variant_type; + GVariant *value; + + siglen = g_data_input_stream_read_byte (dis, NULL, &local_error); + if (local_error != NULL) + goto fail; + sig = read_string (mis, dis, (gsize) siglen, &local_error); + if (sig == NULL) + goto fail; + if (!g_variant_is_signature (sig)) + { + g_set_error (&local_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Parsed value `%s' for variant is not a valid D-Bus signature"), + sig); + goto fail; + } + variant_type = g_variant_type_new (sig); + value = parse_value_from_blob (mis, dis, variant_type, &local_error); + g_variant_type_free (variant_type); + if (value == NULL) + goto fail; + ret = g_variant_new_variant (value); + } + else + { + gchar *s; + s = g_variant_type_dup_string (type); + g_set_error (&local_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error deserializing GVariant with type-string `%s' from the D-Bus wire format"), + s); + g_free (s); + goto fail; + } + + g_assert (ret != NULL); + return ret; + + fail: + g_propagate_error (error, local_error); + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* message_header must be at least 16 bytes */ + +/** + * g_dbus_message_bytes_needed: + * @blob: A blob represent a binary D-Bus message. + * @blob_len: The length of @blob (must be at least 16). + * @error: Return location for error or %NULL. + * + * Utility function to calculate how many bytes are needed to + * completely deserialize the D-Bus message stored at @blob. + * + * Returns: Number of bytes needed or -1 if @error is set (e.g. if + * @blob contains invalid data or not enough data is available to + * determine the size). + */ +gssize +g_dbus_message_bytes_needed (guchar *blob, + gsize blob_len, + GError **error) +{ + gssize ret; + + ret = -1; + + g_return_val_if_fail (blob != NULL, -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); + g_return_val_if_fail (blob_len >= 16, -1); + + if (blob[0] == 'l') + { + /* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */ + ret = 12 + 4 + GUINT32_FROM_LE (((guint32 *) blob)[3]); + /* round up so it's a multiple of 8 */ + ret = 8 * ((ret + 7)/8); + /* finally add the body size */ + ret += GUINT32_FROM_LE (((guint32 *) blob)[1]); + } + else if (blob[0] == 'B') + { + /* TODO */ + g_assert_not_reached (); + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Unable to determine message blob length - given blob is malformed"); + } + + if (ret > (2<<27)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Blob indicates that message exceeds maximum message length (128MiB)"); + ret = -1; + } + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_new_from_blob: + * @blob: A blob represent a binary D-Bus message. + * @blob_len: The length of @blob. + * @error: Return location for error or %NULL. + * + * Creates a new #GDBusMessage from the data stored at @blob. + * + * Returns: A new #GDBusMessage or %NULL if @error is set. Free with + * g_object_unref(). + */ +GDBusMessage * +g_dbus_message_new_from_blob (guchar *blob, + gsize blob_len, + GError **error) +{ + gboolean ret; + GMemoryInputStream *mis; + GDataInputStream *dis; + GDBusMessage *message; + guchar endianness; + guchar major_protocol_version; + GDataStreamByteOrder byte_order; + guint32 message_body_len; + GVariant *headers; + GVariant *item; + GVariantIter iter; + GVariant *signature; + + ret = FALSE; + + g_return_val_if_fail (blob != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_return_val_if_fail (blob_len >= 12, NULL); + + message = g_dbus_message_new (); + + mis = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new_from_data (blob, blob_len, NULL)); + dis = g_data_input_stream_new (G_INPUT_STREAM (mis)); + + endianness = g_data_input_stream_read_byte (dis, NULL, NULL); + switch (endianness) + { + case 'l': + byte_order = G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN; + break; + case 'B': + byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN; + break; + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Invalid endianness value. Expected 'l' or 'B' but found '%c' (%d)"), + endianness, endianness); + goto out; + } + g_data_input_stream_set_byte_order (dis, byte_order); + + message->priv->type = g_data_input_stream_read_byte (dis, NULL, NULL); + message->priv->flags = g_data_input_stream_read_byte (dis, NULL, NULL); + major_protocol_version = g_data_input_stream_read_byte (dis, NULL, NULL); + if (major_protocol_version != 1) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Invalid major protocol version. Expected 1 but found %d"), + major_protocol_version); + goto out; + } + message_body_len = g_data_input_stream_read_uint32 (dis, NULL, NULL); + message->priv->serial = g_data_input_stream_read_uint32 (dis, NULL, NULL); + + headers = parse_value_from_blob (mis, + dis, + G_VARIANT_TYPE ("a{yv}"), + error); + if (headers == NULL) + goto out; + g_variant_ref_sink (headers); + g_variant_iter_init (&iter, headers); + while ((item = g_variant_iter_next_value (&iter))) + { + guchar header_field; + GVariant *value; + g_variant_get (item, + "{yv}", + &header_field, + &value); + g_dbus_message_set_header (message, header_field, value); + } + g_variant_unref (headers); + + signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE); + if (signature != NULL) + { + const gchar *signature_str; + gsize signature_str_len; + + signature_str = g_variant_get_string (signature, NULL); + signature_str_len = strlen (signature_str); + + /* signature but no body */ + if (message_body_len == 0 && signature_str_len > 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Signature header with signature `%s' found but message body is empty"), + signature_str); + goto out; + } + else if (signature_str_len > 0) + { + GVariantType *variant_type; + gchar *tupled_signature_str; + + if (!g_variant_is_signature (signature_str)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Parsed value `%s' is not a valid D-Bus signature (for body)"), + signature_str); + goto out; + } + tupled_signature_str = g_strdup_printf ("(%s)", signature_str); + variant_type = g_variant_type_new (tupled_signature_str); + g_free (tupled_signature_str); + message->priv->body = parse_value_from_blob (mis, + dis, + variant_type, + error); + if (message->priv->body == NULL) + { + g_variant_type_free (variant_type); + goto out; + } + g_variant_ref_sink (message->priv->body); + g_variant_type_free (variant_type); + } + } + else + { + /* no signature, this is only OK if the body is empty */ + if (message_body_len != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("No signature header in message but the message body is %" G_GUINT32_FORMAT " bytes"), + message_body_len); + goto out; + } + } + + + ret = TRUE; + + out: + g_object_unref (dis); + g_object_unref (mis); + + if (ret) + { + return message; + } + else + { + if (message != NULL) + g_object_unref (message); + return NULL; + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gsize +ensure_output_padding (GMemoryOutputStream *mos, + GDataOutputStream *dos, + gsize padding_size) +{ + gsize offset; + gsize wanted_offset; + gsize padding_needed; + guint n; + + offset = g_memory_output_stream_get_data_size (mos); + wanted_offset = ((offset + padding_size - 1) / padding_size) * padding_size; + padding_needed = wanted_offset - offset; + + for (n = 0; n < padding_needed; n++) + g_data_output_stream_put_byte (dos, '\0', NULL, NULL); + + return padding_needed; +} + +static gboolean +append_value_to_blob (GVariant *value, + GMemoryOutputStream *mos, + GDataOutputStream *dos, + gsize *out_padding_added, + GError **error) +{ + const GVariantType *type; + gsize padding_added; + + padding_added = 0; + + type = g_variant_get_type (value); + if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) + { + gboolean v = g_variant_get_boolean (value); + padding_added = ensure_output_padding (mos, dos, 4); + g_data_output_stream_put_uint32 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE)) + { + guint8 v = g_variant_get_byte (value); + g_data_output_stream_put_byte (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) + { + gint16 v = g_variant_get_int16 (value); + padding_added = ensure_output_padding (mos, dos, 2); + g_data_output_stream_put_int16 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) + { + guint16 v = g_variant_get_uint16 (value); + padding_added = ensure_output_padding (mos, dos, 2); + g_data_output_stream_put_uint16 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + { + gint32 v = g_variant_get_int32 (value); + padding_added = ensure_output_padding (mos, dos, 4); + g_data_output_stream_put_int32 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) + { + guint32 v = g_variant_get_uint32 (value); + padding_added = ensure_output_padding (mos, dos, 4); + g_data_output_stream_put_uint32 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) + { + gint64 v = g_variant_get_int64 (value); + padding_added = ensure_output_padding (mos, dos, 8); + g_data_output_stream_put_int64 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) + { + guint64 v = g_variant_get_uint64 (value); + padding_added = ensure_output_padding (mos, dos, 8); + g_data_output_stream_put_uint64 (dos, v, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) + { + guint64 *encoded; + gdouble v = g_variant_get_double (value); + padding_added = ensure_output_padding (mos, dos, 8); + /* TODO: hmm */ + encoded = (guint64 *) &v; + g_data_output_stream_put_uint64 (dos, *encoded, NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) + { + const gchar *v = g_variant_get_string (value, NULL); + gsize len; + padding_added = ensure_output_padding (mos, dos, 4); + len = strlen (v); + g_data_output_stream_put_uint32 (dos, len, NULL, NULL); + g_data_output_stream_put_string (dos, v, NULL, NULL); + g_data_output_stream_put_byte (dos, '\0', NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) + { + /* TODO: validate object path */ + const gchar *v = g_variant_get_string (value, NULL); + gsize len; + padding_added = ensure_output_padding (mos, dos, 4); + len = strlen (v); + g_data_output_stream_put_uint32 (dos, len, NULL, NULL); + g_data_output_stream_put_string (dos, v, NULL, NULL); + g_data_output_stream_put_byte (dos, '\0', NULL, NULL); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE)) + { + /* TODO: validate signature (including max len being 255) */ + const gchar *v = g_variant_get_string (value, NULL); + gsize len; + len = strlen (v); + g_data_output_stream_put_byte (dos, len, NULL, NULL); + g_data_output_stream_put_string (dos, v, NULL, NULL); + g_data_output_stream_put_byte (dos, '\0', NULL, NULL); + } + else if (g_variant_type_is_array (type)) + { + GVariant *item; + GVariantIter iter; + goffset array_len_offset; + goffset array_payload_begin_offset; + goffset cur_offset; + gsize array_len; + guint n; + + padding_added = ensure_output_padding (mos, dos, 4); + + /* array length - will be filled in later */ + array_len_offset = g_memory_output_stream_get_data_size (mos); + g_data_output_stream_put_uint32 (dos, 0xF00DFACE, NULL, NULL); + + /* From the D-Bus spec: + * + * "A UINT32 giving the length of the array data in bytes, + * followed by alignment padding to the alignment boundary of + * the array element type, followed by each array element. The + * array length is from the end of the alignment padding to + * the end of the last element, i.e. it does not include the + * padding after the length, or any padding after the last + * element." + * + * Thus, we need to count how much padding the first element + * contributes and subtract that from the array length. + */ + array_payload_begin_offset = g_memory_output_stream_get_data_size (mos); + + g_variant_iter_init (&iter, value); + n = 0; + while ((item = g_variant_iter_next_value (&iter))) + { + gsize padding_added_for_item; + if (!append_value_to_blob (item, mos, dos, &padding_added_for_item, error)) + goto fail; + if (n == 0) + { + array_payload_begin_offset += padding_added_for_item; + } + n++; + } + + cur_offset = g_memory_output_stream_get_data_size (mos); + + array_len = cur_offset - array_payload_begin_offset; + + if (!g_seekable_seek (G_SEEKABLE (mos), array_len_offset, G_SEEK_SET, NULL, error)) + goto fail; + + g_data_output_stream_put_uint32 (dos, array_len, NULL, NULL); + + if (!g_seekable_seek (G_SEEKABLE (mos), cur_offset, G_SEEK_SET, NULL, error)) + goto fail; + } + else if (g_variant_type_is_dict_entry (type) || g_variant_type_is_tuple (type)) + { + GVariant *item; + GVariantIter iter; + + padding_added = ensure_output_padding (mos, dos, 8); + + g_variant_iter_init (&iter, value); + + while ((item = g_variant_iter_next_value (&iter))) + { + if (!append_value_to_blob (item, mos, dos, NULL, error)) + goto fail; + } + } + else if (g_variant_type_is_variant (type)) + { + GVariant *child; + const gchar *signature; + child = g_variant_get_child_value (value, 0); + signature = g_variant_get_type_string (child); + /* TODO: validate signature (including max len being 255) */ + g_data_output_stream_put_byte (dos, strlen (signature), NULL, NULL); + g_data_output_stream_put_string (dos, signature, NULL, NULL); + g_data_output_stream_put_byte (dos, '\0', NULL, NULL); + if (!append_value_to_blob (child, mos, dos, NULL, error)) + { + g_variant_unref (child); + goto fail; + } + g_variant_unref (child); + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error serializing GVariant with type-string `%s' to the D-Bus wire format"), + g_variant_get_type_string (value)); + goto fail; + } + + if (out_padding_added != NULL) + *out_padding_added = padding_added; + + return TRUE; + + fail: + return FALSE; +} + +static gboolean +append_body_to_blob (GVariant *value, + GMemoryOutputStream *mos, + GDataOutputStream *dos, + GError **error) +{ + gboolean ret; + GVariant *item; + GVariantIter iter; + + ret = FALSE; + + if (!g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Expected a tuple for the body of the GDBusMessage."); + goto fail; + } + + g_variant_iter_init (&iter, value); + while ((item = g_variant_iter_next_value (&iter))) + { + if (!append_value_to_blob (item, mos, dos, NULL, error)) + goto fail; + } + return TRUE; + + fail: + return FALSE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_to_blob: + * @message: A #GDBusMessage. + * @out_size: Return location for size of generated blob. + * @error: Return location for error. + * + * Serializes @message to a blob. + * + * Returns: A pointer to a valid binary D-Bus message of @out_size bytes + * generated by @message or %NULL if @error is set. Free with g_free(). + */ +guchar * +g_dbus_message_to_blob (GDBusMessage *message, + gsize *out_size, + GError **error) +{ + GMemoryOutputStream *mos; + GDataOutputStream *dos; + guchar *ret; + gsize size; + GDataStreamByteOrder byte_order; + goffset body_len_offset; + goffset body_start_offset; + gsize body_size; + GVariant *header_fields; + GVariantBuilder *builder; + GHashTableIter hash_iter; + gpointer key; + GVariant *header_value; + GVariant *signature; + const gchar *signature_str; + gint num_fds_in_message; + gint num_fds_according_to_header; + + ret = NULL; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail (out_size != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + mos = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (NULL, 0, g_realloc, g_free)); + dos = g_data_output_stream_new (G_OUTPUT_STREAM (mos)); + + /* TODO: detect endianess... */ + byte_order = G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN; + g_data_output_stream_set_byte_order (dos, byte_order); + + /* Core header */ + g_data_output_stream_put_byte (dos, byte_order == G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN ? 'l' : 'B', NULL, NULL); + g_data_output_stream_put_byte (dos, message->priv->type, NULL, NULL); + g_data_output_stream_put_byte (dos, message->priv->flags, NULL, NULL); + g_data_output_stream_put_byte (dos, 1, NULL, NULL); /* major protocol version */ + body_len_offset = g_memory_output_stream_get_data_size (mos); + /* body length - will be filled in later */ + g_data_output_stream_put_uint32 (dos, 0xF00DFACE, NULL, NULL); + g_data_output_stream_put_uint32 (dos, message->priv->serial, NULL, NULL); + + num_fds_in_message = 0; +#ifdef G_OS_UNIX + if (message->priv->fd_list != NULL) + num_fds_in_message = g_unix_fd_list_get_length (message->priv->fd_list); +#endif + num_fds_according_to_header = g_dbus_message_get_num_unix_fds (message); + /* TODO: check we have all the right header fields and that they are the correct value etc etc */ + if (num_fds_in_message != num_fds_according_to_header) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Message has %d fds but the header field indicates %d fds"), + num_fds_in_message, + num_fds_according_to_header); + goto out; + } + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{yv}"));//G_VARIANT_TYPE_ARRAY); + g_hash_table_iter_init (&hash_iter, message->priv->headers); + while (g_hash_table_iter_next (&hash_iter, &key, (gpointer) &header_value)) + { + g_variant_builder_add (builder, + "{yv}", + (guchar) GPOINTER_TO_UINT (key), + header_value); + } + header_fields = g_variant_new ("a{yv}", builder); + + if (!append_value_to_blob (header_fields, mos, dos, NULL, error)) + { + g_variant_unref (header_fields); + goto out; + } + g_variant_unref (header_fields); + + /* header size must be a multiple of 8 */ + ensure_output_padding (mos, dos, 8); + + body_start_offset = g_memory_output_stream_get_data_size (mos); + + signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE); + signature_str = NULL; + if (signature != NULL) + signature_str = g_variant_get_string (signature, NULL); + if (message->priv->body != NULL) + { + gchar *tupled_signature_str; + tupled_signature_str = g_strdup_printf ("(%s)", signature_str); + if (signature == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Message body has signature `%s' but there is no signature header"), + signature_str); + g_free (tupled_signature_str); + goto out; + } + else if (g_strcmp0 (tupled_signature_str, g_variant_get_type_string (message->priv->body)) != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Message body has type signature `%s' but signature in the header field is `%s'"), + tupled_signature_str, g_variant_get_type_string (message->priv->body)); + g_free (tupled_signature_str); + goto out; + } + g_free (tupled_signature_str); + if (!append_body_to_blob (message->priv->body, mos, dos, error)) + goto out; + } + else + { + if (signature != NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Message body is empty but signature in the header field is `(%s)'"), + signature_str); + goto out; + } + } + + /* OK, we're done writing the message - set the body length */ + size = g_memory_output_stream_get_data_size (mos); + body_size = size - body_start_offset; + + if (!g_seekable_seek (G_SEEKABLE (mos), body_len_offset, G_SEEK_SET, NULL, error)) + goto out; + + g_data_output_stream_put_uint32 (dos, body_size, NULL, NULL); + + *out_size = size; + ret = g_memdup (g_memory_output_stream_get_data (mos), size); + + out: + g_object_unref (dos); + g_object_unref (mos); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static guint32 +get_uint32_header (GDBusMessage *message, + GDBusMessageHeaderField header_field) +{ + GVariant *value; + guint32 ret; + + ret = 0; + value = g_hash_table_lookup (message->priv->headers, GUINT_TO_POINTER (header_field)); + if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32)) + ret = g_variant_get_uint32 (value); + + return ret; +} + +static const gchar * +get_string_header (GDBusMessage *message, + GDBusMessageHeaderField header_field) +{ + GVariant *value; + const gchar *ret; + + ret = NULL; + value = g_hash_table_lookup (message->priv->headers, GUINT_TO_POINTER (header_field)); + if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + ret = g_variant_get_string (value, NULL); + + return ret; +} + +static const gchar * +get_object_path_header (GDBusMessage *message, + GDBusMessageHeaderField header_field) +{ + GVariant *value; + const gchar *ret; + + ret = NULL; + value = g_hash_table_lookup (message->priv->headers, GUINT_TO_POINTER (header_field)); + if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH)) + ret = g_variant_get_string (value, NULL); + + return ret; +} + +static const gchar * +get_signature_header (GDBusMessage *message, + GDBusMessageHeaderField header_field) +{ + GVariant *value; + const gchar *ret; + + ret = NULL; + value = g_hash_table_lookup (message->priv->headers, GUINT_TO_POINTER (header_field)); + if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_SIGNATURE)) + ret = g_variant_get_string (value, NULL); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +set_uint32_header (GDBusMessage *message, + GDBusMessageHeaderField header_field, + guint32 value) +{ + g_dbus_message_set_header (message, + header_field, + g_variant_new_uint32 (value)); +} + +static void +set_string_header (GDBusMessage *message, + GDBusMessageHeaderField header_field, + const gchar *value) +{ + g_dbus_message_set_header (message, + header_field, + value == NULL ? NULL : g_variant_new_string (value)); +} + +static void +set_object_path_header (GDBusMessage *message, + GDBusMessageHeaderField header_field, + const gchar *value) +{ + g_dbus_message_set_header (message, + header_field, + value == NULL ? NULL : g_variant_new_object_path (value)); +} + +static void +set_signature_header (GDBusMessage *message, + GDBusMessageHeaderField header_field, + const gchar *value) +{ + g_dbus_message_set_header (message, + header_field, + value == NULL ? NULL : g_variant_new_signature (value)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_reply_serial: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL header field. + * + * Returns: The value. + */ +guint32 +g_dbus_message_get_reply_serial (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0); + return get_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL); +} + +/** + * g_dbus_message_set_reply_serial: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL header field. + */ +void +g_dbus_message_set_reply_serial (GDBusMessage *message, + guint32 value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + set_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_interface: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_interface (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE); +} + +/** + * g_dbus_message_set_interface: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE header field. + */ +void +g_dbus_message_set_interface (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_dbus_is_interface_name (value)); + set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_member: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_MEMBER header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_member (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER); +} + +/** + * g_dbus_message_set_member: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_MEMBER header field. + */ +void +g_dbus_message_set_member (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_dbus_is_member_name (value)); + set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_path: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_PATH header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_path (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return get_object_path_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH); +} + +/** + * g_dbus_message_set_path: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_PATH header field. + */ +void +g_dbus_message_set_path (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_variant_is_object_path (value)); + set_object_path_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_sender: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_SENDER header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_sender (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SENDER); +} + +/** + * g_dbus_message_set_sender: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_SENDER header field. + */ +void +g_dbus_message_set_sender (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_dbus_is_name (value)); + set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SENDER, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_destination: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_destination (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION); +} + +/** + * g_dbus_message_set_destination: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION header field. + */ +void +g_dbus_message_set_destination (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_dbus_is_name (value)); + set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_error_name: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_error_name (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME); +} + +/** + * g_dbus_message_set_error_name: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field. + */ +void +g_dbus_message_set_error_name (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_dbus_is_interface_name (value)); + set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_signature: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field. + * + * Returns: The value. + */ +const gchar * +g_dbus_message_get_signature (GDBusMessage *message) +{ + const gchar *ret; + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + ret = get_signature_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE); + if (ret == NULL) + ret = ""; + return ret; +} + +/** + * g_dbus_message_set_signature: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field. + */ +void +g_dbus_message_set_signature (GDBusMessage *message, + const gchar *value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (value == NULL || g_variant_is_signature (value)); + set_signature_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_arg0: + * @message: A #GDBusMessage. + * + * Convenience to get the first item in the body of @message. + * + * Returns: The string item or %NULL if the first item in the body of + * @message is not a string. + */ +const gchar * +g_dbus_message_get_arg0 (GDBusMessage *message) +{ + const gchar *ret; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + + ret = NULL; + + if (message->priv->body != NULL && g_variant_is_of_type (message->priv->body, G_VARIANT_TYPE_TUPLE)) + { + GVariant *item; + item = g_variant_get_child_value (message->priv->body, 0); + if (g_variant_is_of_type (item, G_VARIANT_TYPE_STRING)) + ret = g_variant_get_string (item, NULL); + } + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_get_num_unix_fds: + * @message: A #GDBusMessage. + * + * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header field. + * + * Returns: The value. + */ +guint32 +g_dbus_message_get_num_unix_fds (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0); + return get_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS); +} + +/** + * g_dbus_message_set_num_unix_fds: + * @message: A #GDBusMessage. + * @value: The value to set. + * + * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header field. + */ +void +g_dbus_message_set_num_unix_fds (GDBusMessage *message, + guint32 value) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + set_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS, value); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_message_to_gerror: + * @message: A #GDBusMessage. + * @error: The #GError to set. + * + * If @message is not of type %G_DBUS_MESSAGE_TYPE_ERROR does + * nothing and returns %FALSE. + * + * Otherwise this method encodes the error in @message as a #GError + * using g_dbus_error_set_dbus_error() using the information in the + * %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field of @message as + * well as the first string item in @message's body. + * + * Returns: %TRUE if @error was set, %FALSE otherwise. + */ +gboolean +g_dbus_message_to_gerror (GDBusMessage *message, + GError **error) +{ + gboolean ret; + const gchar *error_name; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); + + ret = FALSE; + if (message->priv->type != G_DBUS_MESSAGE_TYPE_ERROR) + goto out; + + error_name = g_dbus_message_get_error_name (message); + if (error_name != NULL) + { + GVariant *body; + + body = g_dbus_message_get_body (message); + + if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)"))) + { + const gchar *error_message; + g_variant_get (body, "(s)", &error_message); + g_dbus_error_set_dbus_error (error, + error_name, + error_message, + NULL); + } + else + { + /* these two situations are valid, yet pretty rare */ + if (body != NULL) + { + g_dbus_error_set_dbus_error (error, + error_name, + "", + _("Error return with body of type `%s'"), + g_variant_get_type_string (body)); + } + else + { + g_dbus_error_set_dbus_error (error, + error_name, + "", + _("Error return with empty body")); + } + } + } + else + { + /* TOOD: this shouldn't happen - should check this at message serialization + * time and disconnect the peer. + */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Error return without error-name header!"); + } + + ret = TRUE; + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +enum_to_string (GType enum_type, gint value) +{ + gchar *ret; + GEnumClass *klass; + GEnumValue *enum_value; + + klass = g_type_class_ref (enum_type); + enum_value = g_enum_get_value (klass, value); + if (enum_value != NULL) + ret = g_strdup (enum_value->value_nick); + else + ret = g_strdup_printf ("unknown (value %d)", value); + g_type_class_unref (klass); + return ret; +} + +static gchar * +flags_to_string (GType flags_type, guint value) +{ + GString *s; + GFlagsClass *klass; + guint n; + + klass = g_type_class_ref (flags_type); + s = g_string_new (NULL); + for (n = 0; n < 32; n++) + { + if ((value & (1<len > 0) + g_string_append_c (s, ','); + if (flags_value != NULL) + g_string_append (s, flags_value->value_nick); + else + g_string_append_printf (s, "unknown (bit %d)", n); + } + } + if (s->len == 0) + g_string_append (s, "none"); + g_type_class_unref (klass); + return g_string_free (s, FALSE);; +} + +static gint +_sort_keys_func (gconstpointer a, + gconstpointer b) +{ + gint ia; + gint ib; + + ia = GPOINTER_TO_INT (a); + ib = GPOINTER_TO_INT (b); + + return ia - ib; +} + +/** + * g_dbus_message_print: + * @message: A #GDBusMessage. + * @indent: Indentation level. + * + * Produces a human-readable multi-line description of @message. + * + * The contents of the description has no ABI guarantees, the contents + * and formatting is subject to change at any time. Typical output + * looks something like this: + * + * Type: method-call + * Flags: none + * Version: 0 + * Serial: 4 + * Headers: + * path -> objectpath '/org/gtk/GDBus/TestObject' + * interface -> 'org.gtk.GDBus.TestInterface' + * member -> 'GimmeStdout' + * destination -> ':1.146' + * Body: () + * UNIX File Descriptors: + * (none) + * + * or + * + * Type: method-return + * Flags: no-reply-expected + * Version: 0 + * Serial: 477 + * Headers: + * reply-serial -> uint32 4 + * destination -> ':1.159' + * sender -> ':1.146' + * num-unix-fds -> uint32 1 + * Body: () + * UNIX File Descriptors: + * fd 12: dev=0:10,mode=020620,ino=5,uid=500,gid=5,rdev=136:2,size=0,atime=1273085037,mtime=1273085851,ctime=1272982635 + * + * + * Returns: A string that should be freed with g_free(). + */ +gchar * +g_dbus_message_print (GDBusMessage *message, + guint indent) +{ + GString *str; + gchar *s; + GList *keys; + GList *l; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + + str = g_string_new (NULL); + + s = enum_to_string (G_TYPE_DBUS_MESSAGE_TYPE, message->priv->type); + g_string_append_printf (str, "%*sType: %s\n", indent, "", s); + g_free (s); + s = flags_to_string (G_TYPE_DBUS_MESSAGE_FLAGS, message->priv->flags); + g_string_append_printf (str, "%*sFlags: %s\n", indent, "", s); + g_free (s); + g_string_append_printf (str, "%*sVersion: %d\n", indent, "", message->priv->major_protocol_version); + g_string_append_printf (str, "%*sSerial: %d\n", indent, "", message->priv->serial); + + g_string_append_printf (str, "%*sHeaders:\n", indent, ""); + keys = g_hash_table_get_keys (message->priv->headers); + keys = g_list_sort (keys, _sort_keys_func); + if (keys != NULL) + { + for (l = keys; l != NULL; l = l->next) + { + gint key = GPOINTER_TO_INT (l->data); + GVariant *value; + gchar *value_str; + + value = g_hash_table_lookup (message->priv->headers, l->data); + g_assert (value != NULL); + + s = enum_to_string (G_TYPE_DBUS_MESSAGE_HEADER_FIELD, key); + value_str = g_variant_print (value, TRUE); + g_string_append_printf (str, "%*s %s -> %s\n", indent, "", s, value_str); + g_free (s); + g_free (value_str); + } + } + else + { + g_string_append_printf (str, "%*s (none)\n", indent, ""); + } + g_string_append_printf (str, "%*sBody: ", indent, ""); + if (message->priv->body != NULL) + { + g_variant_print_string (message->priv->body, + str, + TRUE); + } + else + { + g_string_append (str, "()"); + } + g_string_append (str, "\n"); +#ifdef G_OS_UNIX + g_string_append_printf (str, "%*sUNIX File Descriptors:\n", indent, ""); + if (message->priv->fd_list != NULL) + { + gint num_fds; + const gint *fds; + gint n; + + fds = g_unix_fd_list_peek_fds (message->priv->fd_list, &num_fds); + if (num_fds > 0) + { + for (n = 0; n < num_fds; n++) + { + GString *fs; + struct stat statbuf; + fs = g_string_new (NULL); + if (fstat (fds[n], &statbuf) == 0) + { + g_string_append_printf (fs, "%s" "dev=%d:%d", fs->len > 0 ? "," : "", + major (statbuf.st_dev), minor (statbuf.st_dev)); + g_string_append_printf (fs, "%s" "mode=0%o", fs->len > 0 ? "," : "", + statbuf.st_mode); + g_string_append_printf (fs, "%s" "ino=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "", + (guint64) statbuf.st_ino); + g_string_append_printf (fs, "%s" "uid=%d", fs->len > 0 ? "," : "", + statbuf.st_uid); + g_string_append_printf (fs, "%s" "gid=%d", fs->len > 0 ? "," : "", + statbuf.st_gid); + g_string_append_printf (fs, "%s" "rdev=%d:%d", fs->len > 0 ? "," : "", + major (statbuf.st_rdev), minor (statbuf.st_rdev)); + g_string_append_printf (fs, "%s" "size=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "", + (guint64) statbuf.st_size); + g_string_append_printf (fs, "%s" "atime=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "", + (guint64) statbuf.st_atime); + g_string_append_printf (fs, "%s" "mtime=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "", + (guint64) statbuf.st_mtime); + g_string_append_printf (fs, "%s" "ctime=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "", + (guint64) statbuf.st_ctime); + } + else + { + g_string_append_printf (fs, "(fstat failed: %s)", strerror (errno)); + } + g_string_append_printf (str, "%*s fd %d: %s\n", indent, "", fds[n], fs->str); + g_string_free (fs, TRUE); + } + } + else + { + g_string_append_printf (str, "%*s (empty)\n", indent, ""); + } + } + else + { + g_string_append_printf (str, "%*s (none)\n", indent, ""); + } +#endif + + return g_string_free (str, FALSE); +} + diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h new file mode 100644 index 000000000..4fb1873b5 --- /dev/null +++ b/gio/gdbusmessage.h @@ -0,0 +1,172 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_MESSAGE_H__ +#define __G_DBUS_MESSAGE_H__ + +#include + +#ifdef G_OS_UNIX +#include +#endif + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_MESSAGE (g_dbus_message_get_gtype ()) +#define G_DBUS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_MESSAGE, GDBusMessage)) +#define G_DBUS_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_MESSAGE, GDBusMessageClass)) +#define G_DBUS_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_MESSAGE, GDBusMessageClass)) +#define G_IS_DBUS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_MESSAGE)) +#define G_IS_DBUS_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_MESSAGE)) + +typedef struct _GDBusMessageClass GDBusMessageClass; +typedef struct _GDBusMessagePrivate GDBusMessagePrivate; + +/** + * GDBusMessageClass: + * + * Class structure for #GDBusMessage. + */ +struct _GDBusMessageClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +/** + * GDBusMessage: + * + * The #GDBusMessage structure contains only private data and should + * only be accessed using the provided API. + */ +struct _GDBusMessage +{ + /*< private >*/ + GObject parent_instance; + GDBusMessagePrivate *priv; +}; + +GType g_dbus_message_get_gtype (void) G_GNUC_CONST; +GDBusMessage *g_dbus_message_new (void); +GDBusMessage *g_dbus_message_new_signal (const gchar *path, + const gchar *interface, + const gchar *signal); +GDBusMessage *g_dbus_message_new_method_call (const gchar *name, + const gchar *path, + const gchar *interface, + const gchar *method); +GDBusMessage *g_dbus_message_new_method_reply (GDBusMessage *method_call_message); +GDBusMessage *g_dbus_message_new_method_error (GDBusMessage *method_call_message, + const gchar *error_name, + const gchar *error_message_format, + ...); +GDBusMessage *g_dbus_message_new_method_error_valist (GDBusMessage *method_call_message, + const gchar *error_name, + const gchar *error_message_format, + va_list var_args); +GDBusMessage *g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message, + const gchar *error_name, + const gchar *error_message); +gchar *g_dbus_message_print (GDBusMessage *message, + guint indent); + +GDBusMessageType g_dbus_message_get_type (GDBusMessage *message); +void g_dbus_message_set_type (GDBusMessage *message, + GDBusMessageType type); +GDBusMessageFlags g_dbus_message_get_flags (GDBusMessage *message); +void g_dbus_message_set_flags (GDBusMessage *message, + GDBusMessageFlags flags); +guint32 g_dbus_message_get_serial (GDBusMessage *message); +void g_dbus_message_set_serial (GDBusMessage *message, + guint32 serial); +GVariant *g_dbus_message_get_header (GDBusMessage *message, + GDBusMessageHeaderField header_field); +void g_dbus_message_set_header (GDBusMessage *message, + GDBusMessageHeaderField header_field, + GVariant *value); +guchar *g_dbus_message_get_header_fields (GDBusMessage *message); +GVariant *g_dbus_message_get_body (GDBusMessage *message); +void g_dbus_message_set_body (GDBusMessage *message, + GVariant *body); +#ifdef G_OS_UNIX +GUnixFDList *g_dbus_message_get_unix_fd_list (GDBusMessage *message); +void g_dbus_message_set_unix_fd_list (GDBusMessage *message, + GUnixFDList *fd_list); +#endif + +guint32 g_dbus_message_get_reply_serial (GDBusMessage *message); +void g_dbus_message_set_reply_serial (GDBusMessage *message, + guint32 value); + +const gchar *g_dbus_message_get_interface (GDBusMessage *message); +void g_dbus_message_set_interface (GDBusMessage *message, + const gchar *value); + +const gchar *g_dbus_message_get_member (GDBusMessage *message); +void g_dbus_message_set_member (GDBusMessage *message, + const gchar *value); + +const gchar *g_dbus_message_get_path (GDBusMessage *message); +void g_dbus_message_set_path (GDBusMessage *message, + const gchar *value); + +const gchar *g_dbus_message_get_sender (GDBusMessage *message); +void g_dbus_message_set_sender (GDBusMessage *message, + const gchar *value); + +const gchar *g_dbus_message_get_destination (GDBusMessage *message); +void g_dbus_message_set_destination (GDBusMessage *message, + const gchar *value); + +const gchar *g_dbus_message_get_error_name (GDBusMessage *message); +void g_dbus_message_set_error_name (GDBusMessage *message, + const gchar *value); + +const gchar *g_dbus_message_get_signature (GDBusMessage *message); +void g_dbus_message_set_signature (GDBusMessage *message, + const gchar *value); + +guint32 g_dbus_message_get_num_unix_fds (GDBusMessage *message); +void g_dbus_message_set_num_unix_fds (GDBusMessage *message, + guint32 value); + +const gchar *g_dbus_message_get_arg0 (GDBusMessage *message); + + +GDBusMessage *g_dbus_message_new_from_blob (guchar *blob, + gsize blob_len, + GError **error); + +gssize g_dbus_message_bytes_needed (guchar *blob, + gsize blob_len, + GError **error); + +guchar *g_dbus_message_to_blob (GDBusMessage *message, + gsize *out_size, + GError **error); + +gboolean g_dbus_message_to_gerror (GDBusMessage *message, + GError **error); + +G_END_DECLS + +#endif /* __G_DBUS_MESSAGE_H__ */ diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c new file mode 100644 index 000000000..dc6950bea --- /dev/null +++ b/gio/gdbusmethodinvocation.c @@ -0,0 +1,795 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 +#include + +#include "gdbusutils.h" +#include "gdbusconnection.h" +#include "gdbusmessage.h" +#include "gdbusmethodinvocation.h" +#include "gdbusintrospection.h" +#include "gdbuserror.h" +#include "gdbusprivate.h" + +/** + * SECTION:gdbusmethodinvocation + * @short_description: Object for handling remote calls + * @include: gdbus/gdbus.h + * + * Instances of the #GDBusMethodInvocation class are used when + * handling D-Bus method calls. It provides a way to asynchronously + * return results and errors. + */ + +struct _GDBusMethodInvocationPrivate +{ + /* construct-only properties */ + gchar *sender; + gchar *object_path; + gchar *interface_name; + gchar *method_name; + const GDBusMethodInfo *method_info; + GDBusConnection *connection; + GDBusMessage *message; + GVariant *parameters; + gpointer user_data; +}; + +enum +{ + PROP_0, + PROP_SENDER, + PROP_OBJECT_PATH, + PROP_INTERFACE_NAME, + PROP_METHOD_NAME, + PROP_METHOD_INFO, + PROP_CONNECTION, + PROP_PARAMETERS, + PROP_MESSAGE, + PROP_USER_DATA +}; + +G_DEFINE_TYPE (GDBusMethodInvocation, g_dbus_method_invocation, G_TYPE_OBJECT); + +static void +g_dbus_method_invocation_finalize (GObject *object) +{ + GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object); + + g_free (invocation->priv->sender); + g_free (invocation->priv->object_path); + g_free (invocation->priv->interface_name); + g_free (invocation->priv->method_name); + g_object_unref (invocation->priv->connection); + g_object_unref (invocation->priv->message); + g_variant_unref (invocation->priv->parameters); + + if (G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize (object); +} + +static void +g_dbus_method_invocation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object); + + switch (prop_id) + { + case PROP_SENDER: + g_value_set_string (value, g_dbus_method_invocation_get_sender (invocation)); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, g_dbus_method_invocation_get_object_path (invocation)); + break; + + case PROP_INTERFACE_NAME: + g_value_set_string (value, g_dbus_method_invocation_get_interface_name (invocation)); + break; + + case PROP_METHOD_NAME: + g_value_set_string (value, g_dbus_method_invocation_get_method_name (invocation)); + break; + + case PROP_METHOD_INFO: + g_value_set_boxed (value, g_dbus_method_invocation_get_method_info (invocation)); + break; + + case PROP_CONNECTION: + g_value_set_object (value, g_dbus_method_invocation_get_connection (invocation)); + break; + + case PROP_PARAMETERS: + g_value_set_boxed (value, g_dbus_method_invocation_get_parameters (invocation)); + break; + + case PROP_MESSAGE: + g_value_set_object (value, g_dbus_method_invocation_get_message (invocation)); + break; + + case PROP_USER_DATA: + g_value_set_pointer (value, g_dbus_method_invocation_get_user_data (invocation)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_method_invocation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object); + + switch (prop_id) + { + case PROP_SENDER: + invocation->priv->sender = g_value_dup_string (value); + break; + + case PROP_OBJECT_PATH: + invocation->priv->object_path = g_value_dup_string (value); + break; + + case PROP_INTERFACE_NAME: + invocation->priv->interface_name = g_value_dup_string (value); + break; + + case PROP_METHOD_NAME: + invocation->priv->method_name = g_value_dup_string (value); + break; + + case PROP_METHOD_INFO: + invocation->priv->method_info = g_value_dup_boxed (value); + break; + + case PROP_CONNECTION: + invocation->priv->connection = g_value_dup_object (value); + break; + + case PROP_PARAMETERS: + invocation->priv->parameters = g_value_dup_boxed (value); + break; + + case PROP_MESSAGE: + invocation->priv->message = g_value_dup_object (value); + break; + + case PROP_USER_DATA: + invocation->priv->user_data = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_method_invocation_finalize; + gobject_class->set_property = g_dbus_method_invocation_set_property; + gobject_class->get_property = g_dbus_method_invocation_get_property; + + /** + * GDBusMethodInvocation:sender: + * + * The bus name that invoked the method or %NULL if the connection is not a bus connection. + */ + g_object_class_install_property (gobject_class, + PROP_SENDER, + g_param_spec_string ("sender", + _("Sender"), + _("The bus name that invoked the method."), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:object-path: + * + * The object path the method was invoked on. + */ + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + _("Object Path"), + _("The object path the method was invoked on."), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:interface-name: + * + * The name of the D-Bus interface the method was invoked on. + */ + g_object_class_install_property (gobject_class, + PROP_INTERFACE_NAME, + g_param_spec_string ("interface-name", + _("Interface Name"), + _("The name of the D-Bus interface the method was invoked on."), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:method-name: + * + * The name of the method that was invoked. + */ + g_object_class_install_property (gobject_class, + PROP_METHOD_NAME, + g_param_spec_string ("method-name", + _("Method Name"), + _("The name of the method that was invoked."), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:method-info: + * + * Information about the method that was invoked, if any. + */ + g_object_class_install_property (gobject_class, + PROP_METHOD_INFO, + g_param_spec_boxed ("method-info", + _("Method Info"), + _("Information about the method that was invoked, if any."), + G_TYPE_DBUS_METHOD_INFO, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:connection: + * + * The #GDBusConnection the method was invoked on. + */ + g_object_class_install_property (gobject_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + _("Connection"), + _("The #GDBusConnection the method was invoked on."), + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:message: + * + * The D-Bus message. + */ + g_object_class_install_property (gobject_class, + PROP_MESSAGE, + g_param_spec_object ("message", + _("Message"), + _("The D-Bus Message."), + G_TYPE_DBUS_MESSAGE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:parameters: + * + * The parameters as a #GVariant tuple. + */ + g_object_class_install_property (gobject_class, + PROP_PARAMETERS, + g_param_spec_boxed ("parameters", + _("Parameters"), + _("The parameters as a #GVariant tuple."), + G_TYPE_VARIANT, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusMethodInvocation:user-data: + * + * The @user_data #gpointer passed to g_dbus_connection_register_object(). + */ + g_object_class_install_property (gobject_class, + PROP_USER_DATA, + g_param_spec_pointer ("user-data", + _("User Data"), + _("The gpointer passed to g_dbus_connection_register_object()."), + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + g_type_class_add_private (klass, sizeof (GDBusMethodInvocationPrivate)); +} + +static void +g_dbus_method_invocation_init (GDBusMethodInvocation *invocation) +{ + invocation->priv = G_TYPE_INSTANCE_GET_PRIVATE (invocation, + G_TYPE_DBUS_METHOD_INVOCATION, + GDBusMethodInvocationPrivate); +} + +/** + * g_dbus_method_invocation_get_sender: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the bus name that invoked the method. + * + * Returns: A string. Do not free, it is owned by @invocation. + */ +const gchar * +g_dbus_method_invocation_get_sender (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->sender; +} + +/** + * g_dbus_method_invocation_get_object_path: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the object path the method was invoked on. + * + * Returns: A string. Do not free, it is owned by @invocation. + */ +const gchar * +g_dbus_method_invocation_get_object_path (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->object_path; +} + +/** + * g_dbus_method_invocation_get_interface_name: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the name of the D-Bus interface the method was invoked on. + * + * Returns: A string. Do not free, it is owned by @invocation. + */ +const gchar * +g_dbus_method_invocation_get_interface_name (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->interface_name; +} + +/** + * g_dbus_method_invocation_get_method_info: + * @invocation: A #GDBusMethodInvocation. + * + * Gets information about the method call, if any. + * + * Returns: A #GDBusMethodInfo or %NULL. Do not free, it is owned by @invocation. + */ +const GDBusMethodInfo * +g_dbus_method_invocation_get_method_info (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->method_info; +} + +/** + * g_dbus_method_invocation_get_method_name: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the name of the method that was invoked. + * + * Returns: A string. Do not free, it is owned by @invocation. + */ +const gchar * +g_dbus_method_invocation_get_method_name (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->method_name; +} + +/** + * g_dbus_method_invocation_get_connection: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the #GDBusConnection the method was invoked on. + * + * Returns: A #GDBusConnection. Do not free, it is owned by @invocation. + */ +GDBusConnection * +g_dbus_method_invocation_get_connection (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->connection; +} + +/** + * g_dbus_method_invocation_get_message: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the #GDBusMessage for the method invocation. This is useful if + * you need to use low-level protocol features, such as UNIX file + * descriptor passing, that cannot be properly expressed in the + * #GVariant API. + * + * See and for an example of how to use this + * low-level API to send and receive UNIX file descriptors. + * + * Returns: A #GDBusMessage. Do not free, it is owned by @invocation. + */ +GDBusMessage * +g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->message; +} + +/** + * g_dbus_method_invocation_get_parameters: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the parameters of the method invocation. + * + * Returns: A #GVariant. Do not free, it is owned by @invocation. + */ +GVariant * +g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->parameters; +} + +/** + * g_dbus_method_invocation_get_user_data: + * @invocation: A #GDBusMethodInvocation. + * + * Gets the @user_data #gpointer passed to g_dbus_connection_register_object(). + * + * Returns: A #gpointer. + */ +gpointer +g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + return invocation->priv->user_data; +} + +/** + * g_dbus_method_invocation_new: + * @sender: The bus name that invoked the method or %NULL if @connection is not a bus connection. + * @object_path: The object path the method was invoked on. + * @interface_name: The name of the D-Bus interface the method was invoked on. + * @method_name: The name of the method that was invoked. + * @method_info: Information about the method call or %NULL. + * @connection: The #GDBusConnection the method was invoked on. + * @message: The D-Bus message as a #GDBusMessage. + * @parameters: The parameters as a #GVariant tuple. + * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object(). + * + * Creates a new #GDBusMethodInvocation object. + * + * Returns: A #GDBusMethodInvocation. Free with g_object_unref(). + */ +GDBusMethodInvocation * +g_dbus_method_invocation_new (const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + const GDBusMethodInfo *method_info, + GDBusConnection *connection, + GDBusMessage *message, + GVariant *parameters, + gpointer user_data) +{ + g_return_val_if_fail (sender == NULL || g_dbus_is_name (sender), NULL); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), NULL); + g_return_val_if_fail (g_dbus_is_member_name (method_name), NULL); + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail (g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); + + return G_DBUS_METHOD_INVOCATION (g_object_new (G_TYPE_DBUS_METHOD_INVOCATION, + "sender", sender, + "object-path", object_path, + "interface-name", interface_name, + "method-name", method_name, + "method-info", method_info, + "connection", connection, + "message", message, + "parameters", parameters, + "user-data", user_data, + NULL)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_method_invocation_return_value: + * @invocation: A #GDBusMethodInvocation. + * @parameters: A #GVariant tuple with out parameters for the method or %NULL if not passing any parameters. + * + * Finishes handling a D-Bus method call by returning @parameters. + * + * It is an error if @parameters is not of the right format. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + GDBusMessage *reply; + GError *error; + + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); + + if (parameters != NULL) + g_variant_ref_sink (parameters); + + /* if we have introspection data, check that the signature of @parameters is correct */ + if (invocation->priv->method_info != NULL) + { + gchar *signature; + const gchar *type_string; + + type_string = "()"; + if (parameters != NULL) + type_string = g_variant_get_type_string (parameters); + signature = _g_dbus_compute_complete_signature (invocation->priv->method_info->out_args, TRUE); + + if (g_strcmp0 (type_string, signature) != 0) + { + g_warning (_("Type of return value is incorrect, got `%s', expected `%s'"), + type_string, + signature); + g_free (signature); + goto out; + } + g_free (signature); + } + + reply = g_dbus_message_new_method_reply (invocation->priv->message); + g_dbus_message_set_body (reply, parameters); + error = NULL; + if (!g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, NULL, &error)) + { + g_warning (_("Error sending message: %s"), error->message); + g_error_free (error); + } + g_object_unref (reply); + + out: + g_object_unref (invocation); + if (parameters != NULL) + g_variant_unref (parameters); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_method_invocation_return_error: + * @invocation: A #GDBusMethodInvocation. + * @domain: A #GQuark for the #GError error domain. + * @code: The error code. + * @format: printf()-style format. + * @...: Parameters for @format. + * + * Finishes handling a D-Bus method call by returning an error. + * + * See g_dbus_error_encode_gerror() for details about what error name + * will be returned on the wire. In a nutshell, if the given error is + * registered using g_dbus_error_register_error() the name given + * during registration is used. Otherwise, a name of the form + * org.gtk.GDBus.UnmappedGError.Quark... is + * used. This provides transparent mapping of #GError between + * applications using GDBus. + * + * If you are writing an application intended to be portable, + * always register errors with g_dbus_error_register_error() + * or use g_dbus_method_invocation_return_dbus_error(). + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +g_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation, + GQuark domain, + gint code, + const gchar *format, + ...) +{ + va_list var_args; + + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_return_if_fail (format != NULL); + + va_start (var_args, format); + g_dbus_method_invocation_return_error_valist (invocation, + domain, + code, + format, + var_args); + va_end (var_args); +} + +/** + * g_dbus_method_invocation_return_error_valist: + * @invocation: A #GDBusMethodInvocation. + * @domain: A #GQuark for the #GError error domain. + * @code: The error code. + * @format: printf()-style format. + * @var_args: #va_list of parameters for @format. + * + * Like g_dbus_method_invocation_return_error() but intended for + * language bindings. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +g_dbus_method_invocation_return_error_valist (GDBusMethodInvocation *invocation, + GQuark domain, + gint code, + const gchar *format, + va_list var_args) +{ + gchar *literal_message; + + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_return_if_fail (format != NULL); + + literal_message = g_strdup_vprintf (format, var_args); + g_dbus_method_invocation_return_error_literal (invocation, + domain, + code, + literal_message); + g_free (literal_message); +} + +/** + * g_dbus_method_invocation_return_error_literal: + * @invocation: A #GDBusMethodInvocation. + * @domain: A #GQuark for the #GError error domain. + * @code: The error code. + * @message: The error message. + * + * Like g_dbus_method_invocation_return_error() but without printf()-style formatting. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +g_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation, + GQuark domain, + gint code, + const gchar *message) +{ + GError *error; + + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_return_if_fail (message != NULL); + + error = g_error_new_literal (domain, code, message); + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); +} + +/** + * g_dbus_method_invocation_return_gerror: + * @invocation: A #GDBusMethodInvocation. + * @error: A #GError. + * + * Like g_dbus_method_invocation_return_error() but takes a #GError + * instead of the error domain, error code and message. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +g_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation, + const GError *error) +{ + gchar *dbus_error_name; + + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_return_if_fail (error != NULL); + + dbus_error_name = g_dbus_error_encode_gerror (error); + + g_dbus_method_invocation_return_dbus_error (invocation, + dbus_error_name, + error->message); + g_free (dbus_error_name); +} + +/** + * g_dbus_method_invocation_return_dbus_error: + * @invocation: A #GDBusMethodInvocation. + * @error_name: A valid D-Bus error name. + * @error_message: A valid D-Bus error message. + * + * Finishes handling a D-Bus method call by returning an error. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +g_dbus_method_invocation_return_dbus_error (GDBusMethodInvocation *invocation, + const gchar *error_name, + const gchar *error_message) +{ + GDBusMessage *reply; + + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_return_if_fail (error_name != NULL && g_dbus_is_name (error_name)); + g_return_if_fail (error_message != NULL); + + reply = g_dbus_message_new_method_error_literal (invocation->priv->message, + error_name, + error_message); + g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, NULL, NULL); + g_object_unref (reply); + + g_object_unref (invocation); +} diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h new file mode 100644 index 000000000..65d0f9924 --- /dev/null +++ b/gio/gdbusmethodinvocation.h @@ -0,0 +1,119 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_METHOD_INVOCATION_H__ +#define __G_DBUS_METHOD_INVOCATION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_METHOD_INVOCATION (g_dbus_method_invocation_get_type ()) +#define G_DBUS_METHOD_INVOCATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocation)) +#define G_DBUS_METHOD_INVOCATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocationClass)) +#define G_DBUS_METHOD_INVOCATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocationClass)) +#define G_IS_DBUS_METHOD_INVOCATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_METHOD_INVOCATION)) +#define G_IS_DBUS_METHOD_INVOCATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_METHOD_INVOCATION)) + +typedef struct _GDBusMethodInvocationClass GDBusMethodInvocationClass; +typedef struct _GDBusMethodInvocationPrivate GDBusMethodInvocationPrivate; + +/** + * GDBusMethodInvocation: + * + * The #GDBusMethodInvocation structure contains only private data and + * should only be accessed using the provided API. + */ +struct _GDBusMethodInvocation +{ + /*< private >*/ + GObject parent_instance; + GDBusMethodInvocationPrivate *priv; +}; + +/** + * GDBusMethodInvocationClass: + * + * Class structure for #GDBusMethodInvocation. + */ +struct _GDBusMethodInvocationClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +GType g_dbus_method_invocation_get_type (void) G_GNUC_CONST; +GDBusMethodInvocation *g_dbus_method_invocation_new (const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + const GDBusMethodInfo *method_info, + GDBusConnection *connection, + GDBusMessage *message, + GVariant *parameters, + gpointer user_data); +const gchar *g_dbus_method_invocation_get_sender (GDBusMethodInvocation *invocation); +const gchar *g_dbus_method_invocation_get_object_path (GDBusMethodInvocation *invocation); +const gchar *g_dbus_method_invocation_get_interface_name (GDBusMethodInvocation *invocation); +const gchar *g_dbus_method_invocation_get_method_name (GDBusMethodInvocation *invocation); +const GDBusMethodInfo *g_dbus_method_invocation_get_method_info (GDBusMethodInvocation *invocation); +GDBusConnection *g_dbus_method_invocation_get_connection (GDBusMethodInvocation *invocation); +GDBusMessage *g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation); +GVariant *g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation); +gpointer g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation); + +void g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation, + GVariant *parameters); +void g_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation, + GQuark domain, + gint code, + const gchar *format, + ...); +void g_dbus_method_invocation_return_error_valist (GDBusMethodInvocation *invocation, + GQuark domain, + gint code, + const gchar *format, + va_list var_args); +void g_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation, + GQuark domain, + gint code, + const gchar *message); +void g_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation, + const GError *error); +void g_dbus_method_invocation_return_dbus_error (GDBusMethodInvocation *invocation, + const gchar *error_name, + const gchar *error_message); + +G_END_DECLS + +#endif /* __G_DBUS_METHOD_INVOCATION_H__ */ diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c new file mode 100644 index 000000000..30901651f --- /dev/null +++ b/gio/gdbusnameowning.c @@ -0,0 +1,713 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include + +#include "gdbusutils.h" +#include "gdbusnameowning.h" +#include "gdbuserror.h" +#include "gdbusprivate.h" +#include "gdbusconnection.h" + +/** + * SECTION:gdbusnameowning + * @title: Owning Bus Names + * @short_description: Simple API for owning bus names + * @include: gdbus/gdbus.h + * + * Convenience API for owning bus names. + * + * Simple application owning a nameFIXME: MISSING XINCLUDE CONTENT + */ + +G_LOCK_DEFINE_STATIC (lock); + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef enum +{ + PREVIOUS_CALL_NONE = 0, + PREVIOUS_CALL_ACQUIRED, + PREVIOUS_CALL_LOST, +} PreviousCall; + +typedef struct +{ + volatile gint ref_count; + guint id; + GBusNameOwnerFlags flags; + gchar *name; + GBusAcquiredCallback bus_acquired_handler; + GBusNameAcquiredCallback name_acquired_handler; + GBusNameLostCallback name_lost_handler; + gpointer user_data; + GDestroyNotify user_data_free_func; + GMainContext *main_context; + + PreviousCall previous_call; + + GDBusConnection *connection; + gulong disconnected_signal_handler_id; + guint name_acquired_subscription_id; + guint name_lost_subscription_id; + + gboolean cancelled; + + gboolean needs_release; +} Client; + +static guint next_global_id = 1; +static GHashTable *map_id_to_client = NULL; + + +static Client * +client_ref (Client *client) +{ + g_atomic_int_inc (&client->ref_count); + return client; +} + +static void +client_unref (Client *client) +{ + if (g_atomic_int_dec_and_test (&client->ref_count)) + { + if (client->connection != NULL) + { + if (client->disconnected_signal_handler_id > 0) + g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id); + if (client->name_acquired_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id); + if (client->name_lost_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id); + g_object_unref (client->connection); + } + if (client->main_context != NULL) + g_main_context_unref (client->main_context); + g_free (client->name); + if (client->user_data_free_func != NULL) + client->user_data_free_func (client->user_data); + g_free (client); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + + +typedef enum +{ + CALL_TYPE_NAME_ACQUIRED, + CALL_TYPE_NAME_LOST +} CallType; + +typedef struct +{ + Client *client; + + /* keep this separate because client->connection may + * be set to NULL after scheduling the call + */ + GDBusConnection *connection; + + /* set to TRUE to call acquired */ + CallType call_type; +} CallHandlerData; + +static void +call_handler_data_free (CallHandlerData *data) +{ + if (data->connection != NULL) + g_object_unref (data->connection); + client_unref (data->client); + g_free (data); +} + +static void +actually_do_call (Client *client, GDBusConnection *connection, CallType call_type) +{ + switch (call_type) + { + case CALL_TYPE_NAME_ACQUIRED: + if (client->name_acquired_handler != NULL) + { + client->name_acquired_handler (connection, + client->name, + client->user_data); + } + break; + + case CALL_TYPE_NAME_LOST: + if (client->name_lost_handler != NULL) + { + client->name_lost_handler (connection, + client->name, + client->user_data); + } + break; + + default: + g_assert_not_reached (); + break; + } +} + +static gboolean +call_in_idle_cb (gpointer _data) +{ + CallHandlerData *data = _data; + actually_do_call (data->client, data->connection, data->call_type); + return FALSE; +} + +static void +schedule_call_in_idle (Client *client, CallType call_type) +{ + CallHandlerData *data; + GSource *idle_source; + + data = g_new0 (CallHandlerData, 1); + data->client = client_ref (client); + data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL; + data->call_type = call_type; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_HIGH); + g_source_set_callback (idle_source, + call_in_idle_cb, + data, + (GDestroyNotify) call_handler_data_free); + g_source_attach (idle_source, client->main_context); + g_source_unref (idle_source); +} + +static void +do_call (Client *client, CallType call_type) +{ + /* only schedule in idle if we're not in the right thread */ + if (g_main_context_get_thread_default () != client->main_context) + schedule_call_in_idle (client, call_type); + else + actually_do_call (client, client->connection, call_type); +} + +static void +call_acquired_handler (Client *client) +{ + if (client->previous_call != PREVIOUS_CALL_ACQUIRED) + { + client->previous_call = PREVIOUS_CALL_ACQUIRED; + if (!client->cancelled) + { + do_call (client, CALL_TYPE_NAME_ACQUIRED); + } + } +} + +static void +call_lost_handler (Client *client) +{ + if (client->previous_call != PREVIOUS_CALL_LOST) + { + client->previous_call = PREVIOUS_CALL_LOST; + if (!client->cancelled) + { + do_call (client, CALL_TYPE_NAME_LOST); + } + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_name_lost_or_acquired (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + Client *client = user_data; + const gchar *name; + + if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 || + g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 || + g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0) + goto out; + + if (g_strcmp0 (signal_name, "NameLost") == 0) + { + g_variant_get (parameters, "(s)", &name); + if (g_strcmp0 (name, client->name) == 0) + { + call_lost_handler (client); + } + } + else if (g_strcmp0 (signal_name, "NameAcquired") == 0) + { + g_variant_get (parameters, "(s)", &name); + if (g_strcmp0 (name, client->name) == 0) + { + call_acquired_handler (client); + } + } + out: + ; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +request_name_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Client *client = user_data; + GVariant *result; + guint32 request_name_reply; + gboolean subscribe; + + request_name_reply = 0; + result = NULL; + + result = g_dbus_connection_invoke_method_finish (client->connection, + res, + NULL); + if (result != NULL) + { + g_variant_get (result, "(u)", &request_name_reply); + g_variant_unref (result); + } + + subscribe = FALSE; + + switch (request_name_reply) + { + case 1: /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */ + /* We got the name - now listen for NameLost and NameAcquired */ + call_acquired_handler (client); + subscribe = TRUE; + client->needs_release = TRUE; + break; + + case 2: /* DBUS_REQUEST_NAME_REPLY_IN_QUEUE */ + /* Waiting in line - listen for NameLost and NameAcquired */ + call_lost_handler (client); + subscribe = TRUE; + client->needs_release = TRUE; + break; + + default: + /* assume we couldn't get the name - explicit fallthrough */ + case 3: /* DBUS_REQUEST_NAME_REPLY_EXISTS */ + case 4: /* DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER */ + /* Some other part of the process is already owning the name */ + call_lost_handler (client); + break; + } + + if (subscribe) + { + /* start listening to NameLost and NameAcquired messages */ + client->name_lost_subscription_id = + g_dbus_connection_signal_subscribe (client->connection, + "org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameLost", + "/org/freedesktop/DBus", + client->name, + on_name_lost_or_acquired, + client, + NULL); + client->name_acquired_subscription_id = + g_dbus_connection_signal_subscribe (client->connection, + "org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameAcquired", + "/org/freedesktop/DBus", + client->name, + on_name_lost_or_acquired, + client, + NULL); + } + + client_unref (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_connection_disconnected (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) +{ + Client *client = user_data; + + if (client->disconnected_signal_handler_id > 0) + g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id); + if (client->name_acquired_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id); + if (client->name_lost_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id); + g_object_unref (client->connection); + client->disconnected_signal_handler_id = 0; + client->name_acquired_subscription_id = 0; + client->name_lost_subscription_id = 0; + client->connection = NULL; + + call_lost_handler (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +has_connection (Client *client) +{ + /* listen for disconnection */ + client->disconnected_signal_handler_id = g_signal_connect (client->connection, + "closed", + G_CALLBACK (on_connection_disconnected), + client); + + /* attempt to acquire the name */ + g_dbus_connection_invoke_method (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "RequestName", /* method name */ + g_variant_new ("(su)", + client->name, + client->flags), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) request_name_cb, + client_ref (client)); +} + + +static void +connection_get_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Client *client = user_data; + + client->connection = g_bus_get_finish (res, NULL); + if (client->connection == NULL) + { + call_lost_handler (client); + goto out; + } + + /* No need to schedule this in idle as we're already in the thread + * that the user called g_bus_own_name() from. This is because + * g_bus_get() guarantees that. + * + * Also, we need to ensure that the handler is invoked *before* + * we call RequestName(). Otherwise there is a race. + */ + if (client->bus_acquired_handler != NULL) + { + client->bus_acquired_handler (client->connection, + client->name, + client->user_data); + } + + has_connection (client); + + out: + client_unref (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_bus_own_name_on_connection: + * @connection: A #GDBusConnection that is not closed. + * @name: The well-known name to own. + * @flags: A set of flags from the #GBusNameOwnerFlags enumeration. + * @name_acquired_handler: Handler to invoke when @name is acquired or %NULL. + * @name_lost_handler: Handler to invoke when @name is lost or %NULL. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Like g_bus_own_name() but takes a #GDBusConnection instead of a + * #GBusType. + * + * Returns: An identifier (never 0) that an be used with + * g_bus_unown_name() to stop owning the name. + **/ +guint +g_bus_own_name_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameOwnerFlags flags, + GBusNameAcquiredCallback name_acquired_handler, + GBusNameLostCallback name_lost_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); + g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->ref_count = 1; + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->flags = flags; + client->name_acquired_handler = name_acquired_handler; + client->name_lost_handler = name_lost_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + + client->connection = g_object_ref (connection); + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + G_UNLOCK (lock); + + has_connection (client); + + return client->id; +} + +/** + * g_bus_own_name: + * @bus_type: The type of bus to own a name on. + * @name: The well-known name to own. + * @flags: A set of flags from the #GBusNameOwnerFlags enumeration. + * @bus_acquired_handler: Handler to invoke when connected to the bus of type @bus_type or %NULL. + * @name_acquired_handler: Handler to invoke when @name is acquired or %NULL. + * @name_lost_handler: Handler to invoke when @name is lost or %NULL. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Starts acquiring @name on the bus specified by @bus_type and calls + * @name_acquired_handler and @name_lost_handler when the name is + * acquired respectively lost. Callbacks will be invoked in the thread-default main + * loop of the thread you are calling this function from. + * + * You are guaranteed that one of the @name_acquired_handler and @name_lost_handler + * callbacks will be invoked after calling this function - there are three + * possible cases: + * + * + * @name_lost_handler with a %NULL connection (if a connection to the bus can't be made). + * + * + * @bus_acquired_handler then @name_lost_handler (if the name can't be obtained) + * + * + * @bus_acquired_handler then @name_acquired_handler (if the name was obtained). + * + * + * When you are done owning the name, just call g_bus_unown_name() + * with the owner id this function returns. + * + * If the name is acquired or lost (for example another application + * could acquire the name if you allow replacement or the application + * currently owning the name exits), the handlers are also invoked. If the + * #GDBusConnection that is used for attempting to own the name + * closes, then @name_lost_handler is invoked since it is no + * longer possible for other processes to access the process. + * + * You cannot use g_bus_own_name() several times (unless interleaved + * with calls to g_bus_unown_name()) - only the first call will work. + * + * Another guarantee is that invocations of @name_acquired_handler + * and @name_lost_handler are guaranteed to alternate; that + * is, if @name_acquired_handler is invoked then you are + * guaranteed that the next time one of the handlers is invoked, it + * will be @name_lost_handler. The reverse is also true. + * + * If you plan on exporting objects (using e.g. g_dbus_connection_register_object()), note + * that it is generally too late to export the objects in @name_acquired_handler. Instead, + * you can do this in @bus_acquired_handler since you are guaranteed that this will + * run before @name is requested from the bus. + * + * This behavior makes it very simple to write applications that wants + * to own names and export objects, see . Simply + * register objects to be exported in @bus_acquired_handler and + * unregister the objects (if any) in @name_lost_handler. + * + * Returns: An identifier (never 0) that an be used with + * g_bus_unown_name() to stop owning the name. + **/ +guint +g_bus_own_name (GBusType bus_type, + const gchar *name, + GBusNameOwnerFlags flags, + GBusAcquiredCallback bus_acquired_handler, + GBusNameAcquiredCallback name_acquired_handler, + GBusNameLostCallback name_lost_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0); + g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->ref_count = 1; + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->flags = flags; + client->bus_acquired_handler = bus_acquired_handler; + client->name_acquired_handler = name_acquired_handler; + client->name_lost_handler = name_lost_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + g_bus_get (bus_type, + NULL, + connection_get_cb, + client_ref (client)); + + G_UNLOCK (lock); + + return client->id; +} + +/** + * g_bus_unown_name: + * @owner_id: An identifier obtained from g_bus_own_name() + * + * Stops owning a name. + */ +void +g_bus_unown_name (guint owner_id) +{ + Client *client; + + g_return_if_fail (owner_id > 0); + + client = NULL; + + G_LOCK (lock); + if (owner_id == 0 || map_id_to_client == NULL || + (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (owner_id))) == NULL) + { + g_warning ("Invalid id %d passed to g_bus_unown_name()", owner_id); + goto out; + } + + client->cancelled = TRUE; + g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (owner_id))); + + out: + G_UNLOCK (lock); + + /* do callback without holding lock */ + if (client != NULL) + { + /* Release the name if needed */ + if (client->needs_release && client->connection != NULL) + { + GVariant *result; + GError *error; + guint32 release_name_reply; + + /* TODO: it kinda sucks having to do a sync call to release the name - but if + * we don't, then a subsequent grab of the name will make the bus daemon return + * IN_QUEUE which will trigger name_lost(). + * + * I believe this is a bug in the bus daemon. + */ + error = NULL; + result = g_dbus_connection_invoke_method_sync (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "ReleaseName", /* method name */ + g_variant_new ("(s)", client->name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + if (result == NULL) + { + g_warning ("Error releasing name %s: %s", client->name, error->message); + g_error_free (error); + } + else + { + g_variant_get (result, "(u)", &release_name_reply); + if (release_name_reply != 1 /* DBUS_RELEASE_NAME_REPLY_RELEASED */) + { + g_warning ("Unexpected reply %d when releasing name %s", release_name_reply, client->name); + } + g_variant_unref (result); + } + } + + if (client->disconnected_signal_handler_id > 0) + g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id); + if (client->name_acquired_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id); + if (client->name_lost_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id); + client->disconnected_signal_handler_id = 0; + client->name_acquired_subscription_id = 0; + client->name_lost_subscription_id = 0; + if (client->connection != NULL) + { + g_object_unref (client->connection); + client->connection = NULL; + } + + client_unref (client); + } +} diff --git a/gio/gdbusnameowning.h b/gio/gdbusnameowning.h new file mode 100644 index 000000000..fc063e036 --- /dev/null +++ b/gio/gdbusnameowning.h @@ -0,0 +1,88 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_NAME_OWNING_H__ +#define __G_DBUS_NAME_OWNING_H__ + +#include + +G_BEGIN_DECLS + +/** + * GBusAcquiredCallback: + * @connection: The #GDBusConnection to a message bus. + * @name: The name that is requested to be owned. + * @user_data: User data passed to g_bus_own_name(). + * + * Invoked when a connection to a message bus has been obtained. + */ +typedef void (*GBusAcquiredCallback) (GDBusConnection *connection, + const gchar *name, + gpointer user_data); + +/** + * GBusNameAcquiredCallback: + * @connection: The #GDBusConnection on which to acquired the name. + * @name: The name being owned. + * @user_data: User data passed to g_bus_own_name() or g_bus_own_name_on_connection(). + * + * Invoked when the name is acquired. + */ +typedef void (*GBusNameAcquiredCallback) (GDBusConnection *connection, + const gchar *name, + gpointer user_data); + +/** + * GBusNameLostCallback: + * @connection: The #GDBusConnection on which to acquire the name or %NULL if + * the connection was disconnected. + * @name: The name being owned. + * @user_data: User data passed to g_bus_own_name() or g_bus_own_name_on_connection(). + * + * Invoked when the name is lost or @connection has been closed. + */ +typedef void (*GBusNameLostCallback) (GDBusConnection *connection, + const gchar *name, + gpointer user_data); + +guint g_bus_own_name (GBusType bus_type, + const gchar *name, + GBusNameOwnerFlags flags, + GBusAcquiredCallback bus_acquired_handler, + GBusNameAcquiredCallback name_acquired_handler, + GBusNameLostCallback name_lost_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); + +guint g_bus_own_name_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameOwnerFlags flags, + GBusNameAcquiredCallback name_acquired_handler, + GBusNameLostCallback name_lost_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); + +void g_bus_unown_name (guint owner_id); + +G_END_DECLS + +#endif /* __G_DBUS_NAME_OWNING_H__ */ diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c new file mode 100644 index 000000000..92e04cc18 --- /dev/null +++ b/gio/gdbusnamewatching.c @@ -0,0 +1,620 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include + +#include "gdbusutils.h" +#include "gdbusnamewatching.h" +#include "gdbuserror.h" +#include "gdbusprivate.h" +#include "gdbusconnection.h" + +/** + * SECTION:gdbusnamewatching + * @title: Watching Bus Names + * @short_description: Simple API for watching bus names + * @include: gdbus/gdbus.h + * + * Convenience API for watching bus names. + * + * Simple application watching a nameFIXME: MISSING XINCLUDE CONTENT + */ + +G_LOCK_DEFINE_STATIC (lock); + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef enum +{ + PREVIOUS_CALL_NONE = 0, + PREVIOUS_CALL_APPEARED, + PREVIOUS_CALL_VANISHED, +} PreviousCall; + +typedef struct +{ + volatile gint ref_count; + guint id; + gchar *name; + GBusNameWatcherFlags flags; + gchar *name_owner; + GBusNameAppearedCallback name_appeared_handler; + GBusNameVanishedCallback name_vanished_handler; + gpointer user_data; + GDestroyNotify user_data_free_func; + GMainContext *main_context; + + GDBusConnection *connection; + gulong disconnected_signal_handler_id; + guint name_owner_changed_subscription_id; + + PreviousCall previous_call; + + gboolean cancelled; + gboolean initialized; +} Client; + +static guint next_global_id = 1; +static GHashTable *map_id_to_client = NULL; + +static Client * +client_ref (Client *client) +{ + g_atomic_int_inc (&client->ref_count); + return client; +} + +static void +client_unref (Client *client) +{ + if (g_atomic_int_dec_and_test (&client->ref_count)) + { + if (client->connection != NULL) + { + if (client->name_owner_changed_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_owner_changed_subscription_id); + if (client->disconnected_signal_handler_id > 0) + g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id); + g_object_unref (client->connection); + } + g_free (client->name); + g_free (client->name_owner); + if (client->main_context != NULL) + g_main_context_unref (client->main_context); + if (client->user_data_free_func != NULL) + client->user_data_free_func (client->user_data); + g_free (client); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef enum +{ + CALL_TYPE_NAME_APPEARED, + CALL_TYPE_NAME_VANISHED +} CallType; + +typedef struct +{ + Client *client; + + /* keep this separate because client->connection may + * be set to NULL after scheduling the call + */ + GDBusConnection *connection; + + /* ditto */ + gchar *name_owner; + + CallType call_type; +} CallHandlerData; + +static void +call_handler_data_free (CallHandlerData *data) +{ + if (data->connection != NULL) + g_object_unref (data->connection); + g_free (data->name_owner); + client_unref (data->client); + g_free (data); +} + +static void +actually_do_call (Client *client, GDBusConnection *connection, const gchar *name_owner, CallType call_type) +{ + switch (call_type) + { + case CALL_TYPE_NAME_APPEARED: + if (client->name_appeared_handler != NULL) + { + client->name_appeared_handler (connection, + client->name, + name_owner, + client->user_data); + } + break; + + case CALL_TYPE_NAME_VANISHED: + if (client->name_vanished_handler != NULL) + { + client->name_vanished_handler (connection, + client->name, + client->user_data); + } + break; + + default: + g_assert_not_reached (); + break; + } +} + +static gboolean +call_in_idle_cb (gpointer _data) +{ + CallHandlerData *data = _data; + actually_do_call (data->client, data->connection, data->name_owner, data->call_type); + return FALSE; +} + +static void +schedule_call_in_idle (Client *client, CallType call_type) +{ + CallHandlerData *data; + GSource *idle_source; + + data = g_new0 (CallHandlerData, 1); + data->client = client_ref (client); + data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL; + data->name_owner = g_strdup (client->name_owner); + data->call_type = call_type; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_HIGH); + g_source_set_callback (idle_source, + call_in_idle_cb, + data, + (GDestroyNotify) call_handler_data_free); + g_source_attach (idle_source, client->main_context); + g_source_unref (idle_source); +} + +static void +do_call (Client *client, CallType call_type) +{ + /* only schedule in idle if we're not in the right thread */ + if (g_main_context_get_thread_default () != client->main_context) + schedule_call_in_idle (client, call_type); + else + actually_do_call (client, client->connection, client->name_owner, call_type); +} + +static void +call_appeared_handler (Client *client) +{ + if (client->previous_call != PREVIOUS_CALL_APPEARED) + { + client->previous_call = PREVIOUS_CALL_APPEARED; + if (!client->cancelled && client->name_appeared_handler != NULL) + { + do_call (client, CALL_TYPE_NAME_APPEARED); + } + } +} + +static void +call_vanished_handler (Client *client, + gboolean ignore_cancelled) +{ + if (client->previous_call != PREVIOUS_CALL_VANISHED) + { + client->previous_call = PREVIOUS_CALL_VANISHED; + if (((!client->cancelled) || ignore_cancelled) && client->name_vanished_handler != NULL) + { + do_call (client, CALL_TYPE_NAME_VANISHED); + } + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_connection_disconnected (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) +{ + Client *client = user_data; + + if (client->name_owner_changed_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (client->connection, client->name_owner_changed_subscription_id); + if (client->disconnected_signal_handler_id > 0) + g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id); + g_object_unref (client->connection); + client->disconnected_signal_handler_id = 0; + client->name_owner_changed_subscription_id = 0; + client->connection = NULL; + + call_vanished_handler (client, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_name_owner_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + Client *client = user_data; + const gchar *name; + const gchar *old_owner; + const gchar *new_owner; + + if (!client->initialized) + goto out; + + if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 || + g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 || + g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0) + goto out; + + g_variant_get (parameters, + "(sss)", + &name, + &old_owner, + &new_owner); + + /* we only care about a specific name */ + if (g_strcmp0 (name, client->name) != 0) + goto out; + + if ((old_owner != NULL && strlen (old_owner) > 0) && client->name_owner != NULL) + { + g_free (client->name_owner); + client->name_owner = NULL; + call_vanished_handler (client, FALSE); + } + + if (new_owner != NULL && strlen (new_owner) > 0) + { + g_warn_if_fail (client->name_owner == NULL); + g_free (client->name_owner); + client->name_owner = g_strdup (new_owner); + call_appeared_handler (client); + } + + out: + ; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +get_name_owner_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Client *client = user_data; + GVariant *result; + const char *name_owner; + + name_owner = NULL; + result = NULL; + + result = g_dbus_connection_invoke_method_finish (client->connection, + res, + NULL); + if (result != NULL) + { + g_variant_get (result, "(s)", &name_owner); + } + + if (name_owner != NULL) + { + g_warn_if_fail (client->name_owner == NULL); + client->name_owner = g_strdup (name_owner); + call_appeared_handler (client); + } + else + { + call_vanished_handler (client, FALSE); + } + + client->initialized = TRUE; + + if (result != NULL) + g_variant_unref (result); + client_unref (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +invoke_get_name_owner (Client *client) +{ + g_dbus_connection_invoke_method (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetNameOwner", /* method name */ + g_variant_new ("(s)", client->name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) get_name_owner_cb, + client_ref (client)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +start_service_by_name_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Client *client = user_data; + GVariant *result; + + result = NULL; + + result = g_dbus_connection_invoke_method_finish (client->connection, + res, + NULL); + if (result != NULL) + { + guint32 start_service_result; + g_variant_get (result, "(u)", &start_service_result); + + if (start_service_result == 1) /* DBUS_START_REPLY_SUCCESS */ + { + invoke_get_name_owner (client); + } + else if (start_service_result == 2) /* DBUS_START_REPLY_ALREADY_RUNNING */ + { + invoke_get_name_owner (client); + } + else + { + g_warning ("Unexpected reply %d from StartServiceByName() method", start_service_result); + call_vanished_handler (client, FALSE); + client->initialized = TRUE; + } + } + else + { + /* Errors are not unexpected; the bus will reply e.g. + * + * org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2 + * was not provided by any .service files + * + * so just report vanished. + */ + call_vanished_handler (client, FALSE); + client->initialized = TRUE; + } + + if (result != NULL) + g_variant_unref (result); + client_unref (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +has_connection (Client *client) +{ + /* listen for disconnection */ + client->disconnected_signal_handler_id = g_signal_connect (client->connection, + "closed", + G_CALLBACK (on_connection_disconnected), + client); + + /* start listening to NameOwnerChanged messages immediately */ + client->name_owner_changed_subscription_id = g_dbus_connection_signal_subscribe (client->connection, + "org.freedesktop.DBus", /* name */ + "org.freedesktop.DBus", /* if */ + "NameOwnerChanged", /* signal */ + "/org/freedesktop/DBus", /* path */ + client->name, + on_name_owner_changed, + client, + NULL); + + if (client->flags & G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + g_dbus_connection_invoke_method (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "StartServiceByName", /* method name */ + g_variant_new ("(su)", client->name, 0), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) start_service_by_name_cb, + client_ref (client)); + } + else + { + /* check owner */ + invoke_get_name_owner (client); + } +} + + +static void +connection_get_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Client *client = user_data; + + client->connection = g_bus_get_finish (res, NULL); + if (client->connection == NULL) + { + call_vanished_handler (client, FALSE); + goto out; + } + + has_connection (client); + + out: + client_unref (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_bus_watch_name: + * @bus_type: The type of bus to watch a name on. + * @name: The name (well-known or unique) to watch. + * @flags: Flags from the #GBusNameWatcherFlags enumeration. + * @name_appeared_handler: Handler to invoke when @name is known to exist or %NULL. + * @name_vanished_handler: Handler to invoke when @name is known to not exist or %NULL. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Starts watching @name on the bus specified by @bus_type and calls + * @name_appeared_handler and @name_vanished_handler when the name is + * known to have a owner respectively known to lose its + * owner. Callbacks will be invoked in the thread-default main + * loop of the thread you are calling this function from. + * + * You are guaranteed that one of the handlers will be invoked after + * calling this function. When you are done watching the name, just + * call g_bus_unwatch_name() with the watcher id this function + * returns. + * + * If the name vanishes or appears (for example the application owning + * the name could restart), the handlers are also invoked. If the + * #GDBusConnection that is used for watching the name disconnects, then + * @name_vanished_handler is invoked since it is no longer + * possible to access the name. + * + * Another guarantee is that invocations of @name_appeared_handler + * and @name_vanished_handler are guaranteed to alternate; that + * is, if @name_appeared_handler is invoked then you are + * guaranteed that the next time one of the handlers is invoked, it + * will be @name_vanished_handler. The reverse is also true. + * + * This behavior makes it very simple to write applications that wants + * to take action when a certain name exists, see . Basically, the application + * should create object proxies in @name_appeared_handler and destroy + * them again (if any) in @name_vanished_handler. + * + * Returns: An identifier (never 0) that an be used with + * g_bus_unwatch_name() to stop watching the name. + **/ +guint +g_bus_watch_name (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0); + g_return_val_if_fail (g_dbus_is_name (name), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->ref_count = 1; + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->flags = flags; + client->name_appeared_handler = name_appeared_handler; + client->name_vanished_handler = name_vanished_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + g_bus_get (bus_type, + NULL, + connection_get_cb, + client_ref (client)); + + G_UNLOCK (lock); + + return client->id; +} + +/** + * g_bus_unwatch_name: + * @watcher_id: An identifier obtained from g_bus_watch_name() + * + * Stops watching a name. + **/ +void +g_bus_unwatch_name (guint watcher_id) +{ + Client *client; + + g_return_if_fail (watcher_id > 0); + + client = NULL; + + G_LOCK (lock); + if (watcher_id == 0 || + map_id_to_client == NULL || + (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL) + { + g_warning ("Invalid id %d passed to g_bus_unwatch_name()", watcher_id); + goto out; + } + + client->cancelled = TRUE; + g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id))); + + out: + G_UNLOCK (lock); + + /* do callback without holding lock */ + if (client != NULL) + { + client_unref (client); + } +} diff --git a/gio/gdbusnamewatching.h b/gio/gdbusnamewatching.h new file mode 100644 index 000000000..a724d4257 --- /dev/null +++ b/gio/gdbusnamewatching.h @@ -0,0 +1,68 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_NAME_WATCHING_H__ +#define __G_DBUS_NAME_WATCHING_H__ + +#include + +G_BEGIN_DECLS + +/** + * GBusNameAppearedCallback: + * @connection: The #GDBusConnection the name is being watched on. + * @name: The name being watched. + * @name_owner: Unique name of the owner of the name being watched. + * @user_data: User data passed to g_bus_watch_name(). + * + * Invoked when the name being watched is known to have to have a owner. + */ +typedef void (*GBusNameAppearedCallback) (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data); + +/** + * GBusNameVanishedCallback: + * @connection: The #GDBusConnection the name is being watched on. + * @name: The name being watched. + * @user_data: User data passed to g_bus_watch_name(). + * + * Invoked when the name being watched is known not to have to have a owner. + */ +typedef void (*GBusNameVanishedCallback) (GDBusConnection *connection, + const gchar *name, + gpointer user_data); + + +guint g_bus_watch_name (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_bus_unwatch_name (guint watcher_id); + +G_END_DECLS + +#endif /* __G_DBUS_NAME_WATCHING_H__ */ diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c new file mode 100644 index 000000000..adc6e12f3 --- /dev/null +++ b/gio/gdbusprivate.c @@ -0,0 +1,1040 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 +#include + +#include + +#ifdef G_OS_UNIX +#include +#include +#include "gunixcredentialsmessage.h" +#include +#endif + +#include "giotypes.h" +#include "gdbusprivate.h" +#include "gdbusmessage.h" +#include "gdbuserror.h" +#include "gdbusintrospection.h" + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +hexdump (const gchar *data, gsize len, guint indent) +{ + guint n, m; + GString *ret; + + ret = g_string_new (NULL); + + for (n = 0; n < len; n += 16) + { + g_string_append_printf (ret, "%*s%04x: ", indent, "", n); + + for (m = n; m < n + 16; m++) + { + if (m > n && (m%4) == 0) + g_string_append_c (ret, ' '); + if (m < len) + g_string_append_printf (ret, "%02x ", (guchar) data[m]); + else + g_string_append (ret, " "); + } + + g_string_append (ret, " "); + + for (m = n; m < len && m < n + 16; m++) + g_string_append_c (ret, g_ascii_isprint (data[m]) ? data[m] : '.'); + + g_string_append_c (ret, '\n'); + } + + return g_string_free (ret, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* Unfortunately ancillary messages are discarded when reading from a + * socket using the GSocketInputStream abstraction. So we provide a + * very GInputStream-ish API that uses GSocket in this case (very + * similar to GSocketInputStream). + */ + +typedef struct +{ + GSocket *socket; + GCancellable *cancellable; + + void *buffer; + gsize count; + + GSocketControlMessage ***messages; + gint *num_messages; + + GSimpleAsyncResult *simple; + + gboolean from_mainloop; +} ReadWithControlData; + +static void +read_with_control_data_free (ReadWithControlData *data) +{ + g_object_unref (data->socket); + if (data->cancellable != NULL) + g_object_unref (data->cancellable); + g_free (data); +} + +static gboolean +_g_socket_read_with_control_messages_ready (GSocket *socket, + GIOCondition condition, + gpointer user_data) +{ + ReadWithControlData *data = user_data; + GError *error; + gssize result; + GInputVector vector; + + error = NULL; + vector.buffer = data->buffer; + vector.size = data->count; + result = g_socket_receive_message (data->socket, + NULL, /* address */ + &vector, + 1, + data->messages, + data->num_messages, + NULL, + data->cancellable, + &error); + if (result >= 0) + { + g_simple_async_result_set_op_res_gssize (data->simple, result); + } + else + { + g_assert (error != NULL); + g_simple_async_result_set_from_error (data->simple, error); + g_error_free (error); + } + + if (data->from_mainloop) + g_simple_async_result_complete (data->simple); + else + g_simple_async_result_complete_in_idle (data->simple); + + return FALSE; +} + +static void +_g_socket_read_with_control_messages (GSocket *socket, + void *buffer, + gsize count, + GSocketControlMessage ***messages, + gint *num_messages, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ReadWithControlData *data; + + data = g_new0 (ReadWithControlData, 1); + data->socket = g_object_ref (socket); + data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL; + data->buffer = buffer; + data->count = count; + data->messages = messages; + data->num_messages = num_messages; + + data->simple = g_simple_async_result_new (G_OBJECT (socket), + callback, + user_data, + _g_socket_read_with_control_messages); + + if (!g_socket_condition_check (socket, G_IO_IN)) + { + GSource *source; + data->from_mainloop = TRUE; + source = g_socket_create_source (data->socket, + G_IO_IN | G_IO_HUP | G_IO_ERR, + cancellable); + g_source_set_callback (source, + (GSourceFunc) _g_socket_read_with_control_messages_ready, + data, + (GDestroyNotify) read_with_control_data_free); + g_source_attach (source, g_main_context_get_thread_default ()); + g_source_unref (source); + } + else + { + _g_socket_read_with_control_messages_ready (data->socket, G_IO_IN, data); + read_with_control_data_free (data); + } +} + +static gssize +_g_socket_read_with_control_messages_finish (GSocket *socket, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + g_return_val_if_fail (G_IS_SOCKET (socket), -1); + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _g_socket_read_with_control_messages); + + if (g_simple_async_result_propagate_error (simple, error)) + return -1; + else + return g_simple_async_result_get_op_res_gssize (simple); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +G_LOCK_DEFINE_STATIC (shared_thread_lock); + +typedef struct +{ + gint num_users; + GThread *thread; + GMainContext *context; + GMainLoop *loop; +} SharedThreadData; + +static SharedThreadData *shared_thread_data = NULL; + +static gpointer +shared_thread_func (gpointer data) +{ + g_main_context_push_thread_default (shared_thread_data->context); + g_main_loop_run (shared_thread_data->loop); + g_main_context_pop_thread_default (shared_thread_data->context); + return NULL; +} + +typedef void (*GDBusSharedThreadFunc) (gpointer user_data); + +typedef struct +{ + GDBusSharedThreadFunc func; + gpointer user_data; + gboolean done; +} CallerData; + +static gboolean +invoke_caller (gpointer user_data) +{ + CallerData *data = user_data; + data->func (data->user_data); + data->done = TRUE; + return FALSE; +} + +static void +_g_dbus_shared_thread_ref (GDBusSharedThreadFunc func, + gpointer user_data) +{ + GError *error; + GSource *idle_source; + CallerData *data; + + G_LOCK (shared_thread_lock); + + if (shared_thread_data != NULL) + { + shared_thread_data->num_users += 1; + goto have_thread; + } + + shared_thread_data = g_new0 (SharedThreadData, 1); + shared_thread_data->num_users = 1; + + error = NULL; + shared_thread_data->context = g_main_context_new (); + shared_thread_data->loop = g_main_loop_new (shared_thread_data->context, FALSE); + shared_thread_data->thread = g_thread_create (shared_thread_func, + NULL, + TRUE, + &error); + g_assert_no_error (error); + + have_thread: + + data = g_new0 (CallerData, 1); + data->func = func; + data->user_data = user_data; + data->done = FALSE; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + invoke_caller, + data, + NULL); + g_source_attach (idle_source, shared_thread_data->context); + g_source_unref (idle_source); + + /* wait for the user code to run.. hmm.. probably use a condition variable instead */ + while (!data->done) + g_thread_yield (); + + g_free (data); + + G_UNLOCK (shared_thread_lock); +} + +static void +_g_dbus_shared_thread_unref (void) +{ + /* TODO: actually destroy the shared thread here */ +#if 0 + G_LOCK (shared_thread_lock); + g_assert (shared_thread_data != NULL); + shared_thread_data->num_users -= 1; + if (shared_thread_data->num_users == 0) + { + g_main_loop_quit (shared_thread_data->loop); + //g_thread_join (shared_thread_data->thread); + g_main_loop_unref (shared_thread_data->loop); + g_main_context_unref (shared_thread_data->context); + g_free (shared_thread_data); + shared_thread_data = NULL; + G_UNLOCK (shared_thread_lock); + } + else + { + G_UNLOCK (shared_thread_lock); + } +#endif +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct GDBusWorker +{ + volatile gint ref_count; + gboolean stopped; + GIOStream *stream; + GDBusCapabilityFlags capabilities; + GCancellable *cancellable; + GDBusWorkerMessageReceivedCallback message_received_callback; + GDBusWorkerDisconnectedCallback disconnected_callback; + gpointer user_data; + + GThread *thread; + + /* if not NULL, stream is GSocketConnection */ + GSocket *socket; + + /* used for reading */ + GMutex *read_lock; + gchar *read_buffer; + gsize read_buffer_allocated_size; + gsize read_buffer_cur_size; + gsize read_buffer_bytes_wanted; + GUnixFDList *read_fd_list; + GSocketControlMessage **read_ancillary_messages; + gint read_num_ancillary_messages; + + /* used for writing */ + GMutex *write_lock; + GQueue *write_queue; + gboolean write_is_pending; +}; + +struct _MessageToWriteData ; +typedef struct _MessageToWriteData MessageToWriteData; + +static void message_to_write_data_free (MessageToWriteData *data); + +static GDBusWorker * +_g_dbus_worker_ref (GDBusWorker *worker) +{ + g_atomic_int_inc (&worker->ref_count); + return worker; +} + +static void +_g_dbus_worker_unref (GDBusWorker *worker) +{ + if (g_atomic_int_dec_and_test (&worker->ref_count)) + { + _g_dbus_shared_thread_unref (); + + g_object_unref (worker->stream); + + g_mutex_free (worker->read_lock); + g_object_unref (worker->cancellable); + if (worker->read_fd_list != NULL) + g_object_unref (worker->read_fd_list); + + g_mutex_free (worker->write_lock); + g_queue_foreach (worker->write_queue, + (GFunc) message_to_write_data_free, + NULL); + g_queue_free (worker->write_queue); + g_free (worker); + } +} + +static void +_g_dbus_worker_emit_disconnected (GDBusWorker *worker, + gboolean remote_peer_vanished, + GError *error) +{ + if (!worker->stopped) + worker->disconnected_callback (worker, remote_peer_vanished, error, worker->user_data); +} + +static void +_g_dbus_worker_emit_message (GDBusWorker *worker, + GDBusMessage *message) +{ + if (!worker->stopped) + worker->message_received_callback (worker, message, worker->user_data); +} + +static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker); + +/* called in private thread shared by all GDBusConnection instances (without read-lock held) */ +static void +_g_dbus_worker_do_read_cb (GInputStream *input_stream, + GAsyncResult *res, + gpointer user_data) +{ + GDBusWorker *worker = user_data; + GError *error; + gssize bytes_read; + + g_mutex_lock (worker->read_lock); + + /* If already stopped, don't even process the reply */ + if (worker->stopped) + goto out; + + error = NULL; + if (worker->socket == NULL) + bytes_read = g_input_stream_read_finish (g_io_stream_get_input_stream (worker->stream), + res, + &error); + else + bytes_read = _g_socket_read_with_control_messages_finish (worker->socket, + res, + &error); + if (worker->read_num_ancillary_messages > 0) + { + gint n; + for (n = 0; n < worker->read_num_ancillary_messages; n++) + { + GSocketControlMessage *control_message = G_SOCKET_CONTROL_MESSAGE (worker->read_ancillary_messages[n]); + + if (FALSE) + { + } +#ifdef G_OS_UNIX + else if (G_IS_UNIX_FD_MESSAGE (control_message)) + { + GUnixFDMessage *fd_message; + gint *fds; + gint num_fds; + + fd_message = G_UNIX_FD_MESSAGE (control_message); + fds = g_unix_fd_message_steal_fds (fd_message, &num_fds); + if (worker->read_fd_list == NULL) + { + worker->read_fd_list = g_unix_fd_list_new_from_array (fds, num_fds); + } + else + { + gint n; + for (n = 0; n < num_fds; n++) + { + /* TODO: really want a append_steal() */ + g_unix_fd_list_append (worker->read_fd_list, fds[n], NULL); + close (fds[n]); + } + } + g_free (fds); + } + else if (G_IS_UNIX_CREDENTIALS_MESSAGE (control_message)) + { + /* do nothing */ + } +#endif + else + { + if (error == NULL) + { + g_set_error (&error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unexpected ancillary message of type %s received from peer", + g_type_name (G_TYPE_FROM_INSTANCE (control_message))); + _g_dbus_worker_emit_disconnected (worker, TRUE, error); + g_error_free (error); + g_object_unref (control_message); + n++; + while (n < worker->read_num_ancillary_messages) + g_object_unref (worker->read_ancillary_messages[n++]); + g_free (worker->read_ancillary_messages); + goto out; + } + } + g_object_unref (control_message); + } + g_free (worker->read_ancillary_messages); + } + + if (bytes_read == -1) + { + _g_dbus_worker_emit_disconnected (worker, TRUE, error); + g_error_free (error); + goto out; + } + +#if 0 + g_debug ("read %d bytes (is_closed=%d blocking=%d condition=0x%02x) stream %p, %p", + (gint) bytes_read, + g_socket_is_closed (g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream))), + g_socket_get_blocking (g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream))), + g_socket_condition_check (g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream)), + G_IO_IN | G_IO_OUT | G_IO_HUP), + worker->stream, + worker); +#endif + + /* TODO: hmm, hmm... */ + if (bytes_read == 0) + { + g_set_error (&error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Underlying GIOStream returned 0 bytes on an async read"); + _g_dbus_worker_emit_disconnected (worker, TRUE, error); + g_error_free (error); + goto out; + } + + worker->read_buffer_cur_size += bytes_read; + if (worker->read_buffer_bytes_wanted == worker->read_buffer_cur_size) + { + /* OK, got what we asked for! */ + if (worker->read_buffer_bytes_wanted == 16) + { + gssize message_len; + /* OK, got the header - determine how many more bytes are needed */ + error = NULL; + message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer, + 16, + &error); + if (message_len == -1) + { + g_warning ("_g_dbus_worker_do_read_cb: error determing bytes needed: %s", error->message); + _g_dbus_worker_emit_disconnected (worker, FALSE, error); + g_error_free (error); + goto out; + } + + worker->read_buffer_bytes_wanted = message_len; + _g_dbus_worker_do_read_unlocked (worker); + } + else + { + GDBusMessage *message; + error = NULL; + + /* TODO: use connection->priv->auth to decode the message */ + + message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer, + worker->read_buffer_cur_size, + &error); + if (message == NULL) + { + _g_dbus_worker_emit_disconnected (worker, FALSE, error); + g_error_free (error); + goto out; + } + + if (worker->read_fd_list != NULL) + { + g_dbus_message_set_unix_fd_list (message, worker->read_fd_list); + worker->read_fd_list = NULL; + } + + if (G_UNLIKELY (_g_dbus_debug_message ())) + { + gchar *s; + g_print ("========================================================================\n" + "GDBus-debug:Message:\n" + " <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n", + worker->read_buffer_cur_size); + s = g_dbus_message_print (message, 2); + g_print ("%s", s); + g_free (s); + s = hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2); + g_print ("%s\n", s); + g_free (s); + } + + /* yay, got a message, go deliver it */ + _g_dbus_worker_emit_message (worker, message); + g_object_unref (message); + + /* start reading another message! */ + worker->read_buffer_bytes_wanted = 0; + worker->read_buffer_cur_size = 0; + _g_dbus_worker_do_read_unlocked (worker); + } + } + else + { + /* didn't get all the bytes we requested - so repeat the request... */ + _g_dbus_worker_do_read_unlocked (worker); + } + + out: + g_mutex_unlock (worker->read_lock); + + /* gives up the reference acquired when calling g_input_stream_read_async() */ + _g_dbus_worker_unref (worker); +} + +/* called in private thread shared by all GDBusConnection instances (with read-lock held) */ +static void +_g_dbus_worker_do_read_unlocked (GDBusWorker *worker) +{ + /* if bytes_wanted is zero, it means start reading a message */ + if (worker->read_buffer_bytes_wanted == 0) + { + worker->read_buffer_cur_size = 0; + worker->read_buffer_bytes_wanted = 16; + } + + /* ensure we have a (big enough) buffer */ + if (worker->read_buffer == NULL || worker->read_buffer_bytes_wanted > worker->read_buffer_allocated_size) + { + /* TODO: 4096 is randomly chosen; might want a better chosen default minimum */ + worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, 4096); + worker->read_buffer = g_realloc (worker->read_buffer, worker->read_buffer_allocated_size); + } + + if (worker->socket == NULL) + g_input_stream_read_async (g_io_stream_get_input_stream (worker->stream), + worker->read_buffer + worker->read_buffer_cur_size, + worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size, + G_PRIORITY_DEFAULT, + worker->cancellable, + (GAsyncReadyCallback) _g_dbus_worker_do_read_cb, + _g_dbus_worker_ref (worker)); + else + { + worker->read_ancillary_messages = NULL; + worker->read_num_ancillary_messages = 0; + _g_socket_read_with_control_messages (worker->socket, + worker->read_buffer + worker->read_buffer_cur_size, + worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size, + &worker->read_ancillary_messages, + &worker->read_num_ancillary_messages, + G_PRIORITY_DEFAULT, + worker->cancellable, + (GAsyncReadyCallback) _g_dbus_worker_do_read_cb, + _g_dbus_worker_ref (worker)); + } +} + +/* called in private thread shared by all GDBusConnection instances (without read-lock held) */ +static void +_g_dbus_worker_do_read (GDBusWorker *worker) +{ + g_mutex_lock (worker->read_lock); + _g_dbus_worker_do_read_unlocked (worker); + g_mutex_unlock (worker->read_lock); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct _MessageToWriteData +{ + GDBusMessage *message; + gchar *blob; + gsize blob_size; +}; + +static void +message_to_write_data_free (MessageToWriteData *data) +{ + g_object_unref (data->message); + g_free (data->blob); + g_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* called in private thread shared by all GDBusConnection instances (with write-lock held) */ +static gboolean +write_message (GDBusWorker *worker, + MessageToWriteData *data, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (data->blob_size > 16, FALSE); + + ret = FALSE; + + /* First, the initial 16 bytes - special case UNIX sockets here + * since it may involve writing an ancillary message with file + * descriptors + */ +#ifdef G_OS_UNIX + { + GOutputVector vector; + GSocketControlMessage *message; + GUnixFDList *fd_list; + gssize bytes_written; + + fd_list = g_dbus_message_get_unix_fd_list (data->message); + + message = NULL; + if (fd_list != NULL) + { + if (!G_IS_UNIX_CONNECTION (worker->stream)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Tried sending a file descriptor on unsupported stream of type %s", + g_type_name (G_TYPE_FROM_INSTANCE (worker->stream))); + goto out; + } + else if (!(worker->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Tried sending a file descriptor but remote peer does not support this capability"); + goto out; + } + message = g_unix_fd_message_new_with_fd_list (fd_list); + } + + vector.buffer = data->blob; + vector.size = 16; + + bytes_written = g_socket_send_message (worker->socket, + NULL, /* address */ + &vector, + 1, + message != NULL ? &message : NULL, + message != NULL ? 1 : 0, + G_SOCKET_MSG_NONE, + worker->cancellable, + error); + if (bytes_written == -1) + { + g_prefix_error (error, _("Error writing first 16 bytes of message to socket: ")); + if (message != NULL) + g_object_unref (message); + goto out; + } + if (message != NULL) + g_object_unref (message); + + if (bytes_written < 16) + { + /* TODO: I think this needs to be handled ... are we guaranteed that the ancillary + * messages are sent? + */ + g_assert_not_reached (); + } + } +#else + /* write the first 16 bytes (guaranteed to return an error if everything can't be written) */ + if (!g_output_stream_write_all (g_io_stream_get_output_stream (worker->stream), + (const gchar *) data->blob, + 16, + NULL, /* bytes_written */ + worker->cancellable, /* cancellable */ + error)) + goto out; +#endif + + /* Then write the rest of the message (guaranteed to return an error if everything can't be written) */ + if (!g_output_stream_write_all (g_io_stream_get_output_stream (worker->stream), + (const gchar *) data->blob + 16, + data->blob_size - 16, + NULL, /* bytes_written */ + worker->cancellable, /* cancellable */ + error)) + goto out; + + ret = TRUE; + + if (G_UNLIKELY (_g_dbus_debug_message ())) + { + gchar *s; + g_print ("========================================================================\n" + "GDBus-debug:Message:\n" + " >>>> SENT D-Bus message (%" G_GSIZE_FORMAT " bytes)\n", + data->blob_size); + s = g_dbus_message_print (data->message, 2); + g_print ("%s", s); + g_free (s); + s = hexdump (data->blob, data->blob_size, 2); + g_print ("%s\n", s); + g_free (s); + } + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* called in private thread shared by all GDBusConnection instances (without write-lock held) */ +static gboolean +write_message_in_idle_cb (gpointer user_data) +{ + GDBusWorker *worker = user_data; + gboolean more_writes_are_pending; + MessageToWriteData *data; + GError *error; + + g_mutex_lock (worker->write_lock); + + data = g_queue_pop_head (worker->write_queue); + g_assert (data != NULL); + + error = NULL; + if (!write_message (worker, + data, + &error)) + { + /* TODO: handle */ + _g_dbus_worker_emit_disconnected (worker, TRUE, error); + g_error_free (error); + } + message_to_write_data_free (data); + + more_writes_are_pending = (g_queue_get_length (worker->write_queue) > 0); + + worker->write_is_pending = more_writes_are_pending; + g_mutex_unlock (worker->write_lock); + + return more_writes_are_pending; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* can be called from any thread - steals blob */ +void +_g_dbus_worker_send_message (GDBusWorker *worker, + GDBusMessage *message, + gchar *blob, + gsize blob_len) +{ + MessageToWriteData *data; + + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail (blob != NULL); + g_return_if_fail (blob_len > 16); + + data = g_new0 (MessageToWriteData, 1); + data->message = g_object_ref (message); + data->blob = blob; /* steal! */ + data->blob_size = blob_len; + + g_mutex_lock (worker->write_lock); + g_queue_push_tail (worker->write_queue, data); + if (!worker->write_is_pending) + { + GSource *idle_source; + + worker->write_is_pending = TRUE; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + write_message_in_idle_cb, + _g_dbus_worker_ref (worker), + (GDestroyNotify) _g_dbus_worker_unref); + g_source_attach (idle_source, shared_thread_data->context); + g_source_unref (idle_source); + } + g_mutex_unlock (worker->write_lock); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +_g_dbus_worker_thread_begin_func (gpointer user_data) +{ + GDBusWorker *worker = user_data; + + worker->thread = g_thread_self (); + + /* begin reading */ + _g_dbus_worker_do_read (worker); +} + +GDBusWorker * +_g_dbus_worker_new (GIOStream *stream, + GDBusCapabilityFlags capabilities, + GDBusWorkerMessageReceivedCallback message_received_callback, + GDBusWorkerDisconnectedCallback disconnected_callback, + gpointer user_data) +{ + GDBusWorker *worker; + + g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL); + g_return_val_if_fail (message_received_callback != NULL, NULL); + g_return_val_if_fail (disconnected_callback != NULL, NULL); + + worker = g_new0 (GDBusWorker, 1); + worker->ref_count = 1; + + worker->read_lock = g_mutex_new (); + worker->message_received_callback = message_received_callback; + worker->disconnected_callback = disconnected_callback; + worker->user_data = user_data; + worker->stream = g_object_ref (stream); + worker->capabilities = capabilities; + worker->cancellable = g_cancellable_new (); + + worker->write_lock = g_mutex_new (); + worker->write_queue = g_queue_new (); + + if (G_IS_SOCKET_CONNECTION (worker->stream)) + worker->socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream)); + + _g_dbus_shared_thread_ref (_g_dbus_worker_thread_begin_func, worker); + + return worker; +} + +/* This can be called from any thread - frees worker - guarantees no callbacks + * will ever be issued again + */ +void +_g_dbus_worker_stop (GDBusWorker *worker) +{ + /* If we're called in the worker thread it means we are called from + * a worker callback. This is fine, we just can't lock in that case since + * we're already holding the lock... + */ + if (g_thread_self () != worker->thread) + g_mutex_lock (worker->read_lock); + worker->stopped = TRUE; + if (g_thread_self () != worker->thread) + g_mutex_unlock (worker->read_lock); + + g_cancellable_cancel (worker->cancellable); + _g_dbus_worker_unref (worker); +} + +#define G_DBUS_DEBUG_AUTHENTICATION (1<<0) +#define G_DBUS_DEBUG_MESSAGE (1<<1) +#define G_DBUS_DEBUG_ALL 0xffffffff +static gint _gdbus_debug_flags = 0; + +gboolean +_g_dbus_debug_authentication (void) +{ + _g_dbus_initialize (); + return (_gdbus_debug_flags & G_DBUS_DEBUG_AUTHENTICATION) != 0; +} + +gboolean +_g_dbus_debug_message (void) +{ + _g_dbus_initialize (); + return (_gdbus_debug_flags & G_DBUS_DEBUG_MESSAGE) != 0; +} + +/** + * _g_dbus_initialize: + * + * Does various one-time init things such as + * + * - registering the G_DBUS_ERROR error domain + * - parses the G_DBUS_DEBUG environment variable + */ +void +_g_dbus_initialize (void) +{ + static volatile gsize initialized = 0; + + if (g_once_init_enter (&initialized)) + { + volatile GQuark g_dbus_error_domain; + const gchar *debug; + + g_dbus_error_domain = G_DBUS_ERROR; + + debug = g_getenv ("G_DBUS_DEBUG"); + if (debug != NULL) + { + gchar **tokens; + guint n; + tokens = g_strsplit (debug, ",", 0); + for (n = 0; tokens[n] != NULL; n++) + { + if (g_strcmp0 (tokens[n], "authentication") == 0) + _gdbus_debug_flags |= G_DBUS_DEBUG_AUTHENTICATION; + else if (g_strcmp0 (tokens[n], "message") == 0) + _gdbus_debug_flags |= G_DBUS_DEBUG_MESSAGE; + else if (g_strcmp0 (tokens[n], "all") == 0) + _gdbus_debug_flags |= G_DBUS_DEBUG_ALL; + } + g_strfreev (tokens); + } + + g_once_init_leave (&initialized, 1); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +gchar * +_g_dbus_compute_complete_signature (GDBusArgInfo **args, + gboolean include_parentheses) +{ + GString *s; + guint n; + + if (include_parentheses) + s = g_string_new ("("); + else + s = g_string_new (""); + if (args != NULL) + for (n = 0; args[n] != NULL; n++) + g_string_append (s, args[n]->signature); + + if (include_parentheses) + g_string_append_c (s, ')'); + + return g_string_free (s, FALSE); +} diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h new file mode 100644 index 000000000..ef7fa0ac5 --- /dev/null +++ b/gio/gdbusprivate.h @@ -0,0 +1,83 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + */ + +#if !defined (GIO_COMPILATION) +#error "gdbusprivate.h is a private header file." +#endif + +#ifndef __G_DBUS_PRIVATE_H__ +#define __G_DBUS_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct GDBusWorker GDBusWorker; + +typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusWorker *worker, + GDBusMessage *message, + gpointer user_data); + +typedef void (*GDBusWorkerDisconnectedCallback) (GDBusWorker *worker, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data); + +/* This function may be called from any thread - callbacks will be in the shared private message thread + * and must not block. + */ +GDBusWorker *_g_dbus_worker_new (GIOStream *stream, + GDBusCapabilityFlags capabilities, + GDBusWorkerMessageReceivedCallback message_received_callback, + GDBusWorkerDisconnectedCallback disconnected_callback, + gpointer user_data); + +/* can be called from any thread - steals blob */ +void _g_dbus_worker_send_message (GDBusWorker *worker, + GDBusMessage *message, + gchar *blob, + gsize blob_len); + +/* can be called from any thread */ +void _g_dbus_worker_stop (GDBusWorker *worker); + +/* ---------------------------------------------------------------------------------------------------- */ + +void _g_dbus_initialize (void); +gboolean _g_dbus_debug_authentication (void); +gboolean _g_dbus_debug_message (void); + +gboolean _g_dbus_address_parse_entry (const gchar *address_entry, + gchar **out_transport_name, + GHashTable **out_key_value_pairs, + GError **error); + +gchar * _g_dbus_compute_complete_signature (GDBusArgInfo **args, + gboolean include_parentheses); + +/* ---------------------------------------------------------------------------------------------------- */ + +G_END_DECLS + +#endif /* __G_DBUS_PRIVATE_H__ */ diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c new file mode 100644 index 000000000..39094d0b4 --- /dev/null +++ b/gio/gdbusproxy.c @@ -0,0 +1,1542 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbusutils.h" +#include "gdbusproxy.h" +#include "gioenumtypes.h" +#include "gdbusconnection.h" +#include "gdbuserror.h" +#include "gdbusprivate.h" +#include "gio-marshal.h" +#include "ginitable.h" +#include "gasyncinitable.h" +#include "gioerror.h" +#include "gasyncresult.h" +#include "gsimpleasyncresult.h" + +/** + * SECTION:gdbusproxy + * @short_description: Base class for proxies + * @include: gdbus/gdbus.h + * + * #GDBusProxy is a base class used for proxies to access a D-Bus + * interface on a remote object. A #GDBusProxy can only be constructed + * for unique name bus and does not track whether the name + * vanishes. Use g_bus_watch_proxy() to construct #GDBusProxy proxies + * for owners of a well-known names. + */ + +struct _GDBusProxyPrivate +{ + GDBusConnection *connection; + GDBusProxyFlags flags; + gchar *unique_bus_name; + gchar *object_path; + gchar *interface_name; + gint timeout_msec; + + /* gchar* -> GVariant* */ + GHashTable *properties; + + GDBusInterfaceInfo *expected_interface; + + guint properties_changed_subscriber_id; + guint signals_subscriber_id; +}; + +enum +{ + PROP_0, + PROP_G_CONNECTION, + PROP_G_UNIQUE_BUS_NAME, + PROP_G_FLAGS, + PROP_G_OBJECT_PATH, + PROP_G_INTERFACE_NAME, + PROP_G_DEFAULT_TIMEOUT, + PROP_G_INTERFACE_INFO +}; + +enum +{ + PROPERTIES_CHANGED_SIGNAL, + SIGNAL_SIGNAL, + LAST_SIGNAL, +}; + +static void g_dbus_proxy_constructed (GObject *object); + +guint signals[LAST_SIGNAL] = {0}; + +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_INITABLE, initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) + ); + +static void +g_dbus_proxy_finalize (GObject *object) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + + if (proxy->priv->properties_changed_subscriber_id > 0) + { + g_dbus_connection_signal_unsubscribe (proxy->priv->connection, + proxy->priv->properties_changed_subscriber_id); + } + + if (proxy->priv->signals_subscriber_id > 0) + { + g_dbus_connection_signal_unsubscribe (proxy->priv->connection, + proxy->priv->signals_subscriber_id); + } + + g_object_unref (proxy->priv->connection); + g_free (proxy->priv->unique_bus_name); + g_free (proxy->priv->object_path); + g_free (proxy->priv->interface_name); + if (proxy->priv->properties != NULL) + g_hash_table_unref (proxy->priv->properties); + + if (proxy->priv->expected_interface != NULL) + g_dbus_interface_info_unref (proxy->priv->expected_interface); + + if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object); +} + +static void +g_dbus_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + + switch (prop_id) + { + case PROP_G_CONNECTION: + g_value_set_object (value, proxy->priv->connection); + break; + + case PROP_G_FLAGS: + g_value_set_flags (value, proxy->priv->flags); + break; + + case PROP_G_UNIQUE_BUS_NAME: + g_value_set_string (value, proxy->priv->unique_bus_name); + break; + + case PROP_G_OBJECT_PATH: + g_value_set_string (value, proxy->priv->object_path); + break; + + case PROP_G_INTERFACE_NAME: + g_value_set_string (value, proxy->priv->interface_name); + break; + + case PROP_G_DEFAULT_TIMEOUT: + g_value_set_int (value, proxy->priv->timeout_msec); + break; + + case PROP_G_INTERFACE_INFO: + g_value_set_boxed (value, g_dbus_proxy_get_interface_info (proxy)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + + switch (prop_id) + { + case PROP_G_CONNECTION: + proxy->priv->connection = g_value_dup_object (value); + break; + + case PROP_G_FLAGS: + proxy->priv->flags = g_value_get_flags (value); + break; + + case PROP_G_UNIQUE_BUS_NAME: + proxy->priv->unique_bus_name = g_value_dup_string (value); + break; + + case PROP_G_OBJECT_PATH: + proxy->priv->object_path = g_value_dup_string (value); + break; + + case PROP_G_INTERFACE_NAME: + proxy->priv->interface_name = g_value_dup_string (value); + break; + + case PROP_G_DEFAULT_TIMEOUT: + g_dbus_proxy_set_default_timeout (proxy, g_value_get_int (value)); + break; + + case PROP_G_INTERFACE_INFO: + g_dbus_proxy_set_interface_info (proxy, g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_proxy_class_init (GDBusProxyClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_proxy_finalize; + gobject_class->set_property = g_dbus_proxy_set_property; + gobject_class->get_property = g_dbus_proxy_get_property; + gobject_class->constructed = g_dbus_proxy_constructed; + + /* Note that all property names are prefixed to avoid collisions with D-Bus property names + * in derived classes */ + + /** + * GDBusProxy:g-interface-info: + * + * Ensure that interactions with this proxy conform to the given + * interface. For example, when completing a method call, if the + * type signature of the message isn't what's expected, the given + * #GError is set. Signals that have a type signature mismatch are + * simply dropped. + */ + g_object_class_install_property (gobject_class, + PROP_G_INTERFACE_INFO, + g_param_spec_boxed ("g-interface-info", + _("Interface Information"), + _("Interface Information"), + G_TYPE_DBUS_INTERFACE_INFO, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy:g-connection: + * + * The #GDBusConnection the proxy is for. + */ + g_object_class_install_property (gobject_class, + PROP_G_CONNECTION, + g_param_spec_object ("g-connection", + _("g-connection"), + _("The connection the proxy is for"), + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy:g-flags: + * + * Flags from the #GDBusProxyFlags enumeration. + */ + g_object_class_install_property (gobject_class, + PROP_G_FLAGS, + g_param_spec_flags ("g-flags", + _("g-flags"), + _("Flags for the proxy"), + G_TYPE_DBUS_PROXY_FLAGS, + G_DBUS_PROXY_FLAGS_NONE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy:g-unique-bus-name: + * + * The unique bus name the proxy is for. + */ + g_object_class_install_property (gobject_class, + PROP_G_UNIQUE_BUS_NAME, + g_param_spec_string ("g-unique-bus-name", + _("g-unique-bus-name"), + _("The unique bus name the proxy is for"), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy:g-object-path: + * + * The object path the proxy is for. + */ + g_object_class_install_property (gobject_class, + PROP_G_OBJECT_PATH, + g_param_spec_string ("g-object-path", + _("g-object-path"), + _("The object path the proxy is for"), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy:g-interface-name: + * + * The D-Bus interface name the proxy is for. + */ + g_object_class_install_property (gobject_class, + PROP_G_INTERFACE_NAME, + g_param_spec_string ("g-interface-name", + _("g-interface-name"), + _("The D-Bus interface name the proxy is for"), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy:g-default-timeout: + * + * The timeout to use if -1 (specifying default timeout) is passed + * as @timeout_msec in the g_dbus_proxy_invoke_method() and + * g_dbus_proxy_invoke_method_sync() functions. + * + * This allows applications to set a proxy-wide timeout for all + * remote method invocations on the proxy. If this property is -1, + * the default timeout (typically 25 seconds) is used. If set to + * %G_MAXINT, then no timeout is used. + */ + g_object_class_install_property (gobject_class, + PROP_G_DEFAULT_TIMEOUT, + g_param_spec_int ("g-default-timeout", + _("Default Timeout"), + _("Timeout for remote method invocation"), + -1, + G_MAXINT, + -1, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusProxy::g-properties-changed: + * @proxy: The #GDBusProxy emitting the signal. + * @changed_properties: A #GHashTable containing the properties that changed. + * + * Emitted when one or more D-Bus properties on @proxy changes. The cached properties + * are already replaced when this signal fires. + */ + signals[PROPERTIES_CHANGED_SIGNAL] = g_signal_new ("g-properties-changed", + G_TYPE_DBUS_PROXY, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusProxyClass, g_properties_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + 1, + G_TYPE_HASH_TABLE); + + /** + * GDBusProxy::g-signal: + * @proxy: The #GDBusProxy emitting the signal. + * @sender_name: The sender of the signal or %NULL if the connection is not a bus connection. + * @signal_name: The name of the signal. + * @parameters: A #GVariant tuple with parameters for the signal. + * + * Emitted when a signal from the remote object and interface that @proxy is for, has been received. + **/ + signals[SIGNAL_SIGNAL] = g_signal_new ("g-signal", + G_TYPE_DBUS_PROXY, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusProxyClass, g_signal), + NULL, + NULL, + _gio_marshal_VOID__STRING_STRING_BOXED, + G_TYPE_NONE, + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_VARIANT); + + + g_type_class_add_private (klass, sizeof (GDBusProxyPrivate)); +} + +static void +g_dbus_proxy_init (GDBusProxy *proxy) +{ + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, G_TYPE_DBUS_PROXY, GDBusProxyPrivate); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_proxy_get_cached_property_names: + * @proxy: A #GDBusProxy. + * @error: Return location for error or %NULL. + * + * Gets the names of all cached properties on @proxy. + * + * Returns: A %NULL-terminated array of strings or %NULL if @error is set. Free with + * g_strfreev(). + */ +gchar ** +g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, + GError **error) +{ + gchar **names; + GPtrArray *p; + GHashTableIter iter; + const gchar *key; + + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + names = NULL; + + if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Properties are not available (proxy created with G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)")); + goto out; + } + + p = g_ptr_array_new (); + + g_hash_table_iter_init (&iter, proxy->priv->properties); + while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) + { + g_ptr_array_add (p, g_strdup (key)); + } + g_ptr_array_sort (p, (GCompareFunc) g_strcmp0); + g_ptr_array_add (p, NULL); + + names = (gchar **) g_ptr_array_free (p, FALSE); + + out: + return names; +} + +/** + * g_dbus_proxy_get_cached_property: + * @proxy: A #GDBusProxy. + * @property_name: Property name. + * @error: Return location for error or %NULL. + * + * Looks up the value for a property from the cache. This call does no blocking IO. + * + * Normally you will not need to modify the returned variant since it is updated automatically + * in response to org.freedesktop.DBus.Properties.PropertiesChanged + * D-Bus signals (which also causes #GDBusProxy::g-properties-changed to be emitted). + * + * However, for properties for which said D-Bus signal is not emitted, you + * can catch other signals and modify the returned variant accordingly (remember to emit + * #GDBusProxy::g-properties-changed yourself). + * + * Returns: A reference to the #GVariant instance that holds the value for @property_name or + * %NULL if @error is set. Free the reference with g_variant_unref(). + */ +GVariant * +g_dbus_proxy_get_cached_property (GDBusProxy *proxy, + const gchar *property_name, + GError **error) +{ + GVariant *value; + + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + + value = NULL; + + if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Properties are not available (proxy created with G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)")); + goto out; + } + + value = g_hash_table_lookup (proxy->priv->properties, property_name); + if (value == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("No property with name %s"), + property_name); + goto out; + } + + g_variant_ref (value); + + out: + + return value; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_signal_received (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (user_data); + + g_signal_emit (proxy, + signals[SIGNAL_SIGNAL], + 0, + sender_name, + signal_name, + parameters); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_properties_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (user_data); + GError *error; + const gchar *interface_name_for_signal; + GVariantIter *iter; + GVariant *item; + GHashTable *changed_properties; + + error = NULL; + iter = NULL; + +#if 0 // TODO! + /* Ignore this signal if properties are not yet available + * + * (can happen in the window between subscribing to PropertiesChanged() and until + * org.freedesktop.DBus.Properties.GetAll() returns) + */ + if (!proxy->priv->properties_available) + goto out; +#endif + + if (strcmp (g_variant_get_type_string (parameters), "(sa{sv})") != 0) + { + g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv})'", + g_variant_get_type_string (parameters)); + goto out; + } + + g_variant_get (parameters, + "(sa{sv})", + &interface_name_for_signal, + &iter); + + if (g_strcmp0 (interface_name_for_signal, proxy->priv->interface_name) != 0) + goto out; + + changed_properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); + + while ((item = g_variant_iter_next_value (iter))) + { + const gchar *key; + GVariant *value; + + g_variant_get (item, + "{sv}", + &key, + &value); + + g_hash_table_insert (proxy->priv->properties, + g_strdup (key), + value); /* steals value */ + + g_hash_table_insert (changed_properties, + g_strdup (key), + g_variant_ref (value)); + } + + + /* emit signal */ + g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL], 0, changed_properties); + + g_hash_table_unref (changed_properties); + + out: + if (iter != NULL) + g_variant_iter_free (iter); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_dbus_proxy_constructed (GObject *object) +{ + if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed != NULL) + G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed (object); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +subscribe_to_signals (GDBusProxy *proxy) +{ + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + { + /* subscribe to PropertiesChanged() */ + proxy->priv->properties_changed_subscriber_id = + g_dbus_connection_signal_subscribe (proxy->priv->connection, + proxy->priv->unique_bus_name, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + proxy->priv->object_path, + proxy->priv->interface_name, + on_properties_changed, + proxy, + NULL); + } + + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS)) + { + /* subscribe to all signals for the object */ + proxy->priv->signals_subscriber_id = + g_dbus_connection_signal_subscribe (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->interface_name, + NULL, /* member */ + proxy->priv->object_path, + NULL, /* arg0 */ + on_signal_received, + proxy, + NULL); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +process_get_all_reply (GDBusProxy *proxy, + GVariant *result) +{ + GVariantIter iter; + GVariant *item; + + if (strcmp (g_variant_get_type_string (result), "(a{sv})") != 0) + { + g_warning ("Value for GetAll reply with type `%s' does not match `(a{sv})'", + g_variant_get_type_string (result)); + goto out; + } + + proxy->priv->properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); + + g_variant_iter_init (&iter, g_variant_get_child_value (result, 0)); + while ((item = g_variant_iter_next_value (&iter)) != NULL) + { + const gchar *key; + GVariant *value; + + g_variant_get (item, + "{sv}", + &key, + &value); + //g_print ("got %s -> %s\n", key, g_variant_markup_print (value, FALSE, 0, 0)); + + g_hash_table_insert (proxy->priv->properties, + g_strdup (key), + value); /* steals value */ + } + out: + ; +} + +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + GVariant *result; + gboolean ret; + + ret = FALSE; + + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + { + /* load all properties synchronously */ + result = g_dbus_connection_invoke_method_sync (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", proxy->priv->interface_name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, /* timeout */ + cancellable, + error); + if (result == NULL) + goto out; + + process_get_all_reply (proxy, result); + + g_variant_unref (result); + } + + subscribe_to_signals (proxy); + + ret = TRUE; + + out: + return ret; +} + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = initable_init; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +get_all_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GVariant *result; + GError *error; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + if (result == NULL) + { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } + else + { + g_simple_async_result_set_op_res_gpointer (simple, + result, + (GDestroyNotify) g_variant_unref); + } + + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); +} + +static void +async_initable_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (proxy), + callback, + user_data, + NULL); + + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + { + /* load all properties asynchronously */ + g_dbus_connection_invoke_method (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", proxy->priv->interface_name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, /* timeout */ + cancellable, + (GAsyncReadyCallback) get_all_cb, + simple); + } + else + { + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + } +} + +static gboolean +async_initable_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GVariant *result; + gboolean ret; + + ret = FALSE; + + result = g_simple_async_result_get_op_res_gpointer (simple); + if (result == NULL) + { + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + { + g_simple_async_result_propagate_error (simple, error); + goto out; + } + } + else + { + process_get_all_reply (proxy, result); + } + + subscribe_to_signals (proxy); + + ret = TRUE; + + out: + return ret; +} + +static void +async_initable_iface_init (GAsyncInitableIface *async_initable_iface) +{ + async_initable_iface->init_async = async_initable_init_async; + async_initable_iface->init_finish = async_initable_init_finish; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_proxy_new: + * @connection: A #GDBusConnection. + * @object_type: Either #G_TYPE_DBUS_PROXY or the #GType for the #GDBusProxy-derived type of proxy to create. + * @flags: Flags used when constructing the proxy. + * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL. + * @unique_bus_name: A unique bus name or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @interface_name: A D-Bus interface name. + * @cancellable: A #GCancellable or %NULL. + * @callback: Callback function to invoke when the proxy is ready. + * @user_data: User data to pass to @callback. + * + * Creates a proxy for accessing @interface_name on the remote object at @object_path + * owned by @unique_bus_name at @connection and asynchronously loads D-Bus properties unless the + * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to the + * #GDBusProxy::g-properties-changed signal to get notified about property changes. + * + * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up + * match rules for signals. Connect to the #GDBusProxy::g-signal signal + * to handle signals from the remote object. + * + * This is a failable asynchronous constructor - when the proxy is + * ready, @callback will be invoked and you can use + * g_dbus_proxy_new_finish() to get the result. + * + * See g_dbus_proxy_new_sync() and for a synchronous version of this constructor. + **/ +void +g_dbus_proxy_new (GDBusConnection *connection, + GType object_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *unique_bus_name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + g_return_if_fail (g_type_is_a (object_type, G_TYPE_DBUS_PROXY)); + g_return_if_fail ((unique_bus_name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || + g_dbus_is_unique_name (unique_bus_name)); + g_return_if_fail (g_variant_is_object_path (object_path)); + g_return_if_fail (g_dbus_is_interface_name (interface_name)); + + g_async_initable_new_async (object_type, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "g-flags", flags, + "g-interface-info", info, + "g-unique-bus-name", unique_bus_name, + "g-connection", connection, + "g-object-path", object_path, + "g-interface-name", interface_name, + NULL); +} + +/** + * g_dbus_proxy_new_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_dbus_proxy_new(). + * @error: Return location for error or %NULL. + * + * Finishes creating a #GDBusProxy. + * + * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref(). + **/ +GDBusProxy * +g_dbus_proxy_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_PROXY (object); + else + return NULL; +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_proxy_new_sync: + * @connection: A #GDBusConnection. + * @object_type: Either #G_TYPE_DBUS_PROXY or the #GType for the #GDBusProxy-derived type of proxy to create. + * @flags: Flags used when constructing the proxy. + * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL. + * @unique_bus_name: A unique bus name or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @interface_name: A D-Bus interface name. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Creates a proxy for accessing @interface_name on the remote object at @object_path + * owned by @unique_bus_name at @connection and synchronously loads D-Bus properties unless the + * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. + * + * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up + * match rules for signals. Connect to the #GDBusProxy::g-signal signal + * to handle signals from the remote object. + * + * This is a synchronous failable constructor. See g_dbus_proxy_new() + * and g_dbus_proxy_new_finish() for the asynchronous version. + * + * Returns: A #GDBusProxy or %NULL if error is set. Free with g_object_unref(). + **/ +GDBusProxy * +g_dbus_proxy_new_sync (GDBusConnection *connection, + GType object_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *unique_bus_name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GError **error) +{ + GInitable *initable; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_DBUS_PROXY), NULL); + g_return_val_if_fail ((unique_bus_name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || + g_dbus_is_unique_name (unique_bus_name), NULL); + 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); + + initable = g_initable_new (object_type, + cancellable, + error, + "g-flags", flags, + "g-interface-info", info, + "g-unique-bus-name", unique_bus_name, + "g-connection", connection, + "g-object-path", object_path, + "g-interface-name", interface_name, + NULL); + if (initable != NULL) + return G_DBUS_PROXY (initable); + else + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_proxy_get_connection: + * @proxy: A #GDBusProxy. + * + * Gets the connection @proxy is for. + * + * Returns: A #GDBusConnection owned by @proxy. Do not free. + **/ +GDBusConnection * +g_dbus_proxy_get_connection (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + return proxy->priv->connection; +} + +/** + * g_dbus_proxy_get_flags: + * @proxy: A #GDBusProxy. + * + * Gets the flags that @proxy was constructed with. + * + * Returns: Flags from the #GDBusProxyFlags enumeration. + **/ +GDBusProxyFlags +g_dbus_proxy_get_flags (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), 0); + return proxy->priv->flags; +} + +/** + * g_dbus_proxy_get_unique_bus_name: + * @proxy: A #GDBusProxy. + * + * Gets the unique bus name @proxy is for. + * + * Returns: A string owned by @proxy. Do not free. + **/ +const gchar * +g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + return proxy->priv->unique_bus_name; +} + +/** + * g_dbus_proxy_get_object_path: + * @proxy: A #GDBusProxy. + * + * Gets the object path @proxy is for. + * + * Returns: A string owned by @proxy. Do not free. + **/ +const gchar * +g_dbus_proxy_get_object_path (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + return proxy->priv->object_path; +} + +/** + * g_dbus_proxy_get_interface_name: + * @proxy: A #GDBusProxy. + * + * Gets the D-Bus interface name @proxy is for. + * + * Returns: A string owned by @proxy. Do not free. + **/ +const gchar * +g_dbus_proxy_get_interface_name (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + return proxy->priv->interface_name; +} + +/** + * g_dbus_proxy_get_default_timeout: + * @proxy: A #GDBusProxy. + * + * Gets the timeout to use if -1 (specifying default timeout) is + * passed as @timeout_msec in the g_dbus_proxy_invoke_method() and + * g_dbus_proxy_invoke_method_sync() functions. + * + * See the #GDBusProxy:g-default-timeout property for more details. + * + * Returns: Timeout to use for @proxy. + */ +gint +g_dbus_proxy_get_default_timeout (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), -1); + return proxy->priv->timeout_msec; +} + +/** + * g_dbus_proxy_set_default_timeout: + * @proxy: A #GDBusProxy. + * @timeout_msec: Timeout in milliseconds. + * + * Sets the timeout to use if -1 (specifying default timeout) is + * passed as @timeout_msec in the g_dbus_proxy_invoke_method() and + * g_dbus_proxy_invoke_method_sync() functions. + * + * See the #GDBusProxy:g-default-timeout property for more details. + */ +void +g_dbus_proxy_set_default_timeout (GDBusProxy *proxy, + gint timeout_msec) +{ + g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0); + + /* TODO: locking? */ + if (proxy->priv->timeout_msec != timeout_msec) + { + proxy->priv->timeout_msec = timeout_msec; + g_object_notify (G_OBJECT (proxy), "g-default-timeout"); + } +} + +/** + * g_dbus_proxy_get_interface_info: + * @proxy: A #GDBusProxy + * + * Returns the #GDBusInterfaceInfo, if any, specifying the minimal + * interface that @proxy conforms to. + * + * See the #GDBusProxy:g-interface-info property for more details. + * + * Returns: A #GDBusInterfaceInfo or %NULL. Do not unref the returned + * object, it is owned by @proxy. + */ +GDBusInterfaceInfo * +g_dbus_proxy_get_interface_info (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + return proxy->priv->expected_interface; +} + +/** + * g_dbus_proxy_set_interface_info: + * @proxy: A #GDBusProxy + * @info: Minimum interface this proxy conforms to or %NULL to unset. + * + * Ensure that interactions with @proxy conform to the given + * interface. For example, when completing a method call, if the type + * signature of the message isn't what's expected, the given #GError + * is set. Signals that have a type signature mismatch are simply + * dropped. + * + * See the #GDBusProxy:g-interface-info property for more details. + */ +void +g_dbus_proxy_set_interface_info (GDBusProxy *proxy, + GDBusInterfaceInfo *info) +{ + g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + if (proxy->priv->expected_interface != NULL) + g_dbus_interface_info_unref (proxy->priv->expected_interface); + proxy->priv->expected_interface = info != NULL ? g_dbus_interface_info_ref (info) : NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +maybe_split_method_name (const gchar *method_name, + gchar **out_interface_name, + const gchar **out_method_name) +{ + gboolean was_split; + + was_split = FALSE; + g_assert (out_interface_name != NULL); + g_assert (out_method_name != NULL); + *out_interface_name = NULL; + *out_method_name = NULL; + + if (strchr (method_name, '.') != NULL) + { + gchar *p; + gchar *last_dot; + + p = g_strdup (method_name); + last_dot = strrchr (p, '.'); + *last_dot = '\0'; + + *out_interface_name = p; + *out_method_name = last_dot + 1; + + was_split = TRUE; + } + + return was_split; +} + + +static void +reply_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GVariant *value; + GError *error; + + error = NULL; + value = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + if (error != NULL) + { + g_simple_async_result_set_from_error (simple, + error); + g_error_free (error); + } + else + { + g_simple_async_result_set_op_res_gpointer (simple, + value, + (GDestroyNotify) g_variant_unref); + } + + /* no need to complete in idle since the method GDBusConnection already does */ + g_simple_async_result_complete (simple); +} + +static const GDBusMethodInfo * +lookup_method_info_or_warn (GDBusProxy *proxy, + const char *method_name) +{ + const GDBusMethodInfo *info; + + if (!proxy->priv->expected_interface) + return NULL; + + info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name); + if (!info) + g_warning ("Trying to invoke method %s which isn't in expected interface %s", + method_name, proxy->priv->expected_interface->name); + + return info; +} + +static gboolean +validate_method_return (const char *method_name, + GVariant *value, + const GDBusMethodInfo *expected_method_info, + GError **error) +{ + const gchar *type_string; + gchar *signature; + gboolean ret; + + ret = TRUE; + signature = NULL; + + if (value == NULL || expected_method_info == NULL) + goto out; + + /* Shouldn't happen... */ + if (g_variant_classify (value) != G_VARIANT_CLASS_TUPLE) + goto out; + + type_string = g_variant_get_type_string (value); + signature = _g_dbus_compute_complete_signature (expected_method_info->out_args, TRUE); + if (g_strcmp0 (type_string, signature) != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Method `%s' returned signature `%s', but expected `%s'"), + method_name, + type_string, + signature); + ret = FALSE; + } + + out: + g_free (signature); + return ret; +} + +/** + * g_dbus_proxy_invoke_method: + * @proxy: A #GDBusProxy. + * @method_name: Name of method to invoke. + * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters. + * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't + * care about the result of the method invocation. + * @user_data: The data to pass to @callback. + * + * Asynchronously invokes the @method_name method on @proxy. + * + * If @method_name contains any dots, then @name is split into interface and + * method name parts. This allows using @proxy for invoking methods on + * other interfaces. + * + * If the #GDBusConnection associated with @proxy is closed then + * the operation will fail with %G_IO_ERROR_CLOSED. If + * @cancellable is canceled, the operation will fail with + * %G_IO_ERROR_CANCELLED. If @parameters contains a value not + * compatible with the D-Bus protocol, the operation fails with + * %G_IO_ERROR_INVALID_ARGUMENT. + * + * This is an asynchronous method. When the operation is finished, @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_proxy_invoke_method_finish() to get the result of the operation. + * See g_dbus_proxy_invoke_method_sync() for the + * synchronous version of this method. + */ +void +g_dbus_proxy_invoke_method (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + gboolean was_split; + gchar *split_interface_name; + const gchar *split_method_name; + const GDBusMethodInfo *expected_method_info; + const gchar *target_method_name; + const gchar *target_interface_name; + + g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name)); + g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); + g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0); + + simple = g_simple_async_result_new (G_OBJECT (proxy), + callback, + user_data, + g_dbus_proxy_invoke_method); + + was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name); + target_method_name = was_split ? split_method_name : method_name; + target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name; + + g_object_set_data_full (G_OBJECT (simple), "-gdbus-proxy-method-name", g_strdup (target_method_name), g_free); + + /* Just warn here */ + expected_method_info = lookup_method_info_or_warn (proxy, target_method_name); + + g_dbus_connection_invoke_method (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + target_interface_name, + target_method_name, + parameters, + flags, + timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec, + cancellable, + (GAsyncReadyCallback) reply_cb, + simple); + + g_free (split_interface_name); +} + +/** + * g_dbus_proxy_invoke_method_finish: + * @proxy: A #GDBusProxy. + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_invoke_method(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_proxy_invoke_method(). + * + * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with + * return values. Free with g_variant_unref(). + */ +GVariant * +g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GVariant *value; + const char *method_name; + const GDBusMethodInfo *expected_method_info; + + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_invoke_method); + + value = NULL; + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + value = g_simple_async_result_get_op_res_gpointer (simple); + method_name = g_object_get_data (G_OBJECT (simple), "-gdbus-proxy-method-name"); + + /* We may not have a method name for internally-generated proxy calls like GetAll */ + if (value && method_name && proxy->priv->expected_interface) + { + expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name); + if (!validate_method_return (method_name, value, expected_method_info, error)) + { + g_variant_unref (value); + value = NULL; + } + } + + out: + return value; +} + +/** + * g_dbus_proxy_invoke_method_sync: + * @proxy: A #GDBusProxy. + * @method_name: Name of method to invoke. + * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters. + * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the @method_name method on @proxy. + * + * If @method_name contains any dots, then @name is split into interface and + * method name parts. This allows using @proxy for invoking methods on + * other interfaces. + * + * If the #GDBusConnection associated with @proxy is disconnected then + * the operation will fail with %G_IO_ERROR_CLOSED. If + * @cancellable is canceled, the operation will fail with + * %G_IO_ERROR_CANCELLED. If @parameters contains a value not + * compatible with the D-Bus protocol, the operation fails with + * %G_IO_ERROR_INVALID_ARGUMENT. + * + * The calling thread is blocked until a reply is received. See + * g_dbus_proxy_invoke_method() for the asynchronous version of this + * method. + * + * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with + * return values. Free with g_variant_unref(). + */ +GVariant * +g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) +{ + GVariant *ret; + gboolean was_split; + gchar *split_interface_name; + const gchar *split_method_name; + const GDBusMethodInfo *expected_method_info; + const char *target_method_name; + const char *target_interface_name; + + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + g_return_val_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name), NULL); + g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); + g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name); + target_method_name = was_split ? split_method_name : method_name; + target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name; + + if (proxy->priv->expected_interface) + { + expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name); + if (!expected_method_info) + g_warning ("Trying to invoke method %s which isn't in expected interface %s", + target_method_name, target_interface_name); + } + else + { + expected_method_info = NULL; + } + + ret = g_dbus_connection_invoke_method_sync (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + target_interface_name, + target_method_name, + parameters, + flags, + timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec, + cancellable, + error); + if (!validate_method_return (target_method_name, ret, expected_method_info, error)) + { + g_variant_unref (ret); + ret = NULL; + } + + g_free (split_interface_name); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h new file mode 100644 index 000000000..1bc9902d0 --- /dev/null +++ b/gio/gdbusproxy.h @@ -0,0 +1,146 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_PROXY_H__ +#define __G_DBUS_PROXY_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_PROXY (g_dbus_proxy_get_type ()) +#define G_DBUS_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_PROXY, GDBusProxy)) +#define G_DBUS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_PROXY, GDBusProxyClass)) +#define G_DBUS_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_PROXY, GDBusProxyClass)) +#define G_IS_DBUS_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_PROXY)) +#define G_IS_DBUS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_PROXY)) + +typedef struct _GDBusProxyClass GDBusProxyClass; +typedef struct _GDBusProxyPrivate GDBusProxyPrivate; + +/** + * GDBusProxy: + * + * The #GDBusProxy structure contains only private data and + * should only be accessed using the provided API. + */ +struct _GDBusProxy +{ + /*< private >*/ + GObject parent_instance; + GDBusProxyPrivate *priv; +}; + +/** + * GDBusProxyClass: + * @g_properties_changed: Signal class handler for the #GDBusProxy::g-properties-changed signal. + * @g_signal: Signal class handler for the #GDBusProxy::g-signal signal. + * + * Class structure for #GDBusProxy. + */ +struct _GDBusProxyClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + /* Signals */ + void (*g_properties_changed) (GDBusProxy *proxy, + GHashTable *changed_properties); + void (*g_signal) (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters); + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +GType g_dbus_proxy_get_type (void) G_GNUC_CONST; +void g_dbus_proxy_new (GDBusConnection *connection, + GType object_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *unique_bus_name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusProxy *g_dbus_proxy_new_finish (GAsyncResult *res, + GError **error); +GDBusProxy *g_dbus_proxy_new_sync (GDBusConnection *connection, + GType object_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *unique_bus_name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GError **error); +GDBusConnection *g_dbus_proxy_get_connection (GDBusProxy *proxy); +GDBusProxyFlags g_dbus_proxy_get_flags (GDBusProxy *proxy); +const gchar *g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy); +const gchar *g_dbus_proxy_get_object_path (GDBusProxy *proxy); +const gchar *g_dbus_proxy_get_interface_name (GDBusProxy *proxy); +gint g_dbus_proxy_get_default_timeout (GDBusProxy *proxy); +void g_dbus_proxy_set_default_timeout (GDBusProxy *proxy, + gint timeout_msec); +GDBusInterfaceInfo *g_dbus_proxy_get_interface_info (GDBusProxy *proxy); +void g_dbus_proxy_set_interface_info (GDBusProxy *proxy, + GDBusInterfaceInfo *info); +GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *proxy, + const gchar *property_name, + GError **error); +gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, + GError **error); +void g_dbus_proxy_invoke_method (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GVariant *g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, + GAsyncResult *res, + GError **error); +GVariant *g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* __G_DBUS_PROXY_H__ */ diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c new file mode 100644 index 000000000..4f85c1de5 --- /dev/null +++ b/gio/gdbusproxywatching.c @@ -0,0 +1,397 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 + +#include + +#include "gdbusutils.h" +#include "gdbusnamewatching.h" +#include "gdbusproxywatching.h" +#include "gdbuserror.h" +#include "gdbusprivate.h" +#include "gdbusproxy.h" +#include "gdbusnamewatching.h" +#include "gcancellable.h" + +/** + * SECTION:gdbusproxywatching + * @title: Watching Proxies + * @short_description: Simple API for watching proxies + * @include: gdbus/gdbus.h + * + * Convenience API for watching bus proxies. + * + * Simple application watching a proxyFIXME: MISSING XINCLUDE CONTENT + */ + +/* ---------------------------------------------------------------------------------------------------- */ + +G_LOCK_DEFINE_STATIC (lock); + +static guint next_global_id = 1; +static GHashTable *map_id_to_client = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + guint id; + GBusProxyAppearedCallback proxy_appeared_handler; + GBusProxyVanishedCallback proxy_vanished_handler; + gpointer user_data; + GDestroyNotify user_data_free_func; + GMainContext *main_context; + + gchar *name; + gchar *name_owner; + GDBusConnection *connection; + guint name_watcher_id; + + GCancellable *cancellable; + + gchar *object_path; + gchar *interface_name; + GType interface_type; + GDBusProxyFlags proxy_flags; + GDBusProxy *proxy; + + gboolean initial_construction; +} Client; + +static void +client_unref (Client *client) +{ + /* ensure we're only called from g_bus_unwatch_proxy */ + g_assert (client->name_watcher_id == 0); + + g_free (client->name_owner); + if (client->connection != NULL) + g_object_unref (client->connection); + if (client->proxy != NULL) + g_object_unref (client->proxy); + + g_free (client->name); + g_free (client->object_path); + g_free (client->interface_name); + + if (client->main_context != NULL) + g_main_context_unref (client->main_context); + + if (client->user_data_free_func != NULL) + client->user_data_free_func (client->user_data); + g_free (client); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +proxy_constructed_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Client *client = user_data; + GDBusProxy *proxy; + GError *error; + + error = NULL; + proxy = g_dbus_proxy_new_finish (res, &error); + if (proxy == NULL) + { + /* g_warning ("error while constructing proxy: %s", error->message); */ + g_error_free (error); + + /* handle initial construction, send out vanished if the name + * is there but we constructing a proxy fails + */ + if (client->initial_construction) + { + if (client->proxy_vanished_handler != NULL) + { + client->proxy_vanished_handler (client->connection, + client->name, + client->user_data); + } + client->initial_construction = FALSE; + } + } + else + { + g_assert (client->proxy == NULL); + g_assert (client->cancellable != NULL); + client->proxy = G_DBUS_PROXY (proxy); + + g_object_unref (client->cancellable); + client->cancellable = NULL; + + /* perform callback */ + if (client->proxy_appeared_handler != NULL) + { + client->proxy_appeared_handler (client->connection, + client->name, + client->name_owner, + client->proxy, + client->user_data); + } + client->initial_construction = FALSE; + } +} + +static void +on_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + Client *client = user_data; + + //g_debug ("\n\nname appeared (owner `%s')", name_owner); + + /* invariants */ + g_assert (client->name_owner == NULL); + g_assert (client->connection == NULL); + g_assert (client->cancellable == NULL); + + client->name_owner = g_strdup (name_owner); + client->connection = g_object_ref (connection); + client->cancellable = g_cancellable_new (); + + g_dbus_proxy_new (client->connection, + client->interface_type, + client->proxy_flags, + NULL, /* GDBusInterfaceInfo */ + client->name_owner, + client->object_path, + client->interface_name, + client->cancellable, + proxy_constructed_cb, + client); +} + +static void +on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + Client *client = user_data; + + /*g_debug ("\n\nname vanished");*/ + + g_free (client->name_owner); + if (client->connection != NULL) + g_object_unref (client->connection); + client->name_owner = NULL; + client->connection = NULL; + + /* free the proxy if we have it */ + if (client->proxy != NULL) + { + g_assert (client->cancellable == NULL); + + g_object_unref (client->proxy); + client->proxy = NULL; + + /* if we have the proxy, it means we last sent out a 'appeared' + * callback - so send out a 'vanished' callback + */ + if (client->proxy_vanished_handler != NULL) + { + client->proxy_vanished_handler (client->connection, + client->name, + client->user_data); + } + client->initial_construction = FALSE; + } + else + { + /* otherwise cancel construction of the proxy if applicable */ + if (client->cancellable != NULL) + { + g_cancellable_cancel (client->cancellable); + g_object_unref (client->cancellable); + client->cancellable = NULL; + } + else + { + /* handle initial construction, send out vanished if + * the name isn't there + */ + if (client->initial_construction) + { + if (client->proxy_vanished_handler != NULL) + { + client->proxy_vanished_handler (client->connection, + client->name, + client->user_data); + } + client->initial_construction = FALSE; + } + } + } +} + +/** + * g_bus_watch_proxy: + * @bus_type: The type of bus to watch a name on (can't be #G_BUS_TYPE_NONE). + * @name: The name (well-known or unique) to watch. + * @flags: Flags from the #GBusNameWatcherFlags enumeration. + * @object_path: The object path of the remote object to watch. + * @interface_name: The D-Bus interface name for the proxy. + * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. + * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. + * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the + * requested proxy is available. + * @proxy_vanished_handler: Handler to invoke when @name is known to not exist + * and the previously created proxy is no longer available. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Starts watching a remote object at @object_path owned by @name on + * the bus specified by @bus_type. When the object is available, a + * #GDBusProxy (or derived class cf. @interface_type) instance is + * constructed for the @interface_name D-Bus interface and then + * @proxy_appeared_handler will be called when the proxy is ready and + * all properties have been loaded. When @name vanishes, + * @proxy_vanished_handler is called. + * + * This function makes it very simple to write applications that wants + * to watch a well-known remote object on a well-known name, see . Basically, the application simply + * starts using the proxy when @proxy_appeared_handler is called and + * stops using it when @proxy_vanished_handler is called. Callbacks + * will be invoked in the thread-default main + * loop of the thread you are calling this function from. + * + * Applications typically use this function to watch the + * manager object of a well-known name. Upon acquiring + * a proxy for the manager object, applications typically construct + * additional proxies in response to the result of enumeration methods + * on the manager object. + * + * Many of the comment that applies to g_bus_watch_name() also applies + * here. For example, you are guaranteed that one of the handlers will + * be invoked (on the main thread) after calling this function and + * also that the two handlers alternate. When you are done watching the + * proxy, just call g_bus_unwatch_proxy(). + * + * Returns: An identifier (never 0) that can be used with + * g_bus_unwatch_proxy() to stop watching the remote object. + **/ +guint +g_bus_watch_proxy (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0); + g_return_val_if_fail (g_dbus_is_name (name), 0); + g_return_val_if_fail (g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0); + g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->proxy_appeared_handler = proxy_appeared_handler; + client->proxy_vanished_handler = proxy_vanished_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + client->name_watcher_id = g_bus_watch_name (bus_type, + name, + flags, + on_name_appeared, + on_name_vanished, + client, + NULL); + + client->object_path = g_strdup (object_path); + client->interface_name = g_strdup (interface_name); + client->interface_type = interface_type; + client->proxy_flags = proxy_flags; + client->initial_construction = TRUE; + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + G_UNLOCK (lock); + + return client->id; +} + +/** + * g_bus_unwatch_proxy: + * @watcher_id: An identifier obtained from g_bus_watch_proxy() + * + * Stops watching proxy. + */ +void +g_bus_unwatch_proxy (guint watcher_id) +{ + Client *client; + + g_return_if_fail (watcher_id > 0); + + client = NULL; + + G_LOCK (lock); + if (watcher_id == 0 || + map_id_to_client == NULL || + (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL) + { + g_warning ("Invalid id %d passed to g_bus_unwatch_proxy()", watcher_id); + goto out; + } + + g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id))); + + out: + G_UNLOCK (lock); + + if (client != NULL) + { + g_bus_unwatch_name (client->name_watcher_id); + client->name_watcher_id = 0; + client_unref (client); + } +} diff --git a/gio/gdbusproxywatching.h b/gio/gdbusproxywatching.h new file mode 100644 index 000000000..3544269d3 --- /dev/null +++ b/gio/gdbusproxywatching.h @@ -0,0 +1,77 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_PROXY_WATCHING_H__ +#define __G_DBUS_PROXY_WATCHING_H__ + +#include + +G_BEGIN_DECLS + +/** + * GBusProxyAppearedCallback: + * @connection: The #GDBusConnection the proxy is being watched on. + * @name: The name being watched. + * @name_owner: Unique name of the owner of the name being watched. + * @proxy: A #GDBusProxy (or derived) instance with all properties loaded. + * @user_data: User data passed to g_bus_watch_proxy(). + * + * Invoked when the proxy being watched is ready for use - the passed + * @proxy object is valid until the #GBusProxyVanishedCallback + * callback is invoked. + */ +typedef void (*GBusProxyAppearedCallback) (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data); + +/** + * GBusProxyVanishedCallback: + * @connection: The #GDBusConnection the proxy is being watched on. + * @name: The name being watched. + * @user_data: User data passed to g_bus_watch_proxy(). + * + * Invoked when the proxy being watched has vanished. The #GDBusProxy + * object passed in the #GBusProxyAppearedCallback callback is no + * longer valid. + */ +typedef void (*GBusProxyVanishedCallback) (GDBusConnection *connection, + const gchar *name, + gpointer user_data); + +guint g_bus_watch_proxy (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_bus_unwatch_proxy (guint watcher_id); + +G_END_DECLS + +#endif /* __G_DBUS_PROXY_WATCHING_H__ */ diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c new file mode 100644 index 000000000..0d0deeaa2 --- /dev/null +++ b/gio/gdbusserver.c @@ -0,0 +1,1043 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 +#include +#include +#include + +#ifdef G_OS_UNIX +#include +#endif + +#include "giotypes.h" +#include "gdbusaddress.h" +#include "gdbusutils.h" +#include "gdbusconnection.h" +#include "gdbusserver.h" +#include "gioenumtypes.h" +#include "gdbusprivate.h" +#include "gdbusauthobserver.h" +#include "gio-marshal.h" + +/** + * SECTION:gdbusserver + * @short_description: Helper for accepting connections + * @include: gdbus/gdbus.h + * + * #GDBusServer is a helper for listening to and accepting D-Bus + * connections. + * + * D-Bus peer-to-peer exampleFIXME: MISSING XINCLUDE CONTENT + */ + +struct _GDBusServerPrivate +{ + GDBusServerFlags flags; + gchar *address; + gchar *guid; + + guchar *nonce; + gchar *nonce_file; + + gchar *client_address; + + GSocketListener *listener; + gboolean is_using_listener; + + /* The result of g_main_context_get_thread_default() when the object + * was created (the GObject _init() function) - this is used for delivery + * of the :new-connection GObject signal. + */ + GMainContext *main_context_at_construction; + + gboolean active; + + GDBusAuthObserver *authentication_observer; +}; + +enum +{ + PROP_0, + PROP_ADDRESS, + PROP_CLIENT_ADDRESS, + PROP_FLAGS, + PROP_GUID, + PROP_ACTIVE, + PROP_AUTHENTICATION_OBSERVER, +}; + +enum +{ + NEW_CONNECTION_SIGNAL, + LAST_SIGNAL, +}; + +guint _signals[LAST_SIGNAL] = {0}; + +static void initable_iface_init (GInitableIface *initable_iface); + +G_DEFINE_TYPE_WITH_CODE (GDBusServer, g_dbus_server, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) + ); + +static void +g_dbus_server_finalize (GObject *object) +{ + GDBusServer *server = G_DBUS_SERVER (object); + + if (server->priv->authentication_observer != NULL) + g_object_unref (server->priv->authentication_observer); + + if (server->priv->listener != NULL) + g_object_unref (server->priv->listener); + + g_free (server->priv->address); + g_free (server->priv->guid); + g_free (server->priv->client_address); + if (server->priv->nonce != NULL) + { + memset (server->priv->nonce, '\0', 16); + g_free (server->priv->nonce); + } + /* we could unlink the nonce file but I don't + * think it's really worth the effort/risk + */ + g_free (server->priv->nonce_file); + + if (server->priv->main_context_at_construction != NULL) + g_main_context_unref (server->priv->main_context_at_construction); + + if (G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize (object); +} + +static void +g_dbus_server_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusServer *server = G_DBUS_SERVER (object); + + switch (prop_id) + { + case PROP_FLAGS: + g_value_set_flags (value, server->priv->flags); + break; + + case PROP_GUID: + g_value_set_string (value, server->priv->guid); + break; + + case PROP_ADDRESS: + g_value_set_string (value, server->priv->address); + break; + + case PROP_CLIENT_ADDRESS: + g_value_set_string (value, server->priv->client_address); + break; + + case PROP_ACTIVE: + g_value_set_boolean (value, server->priv->active); + break; + + case PROP_AUTHENTICATION_OBSERVER: + g_value_set_object (value, server->priv->authentication_observer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_server_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusServer *server = G_DBUS_SERVER (object); + + switch (prop_id) + { + case PROP_FLAGS: + server->priv->flags = g_value_get_flags (value); + break; + + case PROP_GUID: + server->priv->guid = g_value_dup_string (value); + break; + + case PROP_ADDRESS: + server->priv->address = g_value_dup_string (value); + break; + + case PROP_AUTHENTICATION_OBSERVER: + server->priv->authentication_observer = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_dbus_server_class_init (GDBusServerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_dbus_server_finalize; + gobject_class->set_property = g_dbus_server_set_property; + gobject_class->get_property = g_dbus_server_get_property; + + /** + * GDBusServer:flags: + * + * Flags from the #GDBusServerFlags enumeration. + */ + g_object_class_install_property (gobject_class, + PROP_FLAGS, + g_param_spec_flags ("flags", + _("Flags"), + _("Flags for the server"), + G_TYPE_DBUS_SERVER_FLAGS, + G_DBUS_SERVER_FLAGS_NONE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusServer:guid: + * + * The guid of the server. + */ + g_object_class_install_property (gobject_class, + PROP_GUID, + g_param_spec_string ("guid", + _("GUID"), + _("The guid of the server"), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusServer:address: + * + * The D-Bus address to listen on. + */ + g_object_class_install_property (gobject_class, + PROP_ADDRESS, + g_param_spec_string ("address", + _("Address"), + _("The address to listen on"), + NULL, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusServer:client-address: + * + * The D-Bus address that clients can use. + */ + g_object_class_install_property (gobject_class, + PROP_CLIENT_ADDRESS, + g_param_spec_string ("client-address", + _("Client Address"), + _("The address clients can use"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusServer:active: + * + * Whether the server is currently active. + */ + g_object_class_install_property (gobject_class, + PROP_ACTIVE, + g_param_spec_string ("active", + _("Active"), + _("Whether the server is currently active"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusServer:authentication-observer: + * + * A #GDBusAuthObserver object to assist in the authentication process or %NULL. + */ + g_object_class_install_property (gobject_class, + PROP_AUTHENTICATION_OBSERVER, + g_param_spec_object ("authentication-observer", + _("Authentication Observer"), + _("Object used to assist in the authentication process"), + G_TYPE_DBUS_AUTH_OBSERVER, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** + * GDBusServer::new-connection: + * @server: The #GDBusServer emitting the signal. + * @connection: A #GDBusConnection for the new connection. + * + * Emitted when a new authenticated connection has been made. Use + * g_dbus_connection_get_peer_credentials() to figure out what + * identity (if any), was authenticated. + * + * If you want to accept the connection, simply ref the @connection + * object. Then call g_dbus_connection_close() and unref it when you + * are done with it. A typical thing to do when accepting a + * connection is to listen to the #GDBusConnection::closed signal. + * + * If #GDBusServer:flags contains %G_DBUS_SERVER_FLAGS_RUN_IN_THREAD + * then the signal is emitted in a new thread dedicated to the + * connection. Otherwise the signal is emitted in the thread-default main + * loop of the thread that @server was constructed in. + */ + _signals[NEW_CONNECTION_SIGNAL] = g_signal_new ("new-connection", + G_TYPE_DBUS_SERVER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GDBusServerClass, new_connection), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_DBUS_CONNECTION); + + + g_type_class_add_private (klass, sizeof (GDBusServerPrivate)); +} + +static void +g_dbus_server_init (GDBusServer *server) +{ + server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, G_TYPE_DBUS_SERVER, GDBusServerPrivate); + + server->priv->main_context_at_construction = g_main_context_get_thread_default (); + if (server->priv->main_context_at_construction != NULL) + g_main_context_ref (server->priv->main_context_at_construction); +} + +static gboolean +on_run (GSocketService *service, + GSocketConnection *socket_connection, + GObject *source_object, + gpointer user_data); + +/** + * g_dbus_server_new_sync: + * @address: A D-Bus address. + * @flags: Flags from the #GDBusServerFlags enumeration. + * @guid: A D-Bus GUID. + * @observer: A #GDBusAuthObserver or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for server or %NULL. + * + * Creates a new D-Bus server that listens on the first address in + * @address that works. + * + * Once constructed, you can use g_dbus_server_get_client_address() to + * get a D-Bus address string that clients can use to connect. + * + * Connect to the #GDBusServer::new-connection signal to handle + * incoming connections. + * + * The returned #GDBusServer isn't active - you have to start it with + * g_dbus_server_start(). + * + * See for how #GDBusServer can + * be used. + * + * This is a synchronous failable constructor. See + * g_dbus_server_new() for the asynchronous version. + * + * Returns: A #GDBusServer or %NULL if @error is set. Free with + * g_object_unref(). + */ +GDBusServer * +g_dbus_server_new_sync (const gchar *address, + GDBusServerFlags flags, + const gchar *guid, + GDBusAuthObserver *observer, + GCancellable *cancellable, + GError **error) +{ + GDBusServer *server; + + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (g_dbus_is_guid (guid), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + server = g_initable_new (G_TYPE_DBUS_SERVER, + cancellable, + error, + "address", address, + "flags", flags, + "guid", guid, + "authentication-observer", observer, + NULL); + if (server != NULL) + { + /* Right now we don't have any transport not using the listener... */ + g_assert (server->priv->is_using_listener); + g_signal_connect (G_SOCKET_SERVICE (server->priv->listener), + "run", + G_CALLBACK (on_run), + server); + } + + return server; +} + +/** + * g_dbus_server_get_client_address: + * @server: A #GDBusServer. + * + * Gets a D-Bus address string that can be used by clients to connect + * to @server. + * + * Returns: A D-Bus address string. Do not free, the string is owned + * by @server. + */ +const gchar * +g_dbus_server_get_client_address (GDBusServer *server) +{ + g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL); + return server->priv->client_address; +} + +/** + * g_dbus_server_get_guid: + * @server: A #GDBusServer. + * + * Gets the GUID for @server. + * + * Returns: A D-Bus GUID. Do not free this string, it is owned by @server. + */ +const gchar * +g_dbus_server_get_guid (GDBusServer *server) +{ + g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL); + return server->priv->guid; +} + +/** + * g_dbus_server_get_flags: + * @server: A #GDBusServer. + * + * Gets the flags for @server. + * + * Returns: A set of flags from the #GDBusServerFlags enumeration. + */ +GDBusServerFlags +g_dbus_server_get_flags (GDBusServer *server) +{ + g_return_val_if_fail (G_IS_DBUS_SERVER (server), G_DBUS_SERVER_FLAGS_NONE); + return server->priv->flags; +} + +/** + * g_dbus_server_is_active: + * @server: A #GDBusServer. + * + * Gets whether @server is active. + * + * Returns: %TRUE if server is active, %FALSE otherwise. + */ +gboolean +g_dbus_server_is_active (GDBusServer *server) +{ + g_return_val_if_fail (G_IS_DBUS_SERVER (server), G_DBUS_SERVER_FLAGS_NONE); + return server->priv->active; +} + +/** + * g_dbus_server_start: + * @server: A #GDBusServer. + * + * Starts @server. + */ +void +g_dbus_server_start (GDBusServer *server) +{ + g_return_if_fail (G_IS_DBUS_SERVER (server)); + if (server->priv->active) + return; + /* Right now we don't have any transport not using the listener... */ + g_assert (server->priv->is_using_listener); + g_socket_service_start (G_SOCKET_SERVICE (server->priv->listener)); + server->priv->active = TRUE; + g_object_notify (G_OBJECT (server), "active"); +} + +/** + * g_dbus_server_stop: + * @server: A #GDBusServer. + * + * Stops @server. + */ +void +g_dbus_server_stop (GDBusServer *server) +{ + g_return_if_fail (G_IS_DBUS_SERVER (server)); + if (!server->priv->active) + return; + /* Right now we don't have any transport not using the listener... */ + g_assert (server->priv->is_using_listener); + g_socket_service_stop (G_SOCKET_SERVICE (server->priv->listener)); + server->priv->active = FALSE; + g_object_notify (G_OBJECT (server), "active"); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_UNIX + +static gint +random_ascii (void) +{ + gint ret; + ret = g_random_int_range (0, 60); + if (ret < 25) + ret += 'A'; + else if (ret < 50) + ret += 'a' - 25; + else + ret += '0' - 50; + return ret; +} + +/* note that address_entry has already been validated => exactly one of path, tmpdir or abstract keys are set */ +static gboolean +try_unix (GDBusServer *server, + const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + const gchar *path; + const gchar *tmpdir; + const gchar *abstract; + GSocketAddress *address; + + ret = FALSE; + address = NULL; + + path = g_hash_table_lookup (key_value_pairs, "path"); + tmpdir = g_hash_table_lookup (key_value_pairs, "tmpdir"); + abstract = g_hash_table_lookup (key_value_pairs, "abstract"); + + if (path != NULL) + { + address = g_unix_socket_address_new (path); + } + else if (tmpdir != NULL) + { + gint n; + GString *s; + GError *local_error; + + retry: + s = g_string_new (tmpdir); + g_string_append (s, "/dbus-"); + for (n = 0; n < 8; n++) + g_string_append_c (s, random_ascii ()); + + /* prefer abstract namespace if available */ + if (g_unix_socket_address_abstract_names_supported ()) + address = g_unix_socket_address_new_with_type (s->str, + -1, + G_UNIX_SOCKET_ADDRESS_ABSTRACT); + else + address = g_unix_socket_address_new (s->str); + g_string_free (s, TRUE); + + local_error = NULL; + if (!g_socket_listener_add_address (server->priv->listener, + address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, /* source_object */ + NULL, /* effective_address */ + &local_error)) + { + if (local_error->domain == G_IO_ERROR && local_error->code == G_IO_ERROR_ADDRESS_IN_USE) + { + g_error_free (local_error); + goto retry; + } + g_propagate_error (error, local_error); + goto out; + } + ret = TRUE; + goto out; + } + else if (abstract != NULL) + { + if (!g_unix_socket_address_abstract_names_supported ()) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("Abstract name space not supported")); + goto out; + } + address = g_unix_socket_address_new_with_type (abstract, + -1, + G_UNIX_SOCKET_ADDRESS_ABSTRACT); + } + else + { + g_assert_not_reached (); + } + + if (!g_socket_listener_add_address (server->priv->listener, + address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, /* source_object */ + NULL, /* effective_address */ + error)) + goto out; + + ret = TRUE; + + out: + + if (address != NULL) + { + /* Fill out client_address if the connection attempt worked */ + if (ret) + { + server->priv->is_using_listener = TRUE; + + switch (g_unix_socket_address_get_address_type (G_UNIX_SOCKET_ADDRESS (address))) + { + case G_UNIX_SOCKET_ADDRESS_ABSTRACT: + server->priv->client_address = g_strdup_printf ("unix:abstract=%s", + g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address))); + break; + + case G_UNIX_SOCKET_ADDRESS_PATH: + server->priv->client_address = g_strdup_printf ("unix:path=%s", + g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address))); + break; + + default: + g_assert_not_reached (); + break; + } + } + g_object_unref (address); + } + return ret; +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +/* note that address_entry has already been validated => + * both host and port (guranteed to be a number in [0, 65535]) are set (family is optional) + */ +static gboolean +try_tcp (GDBusServer *server, + const gchar *address_entry, + GHashTable *key_value_pairs, + gboolean do_nonce, + GError **error) +{ + gboolean ret; + const gchar *host; + const gchar *port; + const gchar *family; + gint port_num; + GSocketAddress *address; + GResolver *resolver; + GList *resolved_addresses; + GList *l; + + ret = FALSE; + address = NULL; + resolver = NULL; + resolved_addresses = NULL; + + host = g_hash_table_lookup (key_value_pairs, "host"); + port = g_hash_table_lookup (key_value_pairs, "port"); + family = g_hash_table_lookup (key_value_pairs, "family"); + if (g_hash_table_lookup (key_value_pairs, "noncefile") != NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Cannot specify nonce file when creating a server")); + goto out; + } + + if (host == NULL) + host = "localhost"; + if (port == NULL) + port = "0"; + port_num = strtol (port, NULL, 10); + + resolver = g_resolver_get_default (); + resolved_addresses = g_resolver_lookup_by_name (resolver, + host, + NULL, + error); + if (resolved_addresses == NULL) + { + goto out; + } + /* TODO: handle family */ + for (l = resolved_addresses; l != NULL; l = l->next) + { + GInetAddress *address = G_INET_ADDRESS (l->data); + GSocketAddress *socket_address; + GSocketAddress *effective_address; + + socket_address = g_inet_socket_address_new (address, port_num); + if (!g_socket_listener_add_address (server->priv->listener, + socket_address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_TCP, + NULL, /* GObject *source_object */ + &effective_address, + error)) + { + g_object_unref (socket_address); + goto out; + } + if (port_num == 0) + { + /* make sure we allocate the same port number for other listeners */ + port_num = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (effective_address)); + } + g_object_unref (effective_address); + g_object_unref (socket_address); + } + + if (do_nonce) + { + gint fd; + guint n; + gsize bytes_written; + gsize bytes_remaining; + + server->priv->nonce = g_new0 (guchar, 16); + for (n = 0; n < 16; n++) + server->priv->nonce[n] = g_random_int_range (0, 256); + fd = g_file_open_tmp ("gdbus-nonce-file-XXXXXX", + &server->priv->nonce_file, + error); + if (fd == -1) + { + g_socket_listener_close (server->priv->listener); + goto out; + } + again: + bytes_written = 0; + bytes_remaining = 16; + while (bytes_remaining > 0) + { + gssize ret; + ret = write (fd, server->priv->nonce + bytes_written, bytes_remaining); + if (ret == -1) + { + if (errno == EINTR) + goto again; + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error writing nonce file at `%s': %s"), + server->priv->nonce_file, + strerror (errno)); + goto out; + } + bytes_written += ret; + bytes_remaining -= ret; + } + close (fd); + server->priv->client_address = g_strdup_printf ("nonce-tcp:host=%s,port=%d,noncefile=%s", + host, + port_num, + server->priv->nonce_file); + } + else + { + server->priv->client_address = g_strdup_printf ("tcp:host=%s,port=%d", host, port_num); + } + server->priv->is_using_listener = TRUE; + ret = TRUE; + + out: + g_list_foreach (resolved_addresses, (GFunc) g_object_unref, NULL); + g_list_free (resolved_addresses); + g_object_unref (resolver); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GDBusServer *server; + GDBusConnection *connection; +} EmitIdleData; + +static void +emit_idle_data_free (EmitIdleData *data) +{ + g_object_unref (data->server); + g_object_unref (data->connection); + g_free (data); +} + +static gboolean +emit_new_connection_in_idle (gpointer user_data) +{ + EmitIdleData *data = user_data; + + g_signal_emit (data->server, + _signals[NEW_CONNECTION_SIGNAL], + 0, + data->connection); + g_object_unref (data->connection); + + return FALSE; +} + +/* Called in new thread */ +static gboolean +on_run (GSocketService *service, + GSocketConnection *socket_connection, + GObject *source_object, + gpointer user_data) +{ + GDBusServer *server = G_DBUS_SERVER (user_data); + GDBusConnection *connection; + GDBusConnectionFlags connection_flags; + + if (server->priv->nonce != NULL) + { + gchar buf[16]; + gsize bytes_read; + + if (!g_input_stream_read_all (g_io_stream_get_input_stream (G_IO_STREAM (socket_connection)), + buf, + 16, + &bytes_read, + NULL, /* GCancellable */ + NULL)) /* GError */ + goto out; + + if (bytes_read != 16) + goto out; + + if (memcmp (buf, server->priv->nonce, 16) != 0) + goto out; + } + + connection_flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER; + if (server->priv->flags & G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS) + connection_flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; + + connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), + server->priv->guid, + connection_flags, + server->priv->authentication_observer, + NULL, /* GCancellable */ + NULL); /* GError */ + if (connection == NULL) + goto out; + + if (server->priv->flags & G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) + { + g_signal_emit (server, + _signals[NEW_CONNECTION_SIGNAL], + 0, + connection); + g_object_unref (connection); + } + else + { + GSource *idle_source; + EmitIdleData *data; + + data = g_new0 (EmitIdleData, 1); + data->server = g_object_ref (server); + data->connection = g_object_ref (connection); + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + emit_new_connection_in_idle, + data, + (GDestroyNotify) emit_idle_data_free); + g_source_attach (idle_source, server->priv->main_context_at_construction); + g_source_unref (idle_source); + } + + out: + return TRUE; +} + +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GDBusServer *server = G_DBUS_SERVER (initable); + gboolean ret; + guint n; + gchar **addr_array; + GError *last_error; + + ret = FALSE; + last_error = NULL; + + if (!g_dbus_is_guid (server->priv->guid)) + { + g_set_error (&last_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("The string `%s' is not a valid D-Bus GUID"), + server->priv->guid); + goto out; + } + + server->priv->listener = G_SOCKET_LISTENER (g_threaded_socket_service_new (-1)); + + addr_array = g_strsplit (server->priv->address, ";", 0); + last_error = NULL; + for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++) + { + const gchar *address_entry = addr_array[n]; + GHashTable *key_value_pairs; + gchar *transport_name; + GError *this_error; + + this_error = NULL; + if (g_dbus_is_supported_address (address_entry, + &this_error) && + _g_dbus_address_parse_entry (address_entry, + &transport_name, + &key_value_pairs, + &this_error)) + { + + if (FALSE) + { + } +#ifdef G_OS_UNIX + else if (g_strcmp0 (transport_name, "unix") == 0) + { + ret = try_unix (server, address_entry, key_value_pairs, &this_error); + } +#endif + else if (g_strcmp0 (transport_name, "tcp") == 0) + { + ret = try_tcp (server, address_entry, key_value_pairs, FALSE, &this_error); + } + else if (g_strcmp0 (transport_name, "nonce-tcp") == 0) + { + ret = try_tcp (server, address_entry, key_value_pairs, TRUE, &this_error); + } + else + { + g_set_error (&this_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Cannot listen on unsupported transport `%s'"), + transport_name); + } + + g_free (transport_name); + if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); + + if (ret) + { + g_assert (this_error == NULL); + goto out; + } + } + + if (this_error != NULL) + { + if (last_error != NULL) + g_error_free (last_error); + last_error = this_error; + } + } + + if (!ret) + goto out; + + out: + if (ret) + { + if (last_error != NULL) + g_error_free (last_error); + } + else + { + g_assert (last_error != NULL); + g_propagate_error (error, last_error); + } + return ret; +} + + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = initable_init; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusserver.h b/gio/gdbusserver.h new file mode 100644 index 000000000..404385459 --- /dev/null +++ b/gio/gdbusserver.h @@ -0,0 +1,97 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_SERVER_H__ +#define __G_DBUS_SERVER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DBUS_SERVER (g_dbus_server_get_type ()) +#define G_DBUS_SERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_SERVER, GDBusServer)) +#define G_DBUS_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_SERVER, GDBusServerClass)) +#define G_DBUS_SERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_SERVER, GDBusServerClass)) +#define G_IS_DBUS_SERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_SERVER)) +#define G_IS_DBUS_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_SERVER)) + +typedef struct _GDBusServerClass GDBusServerClass; +typedef struct _GDBusServerPrivate GDBusServerPrivate; + +/** + * GDBusServer: + * + * The #GDBusServer structure contains only private data and + * should only be accessed using the provided API. + */ +struct _GDBusServer +{ + /*< private >*/ + GObject parent_instance; + GDBusServerPrivate *priv; +}; + +/** + * GDBusServerClass: + * @new_connection: Signal class handler for the #GDBusServer::new-connection signal. + * + * Class structure for #GDBusServer. + */ +struct _GDBusServerClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + /* Signals */ + void (*new_connection) (GDBusServer *server, + GDBusConnection *connection); + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); +}; + +GType g_dbus_server_get_type (void) G_GNUC_CONST; +GDBusServer *g_dbus_server_new_sync (const gchar *address, + GDBusServerFlags flags, + const gchar *guid, + GDBusAuthObserver *observer, + GCancellable *cancellable, + GError **error); +const gchar *g_dbus_server_get_client_address (GDBusServer *server); +const gchar *g_dbus_server_get_guid (GDBusServer *server); +GDBusServerFlags g_dbus_server_get_flags (GDBusServer *server); +void g_dbus_server_start (GDBusServer *server); +void g_dbus_server_stop (GDBusServer *server); +gboolean g_dbus_server_is_active (GDBusServer *server); + +G_END_DECLS + +#endif /* __G_DBUS_SERVER_H__ */ diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c new file mode 100644 index 000000000..b1ec0ea1c --- /dev/null +++ b/gio/gdbusutils.c @@ -0,0 +1,364 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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 +#include + +#include + +#include "gdbusutils.h" + +/** + * SECTION:gdbusutils + * @title: D-Bus Utilities + * @short_description: Various utilities related to D-Bus. + * @include: gdbus/gdbus.h + * + * Various utility routines related to D-Bus. + */ + +static gboolean +is_valid_bus_name_character (gint c, + gboolean allow_hyphen) +{ + return + (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_') || + (allow_hyphen && c == '-'); +} + +static gboolean +is_valid_initial_bus_name_character (gint c, + gboolean allow_initial_digit, + gboolean allow_hyphen) +{ + if (allow_initial_digit) + return is_valid_bus_name_character (c, allow_hyphen); + else + return + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_') || + (allow_hyphen && c == '-'); +} + +static gboolean +is_valid_name (const gchar *start, + guint len, + gboolean allow_initial_digit, + gboolean allow_hyphen) +{ + gboolean ret; + const gchar *s; + const gchar *end; + gboolean has_dot; + + ret = FALSE; + + if (len == 0) + goto out; + + s = start; + end = s + len; + has_dot = FALSE; + while (s != end) + { + if (*s == '.') + { + s += 1; + if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, allow_initial_digit, allow_hyphen))) + goto out; + has_dot = TRUE; + } + else if (G_UNLIKELY (!is_valid_bus_name_character (*s, allow_hyphen))) + { + goto out; + } + s += 1; + } + + if (G_UNLIKELY (!has_dot)) + goto out; + + ret = TRUE; + + out: + return ret; +} + +/** + * g_dbus_is_name: + * @string: The string to check. + * + * Checks if @string is a valid D-Bus bus name (either unique or well-known). + * + * Returns: %TRUE if valid, %FALSE otherwise. + */ +gboolean +g_dbus_is_name (const gchar *string) +{ + guint len; + gboolean ret; + const gchar *s; + const gchar *end; + + g_return_val_if_fail (string != NULL, FALSE); + + ret = FALSE; + + len = strlen (string); + if (G_UNLIKELY (len == 0 || len > 255)) + goto out; + + s = string; + end = s + len; + if (*s == ':') + { + /* handle unique name */ + if (!is_valid_name (s + 1, len - 1, TRUE, TRUE)) + goto out; + ret = TRUE; + goto out; + } + else if (G_UNLIKELY (*s == '.')) + { + /* can't start with a . */ + goto out; + } + else if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, FALSE, TRUE))) + goto out; + + ret = is_valid_name (s + 1, len - 1, FALSE, TRUE); + + out: + return ret; +} + +/** + * g_dbus_is_unique_name: + * @string: The string to check. + * + * Checks if @string is a valid D-Bus unique bus name. + * + * Returns: %TRUE if valid, %FALSE otherwise. + */ +gboolean +g_dbus_is_unique_name (const gchar *string) +{ + gboolean ret; + guint len; + + g_return_val_if_fail (string != NULL, FALSE); + + ret = FALSE; + + len = strlen (string); + if (G_UNLIKELY (len == 0 || len > 255)) + goto out; + + if (G_UNLIKELY (*string != ':')) + goto out; + + if (G_UNLIKELY (!is_valid_name (string + 1, len - 1, TRUE, TRUE))) + goto out; + + ret = TRUE; + + out: + return ret; +} + +/** + * g_dbus_is_member_name: + * @string: The string to check. + * + * Checks if @string is a valid D-Bus member (e.g. signal or method) name. + * + * Returns: %TRUE if valid, %FALSE otherwise. + */ +gboolean +g_dbus_is_member_name (const gchar *string) +{ + gboolean ret; + guint n; + + ret = FALSE; + if (G_UNLIKELY (string == NULL)) + goto out; + + if (G_UNLIKELY (!is_valid_initial_bus_name_character (string[0], FALSE, FALSE))) + goto out; + + for (n = 1; string[n] != '\0'; n++) + { + if (G_UNLIKELY (!is_valid_bus_name_character (string[n], FALSE))) + { + goto out; + } + } + + ret = TRUE; + + out: + return ret; +} + +/** + * g_dbus_is_interface_name: + * @string: The string to check. + * + * Checks if @string is a valid D-Bus interface name. + * + * Returns: %TRUE if valid, %FALSE otherwise. + */ +gboolean +g_dbus_is_interface_name (const gchar *string) +{ + guint len; + gboolean ret; + const gchar *s; + const gchar *end; + + g_return_val_if_fail (string != NULL, FALSE); + + ret = FALSE; + + len = strlen (string); + if (G_UNLIKELY (len == 0 || len > 255)) + goto out; + + s = string; + end = s + len; + if (G_UNLIKELY (*s == '.')) + { + /* can't start with a . */ + goto out; + } + else if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, FALSE, FALSE))) + goto out; + + ret = is_valid_name (s + 1, len - 1, FALSE, FALSE); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* TODO: maybe move to glib? if so, it should conform to http://en.wikipedia.org/wiki/Guid and/or + * http://tools.ietf.org/html/rfc4122 - specifically it should have hyphens then. + */ + +/** + * g_dbus_generate_guid: + * + * Generate a D-Bus GUID that can be used with + * e.g. g_dbus_connection_new(). + * + * See the D-Bus specification regarding what strings are valid D-Bus + * GUID (for example, D-Bus GUIDs are not RFC-4122 compliant). + * + * Returns: A valid D-Bus GUID. Free with g_free(). + */ +gchar * +g_dbus_generate_guid (void) +{ + GString *s; + GTimeVal now; + guint32 r1; + guint32 r2; + guint32 r3; + + s = g_string_new (NULL); + + r1 = g_random_int (); + r2 = g_random_int (); + r3 = g_random_int (); + g_get_current_time (&now); + + g_string_append_printf (s, "%08x", r1); + g_string_append_printf (s, "%08x", r2); + g_string_append_printf (s, "%08x", r3); + g_string_append_printf (s, "%08x", (guint32) now.tv_sec); + + return g_string_free (s, FALSE); +} + +/** + * g_dbus_is_guid: + * @string: The string to check. + * + * Checks if @string is a D-Bus GUID. + * + * See the D-Bus specification regarding what strings are valid D-Bus + * GUID (for example, D-Bus GUIDs are not RFC-4122 compliant). + * + * Returns: %TRUE if @string is a guid, %FALSE otherwise. + */ +gboolean +g_dbus_is_guid (const gchar *string) +{ + gboolean ret; + guint n; + + g_return_val_if_fail (string != NULL, FALSE); + + ret = FALSE; + + for (n = 0; n < 32; n++) + { + if (!g_ascii_isxdigit (string[n])) + goto out; + } + if (string[32] != '\0') + goto out; + + ret = TRUE; + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + + +/** + * g_dbus_is_activated: + * + * Determine if the process has been activated by a message bus. + * + * Returns: %TRUE if this process has been started by a message bus, %FALSE otherwise. + */ +gboolean +g_dbus_is_activated (void) +{ + /* TODO: technically this will make child processes forked by us + * return TRUE too.. + */ + return g_getenv ("DBUS_STARTER_BUS_TYPE") != NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusutils.h b/gio/gdbusutils.h new file mode 100644 index 000000000..e3e606bb0 --- /dev/null +++ b/gio/gdbusutils.h @@ -0,0 +1,42 @@ +/* GDBus - GLib D-Bus Library + * + * Copyright (C) 2008-2009 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_UTILS_H__ +#define __G_DBUS_UTILS_H__ + +#include + +G_BEGIN_DECLS + +gboolean g_dbus_is_guid (const gchar *string); +gchar *g_dbus_generate_guid (void); + +gboolean g_dbus_is_name (const gchar *string); +gboolean g_dbus_is_unique_name (const gchar *string); +gboolean g_dbus_is_member_name (const gchar *string); +gboolean g_dbus_is_interface_name (const gchar *string); + +gboolean g_dbus_is_activated (void); + +G_END_DECLS + +#endif /* __G_DBUS_UTILS_H__ */ diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list index 8439304bf..8473ea70d 100644 --- a/gio/gio-marshal.list +++ b/gio/gio-marshal.list @@ -6,3 +6,5 @@ BOOLEAN:OBJECT,OBJECT VOID:STRING,BOXED,BOXED BOOL:POINTER,INT BOOL:UINT +VOID:STRING,STRING,BOXED +VOID:BOOL,BOXED diff --git a/gio/gio.h b/gio/gio.h index ac0dc7fbf..ca55cd674 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -97,6 +97,22 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #undef __GIO_GIO_H_INSIDE__ #endif /* __G_IO_H__ */ diff --git a/gio/gioenums.h b/gio/gioenums.h index 9c98ffd9b..c1d2e9f60 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -429,6 +429,11 @@ typedef enum { * @G_IO_ERROR_ADDRESS_IN_USE: The requested address is already in use. Since 2.22 * @G_IO_ERROR_PARTIAL_INPUT: Need more input to finish operation. Since 2.24 * @G_IO_ERROR_INVALID_DATA: There input data was invalid. Since 2.24 + * @G_IO_ERROR_DBUS_ERROR: A remote object generated an error that + * doesn't correspond to a locally registered #GError error + * domain. Use g_dbus_error_get_remote_error() to extract the D-Bus + * error name and g_dbus_error_strip_remote_error() to fix up the + * message so it matches what was received on the wire. Since 2.26. * * Error codes returned by GIO functions. * @@ -469,7 +474,8 @@ typedef enum { G_IO_ERROR_NOT_INITIALIZED, G_IO_ERROR_ADDRESS_IN_USE, G_IO_ERROR_PARTIAL_INPUT, - G_IO_ERROR_INVALID_DATA + G_IO_ERROR_INVALID_DATA, + G_IO_ERROR_DBUS_ERROR } GIOErrorEnum; @@ -732,6 +738,370 @@ typedef enum { G_UNIX_SOCKET_ADDRESS_ABSTRACT_PADDED } GUnixSocketAddressType; +/** + * GBusType: + * @G_BUS_TYPE_NONE: Not a message bus connection. + * @G_BUS_TYPE_SESSION: The login session message bus. + * @G_BUS_TYPE_SYSTEM: The system-wide message bus. + * @G_BUS_TYPE_STARTER: Connect to the bus that activated the program. + * + * An enumeration to specify the type of a #GDBusConnection. + */ +typedef enum +{ + G_BUS_TYPE_NONE = -1, + G_BUS_TYPE_SESSION = 0, + G_BUS_TYPE_SYSTEM = 1, + G_BUS_TYPE_STARTER = 2 +} GBusType; + +/** + * GBusNameOwnerFlags: + * @G_BUS_NAME_OWNER_FLAGS_NONE: No flags set. + * @G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT: Allow another message bus connection to claim the the name. + * @G_BUS_NAME_OWNER_FLAGS_REPLACE: If another message bus connection owns the name and have + * specified #G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, then take the name from the other connection. + * + * Flags used in g_bus_own_name(). + */ +typedef enum +{ + G_BUS_NAME_OWNER_FLAGS_NONE = 0, /*< nick=none >*/ + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT = (1<<0), /*< nick=allow-replacement >*/ + G_BUS_NAME_OWNER_FLAGS_REPLACE = (1<<1), /*< nick=replace >*/ +} GBusNameOwnerFlags; + +/** + * GBusNameWatcherFlags: + * @G_BUS_NAME_WATCHER_FLAGS_NONE: No flags set. + * @G_BUS_NAME_WATCHER_FLAGS_AUTO_START: If no-one owns the name when + * beginning to watch the name, ask the bus to launch an owner for the + * name. + * + * Flags used in g_bus_watch_name(). + */ +typedef enum +{ + G_BUS_NAME_WATCHER_FLAGS_NONE = 0, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START = (1<<0) +} GBusNameWatcherFlags; + +/** + * GDBusProxyFlags: + * @G_DBUS_PROXY_FLAGS_NONE: No flags set. + * @G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES: Don't load properties. + * @G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS: Don't connect to signals on the remote object. + * + * Flags used when constructing an instance of a #GDBusProxy derived class. + */ +typedef enum +{ + G_DBUS_PROXY_FLAGS_NONE = 0, /*< nick=none >*/ + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), /*< nick=do-not-load-properties >*/ + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1), /*< nick=do-not-connect-signals >*/ +} GDBusProxyFlags; + +/** + * GDBusError: + * @G_DBUS_ERROR_FAILED: + * A generic error; "something went wrong" - see the error message for + * more. + * @G_DBUS_ERROR_NO_MEMORY: + * There was not enough memory to complete an operation. + * @G_DBUS_ERROR_SERVICE_UNKNOWN: + * The bus doesn't know how to launch a service to supply the bus name + * you wanted. + * @G_DBUS_ERROR_NAME_HAS_NO_OWNER: + * The bus name you referenced doesn't exist (i.e. no application owns + * it). + * @G_DBUS_ERROR_NO_REPLY: + * No reply to a message expecting one, usually means a timeout occurred. + * @G_DBUS_ERROR_IO_ERROR: + * Something went wrong reading or writing to a socket, for example. + * @G_DBUS_ERROR_BAD_ADDRESS: + * A D-Bus bus address was malformed. + * @G_DBUS_ERROR_NOT_SUPPORTED: + * Requested operation isn't supported (like ENOSYS on UNIX). + * @G_DBUS_ERROR_LIMITS_EXCEEDED: + * Some limited resource is exhausted. + * @G_DBUS_ERROR_ACCESS_DENIED: + * Security restrictions don't allow doing what you're trying to do. + * @G_DBUS_ERROR_AUTH_FAILED: + * Authentication didn't work. + * @G_DBUS_ERROR_NO_SERVER: + * Unable to connect to server (probably caused by ECONNREFUSED on a + * socket). + * @G_DBUS_ERROR_TIMEOUT: + * Certain timeout errors, possibly ETIMEDOUT on a socket. Note that + * %G_DBUS_ERROR_NO_REPLY is used for message reply timeouts. Warning: + * this is confusingly-named given that %G_DBUS_ERROR_TIMED_OUT also + * exists. We can't fix it for compatibility reasons so just be + * careful. + * @G_DBUS_ERROR_NO_NETWORK: + * No network access (probably ENETUNREACH on a socket). + * @G_DBUS_ERROR_ADDRESS_IN_USE: + * Can't bind a socket since its address is in use (i.e. EADDRINUSE). + * @G_DBUS_ERROR_DISCONNECTED: + * The connection is disconnected and you're trying to use it. + * @G_DBUS_ERROR_INVALID_ARGS: + * Invalid arguments passed to a method call. + * @G_DBUS_ERROR_FILE_NOT_FOUND: + * Missing file. + * @G_DBUS_ERROR_FILE_EXISTS: + * Existing file and the operation you're using does not silently overwrite. + * @G_DBUS_ERROR_UNKNOWN_METHOD: + * Method name you invoked isn't known by the object you invoked it on. + * @G_DBUS_ERROR_TIMED_OUT: + * Certain timeout errors, e.g. while starting a service. Warning: this is + * confusingly-named given that %G_DBUS_ERROR_TIMEOUT also exists. We + * can't fix it for compatibility reasons so just be careful. + * @G_DBUS_ERROR_MATCH_RULE_NOT_FOUND: + * Tried to remove or modify a match rule that didn't exist. + * @G_DBUS_ERROR_MATCH_RULE_INVALID: + * The match rule isn't syntactically valid. + * @G_DBUS_ERROR_SPAWN_EXEC_FAILED: + * While starting a new process, the exec() call failed. + * @G_DBUS_ERROR_SPAWN_FORK_FAILED: + * While starting a new process, the fork() call failed. + * @G_DBUS_ERROR_SPAWN_CHILD_EXITED: + * While starting a new process, the child exited with a status code. + * @G_DBUS_ERROR_SPAWN_CHILD_SIGNALED: + * While starting a new process, the child exited on a signal. + * @G_DBUS_ERROR_SPAWN_FAILED: + * While starting a new process, something went wrong. + * @G_DBUS_ERROR_SPAWN_SETUP_FAILED: + * We failed to setup the environment correctly. + * @G_DBUS_ERROR_SPAWN_CONFIG_INVALID: + * We failed to setup the config parser correctly. + * @G_DBUS_ERROR_SPAWN_SERVICE_INVALID: + * Bus name was not valid. + * @G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND: + * Service file not found in system-services directory. + * @G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID: + * Permissions are incorrect on the setuid helper. + * @G_DBUS_ERROR_SPAWN_FILE_INVALID: + * Service file invalid (Name, User or Exec missing). + * @G_DBUS_ERROR_SPAWN_NO_MEMORY: + * Tried to get a UNIX process ID and it wasn't available. + * @G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN: + * Tried to get a UNIX process ID and it wasn't available. + * @G_DBUS_ERROR_INVALID_SIGNATURE: + * A type signature is not valid. + * @G_DBUS_ERROR_INVALID_FILE_CONTENT: + * A file contains invalid syntax or is otherwise broken. + * @G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN: + * Asked for SELinux security context and it wasn't available. + * @G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN: + * Asked for ADT audit data and it wasn't available. + * @G_DBUS_ERROR_OBJECT_PATH_IN_USE: + * There's already an object with the requested object path. + * + * Error codes for the %G_DBUS_ERROR error domain. + */ +typedef enum +{ + /* Well-known errors in the org.freedesktop.DBus.Error namespace */ + G_DBUS_ERROR_FAILED, /* org.freedesktop.DBus.Error.Failed */ + G_DBUS_ERROR_NO_MEMORY, /* org.freedesktop.DBus.Error.NoMemory */ + G_DBUS_ERROR_SERVICE_UNKNOWN, /* org.freedesktop.DBus.Error.ServiceUnknown */ + G_DBUS_ERROR_NAME_HAS_NO_OWNER, /* org.freedesktop.DBus.Error.NameHasNoOwner */ + G_DBUS_ERROR_NO_REPLY, /* org.freedesktop.DBus.Error.NoReply */ + G_DBUS_ERROR_IO_ERROR, /* org.freedesktop.DBus.Error.IOError */ + G_DBUS_ERROR_BAD_ADDRESS, /* org.freedesktop.DBus.Error.BadAddress */ + G_DBUS_ERROR_NOT_SUPPORTED, /* org.freedesktop.DBus.Error.NotSupported */ + G_DBUS_ERROR_LIMITS_EXCEEDED, /* org.freedesktop.DBus.Error.LimitsExceeded */ + G_DBUS_ERROR_ACCESS_DENIED, /* org.freedesktop.DBus.Error.AccessDenied */ + G_DBUS_ERROR_AUTH_FAILED, /* org.freedesktop.DBus.Error.AuthFailed */ + G_DBUS_ERROR_NO_SERVER, /* org.freedesktop.DBus.Error.NoServer */ + G_DBUS_ERROR_TIMEOUT, /* org.freedesktop.DBus.Error.Timeout */ + G_DBUS_ERROR_NO_NETWORK, /* org.freedesktop.DBus.Error.NoNetwork */ + G_DBUS_ERROR_ADDRESS_IN_USE, /* org.freedesktop.DBus.Error.AddressInUse */ + G_DBUS_ERROR_DISCONNECTED, /* org.freedesktop.DBus.Error.Disconnected */ + G_DBUS_ERROR_INVALID_ARGS, /* org.freedesktop.DBus.Error.InvalidArgs */ + G_DBUS_ERROR_FILE_NOT_FOUND, /* org.freedesktop.DBus.Error.FileNotFound */ + G_DBUS_ERROR_FILE_EXISTS, /* org.freedesktop.DBus.Error.FileExists */ + G_DBUS_ERROR_UNKNOWN_METHOD, /* org.freedesktop.DBus.Error.UnknownMethod */ + G_DBUS_ERROR_TIMED_OUT, /* org.freedesktop.DBus.Error.TimedOut */ + G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, /* org.freedesktop.DBus.Error.MatchRuleNotFound */ + G_DBUS_ERROR_MATCH_RULE_INVALID, /* org.freedesktop.DBus.Error.MatchRuleInvalid */ + G_DBUS_ERROR_SPAWN_EXEC_FAILED, /* org.freedesktop.DBus.Error.Spawn.ExecFailed */ + G_DBUS_ERROR_SPAWN_FORK_FAILED, /* org.freedesktop.DBus.Error.Spawn.ForkFailed */ + G_DBUS_ERROR_SPAWN_CHILD_EXITED, /* org.freedesktop.DBus.Error.Spawn.ChildExited */ + G_DBUS_ERROR_SPAWN_CHILD_SIGNALED, /* org.freedesktop.DBus.Error.Spawn.ChildSignaled */ + G_DBUS_ERROR_SPAWN_FAILED, /* org.freedesktop.DBus.Error.Spawn.Failed */ + G_DBUS_ERROR_SPAWN_SETUP_FAILED, /* org.freedesktop.DBus.Error.Spawn.FailedToSetup */ + G_DBUS_ERROR_SPAWN_CONFIG_INVALID, /* org.freedesktop.DBus.Error.Spawn.ConfigInvalid */ + G_DBUS_ERROR_SPAWN_SERVICE_INVALID, /* org.freedesktop.DBus.Error.Spawn.ServiceNotValid */ + G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND, /* org.freedesktop.DBus.Error.Spawn.ServiceNotFound */ + G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID, /* org.freedesktop.DBus.Error.Spawn.PermissionsInvalid */ + G_DBUS_ERROR_SPAWN_FILE_INVALID, /* org.freedesktop.DBus.Error.Spawn.FileInvalid */ + G_DBUS_ERROR_SPAWN_NO_MEMORY, /* org.freedesktop.DBus.Error.Spawn.NoMemory */ + G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, /* org.freedesktop.DBus.Error.UnixProcessIdUnknown */ + G_DBUS_ERROR_INVALID_SIGNATURE, /* org.freedesktop.DBus.Error.InvalidSignature */ + G_DBUS_ERROR_INVALID_FILE_CONTENT, /* org.freedesktop.DBus.Error.InvalidFileContent */ + G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, /* org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown */ + G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN, /* org.freedesktop.DBus.Error.AdtAuditDataUnknown */ + G_DBUS_ERROR_OBJECT_PATH_IN_USE, /* org.freedesktop.DBus.Error.ObjectPathInUse */ +} GDBusError; +/* TODO: remember to update g_dbus_error_quark() in gdbuserror.c if you extend this enumeration */ + +/** + * GDBusConnectionFlags: + * @G_DBUS_CONNECTION_FLAGS_NONE: No flags set. + * @G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT: Perform authentication against server. + * @G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER: Perform authentication against client. + * @G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS: When + * authenticating as a server, allow the anonymous authentication + * method. + * @G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION: Pass this flag if connecting to a peer that is a + * message bus. This means that the Hello() method will be invoked as part of the connection setup. + * + * Flags used when creating a new #GDBusConnection. + */ +typedef enum { + G_DBUS_CONNECTION_FLAGS_NONE = 0, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT = (1<<0), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER = (1<<1), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS = (1<<2), + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION = (1<<3) +} GDBusConnectionFlags; + +/** + * GDBusCapabilityFlags: + * @G_DBUS_CAPABILITY_FLAGS_NONE: No flags set. + * @G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING: The connection + * supports exchanging UNIX file descriptors with the remote peer. + * + * Capabilities negotiated with the remote peer. + */ +typedef enum { + G_DBUS_CAPABILITY_FLAGS_NONE = 0, + G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING = (1<<0), +} GDBusCapabilityFlags; + +/** + * GDBusInvokeMethodFlags: + * @G_DBUS_INVOKE_METHOD_FLAGS_NONE: No flags set. + * @G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START: The bus must not launch + * an owner for the destination name in response to this method + * invocation. + * + * Flags used in g_dbus_connection_invoke_method() and similar APIs. + */ +typedef enum { + G_DBUS_INVOKE_METHOD_FLAGS_NONE = 0, + G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START = (1<<0), +} GDBusInvokeMethodFlags; + +/** + * GDBusMessageType: + * @G_DBUS_MESSAGE_TYPE_INVALID: Message is of invalid type. + * @G_DBUS_MESSAGE_TYPE_METHOD_CALL: Method call. + * @G_DBUS_MESSAGE_TYPE_METHOD_RETURN: Method reply. + * @G_DBUS_MESSAGE_TYPE_ERROR: Error reply. + * @G_DBUS_MESSAGE_TYPE_SIGNAL: Signal emission. + * + * Message types used in #GDBusMessage. + */ +typedef enum { + G_DBUS_MESSAGE_TYPE_INVALID, + G_DBUS_MESSAGE_TYPE_METHOD_CALL, + G_DBUS_MESSAGE_TYPE_METHOD_RETURN, + G_DBUS_MESSAGE_TYPE_ERROR, + G_DBUS_MESSAGE_TYPE_SIGNAL +} GDBusMessageType; + +/** + * GDBusMessageFlags: + * @G_DBUS_MESSAGE_FLAGS_NONE: No flags set. + * @G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED: A reply is not expected. + * @G_DBUS_MESSAGE_FLAGS_NO_AUTO_START: The bus must not launch an + * owner for the destination name in response to this message. + * + * Message flags used in #GDBusMessage. + */ +typedef enum { + G_DBUS_MESSAGE_FLAGS_NONE = 0, + G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED = (1<<0), + G_DBUS_MESSAGE_FLAGS_NO_AUTO_START = (1<<1) +} GDBusMessageFlags; + +/** + * GDBusMessageHeaderField: + * @G_DBUS_MESSAGE_HEADER_FIELD_INVALID: Not a valid header field. + * @G_DBUS_MESSAGE_HEADER_FIELD_PATH: The object path. + * @G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE: The interface name. + * @G_DBUS_MESSAGE_HEADER_FIELD_MEMBER: The method or signal name. + * @G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME: The name of the error that occurred. + * @G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL: The serial number the message is a reply to. + * @G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: The name the message is intended for. + * @G_DBUS_MESSAGE_HEADER_FIELD_SENDER: Unique name of the sender of the message (filled in by the bus). + * @G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE: The signature of the message body. + * @G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS: The number of UNIX file descriptors that accompany the message. + * + * Header fields used in #GDBusMessage. + */ +typedef enum { + G_DBUS_MESSAGE_HEADER_FIELD_INVALID, + G_DBUS_MESSAGE_HEADER_FIELD_PATH, + G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, + G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, + G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, + G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, + G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, + G_DBUS_MESSAGE_HEADER_FIELD_SENDER, + G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, + G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS +} GDBusMessageHeaderField; + +/** + * GDBusPropertyInfoFlags: + * @G_DBUS_PROPERTY_INFO_FLAGS_NONE: No flags set. + * @G_DBUS_PROPERTY_INFO_FLAGS_READABLE: Property is readable. + * @G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE: Property is writable. + * + * Flags describing the access control of a D-Bus property. + */ +typedef enum +{ + G_DBUS_PROPERTY_INFO_FLAGS_NONE = 0, + G_DBUS_PROPERTY_INFO_FLAGS_READABLE = (1<<0), + G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE = (1<<1), +} GDBusPropertyInfoFlags; + +/** + * GDBusSubtreeFlags: + * @G_DBUS_SUBTREE_FLAGS_NONE: No flags set. + * @G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES: Method calls to objects not in the enumerated range + * will still be dispatched. This is useful if you want + * to dynamically spawn objects in the subtree. + * + * Flags passed to g_dbus_connection_register_subtree(). + */ +typedef enum +{ + G_DBUS_SUBTREE_FLAGS_NONE = 0, + G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES = (1<<0), +} GDBusSubtreeFlags; + +/** + * GDBusServerFlags: + * @G_DBUS_SERVER_FLAGS_NONE: No flags set. + * @G_DBUS_SERVER_FLAGS_RUN_IN_THREAD: All #GDBusServer::new-connection + * signals will run in separated dedicated threads (see signal for + * details). + * @G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS: Allow the anonymous + * authentication method. + * + * Flags used when creating a #GDBusServer. + */ +typedef enum +{ + G_DBUS_SERVER_FLAGS_NONE = 0, + G_DBUS_SERVER_FLAGS_RUN_IN_THREAD = (1<<0), + G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS = (1<<1) +} GDBusServerFlags; + G_END_DECLS #endif /* __GIO_ENUMS_H__ */ diff --git a/gio/giotypes.h b/gio/giotypes.h index 2abc87dd6..22a00266c 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -333,6 +333,25 @@ struct _GOutputVector { gsize size; }; +typedef struct _GCredentials GCredentials; +typedef struct _GUnixCredentialsMessage GUnixCredentialsMessage; +typedef struct _GDBusMessage GDBusMessage; +typedef struct _GDBusConnection GDBusConnection; +typedef struct _GMessageBusConnection GMessageBusConnection; +typedef struct _GDBusProxy GDBusProxy; +typedef struct _GDBusMethodInvocation GDBusMethodInvocation; +typedef struct _GDBusServer GDBusServer; +typedef struct _GDBusAuthObserver GDBusAuthObserver; +typedef struct _GDBusErrorEntry GDBusErrorEntry; +typedef struct _GDBusInterfaceVTable GDBusInterfaceVTable; +typedef struct _GDBusSubtreeVTable GDBusSubtreeVTable; +typedef struct _GDBusAnnotationInfo GDBusAnnotationInfo; +typedef struct _GDBusArgInfo GDBusArgInfo; +typedef struct _GDBusMethodInfo GDBusMethodInfo; +typedef struct _GDBusSignalInfo GDBusSignalInfo; +typedef struct _GDBusPropertyInfo GDBusPropertyInfo; +typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo; +typedef struct _GDBusNodeInfo GDBusNodeInfo; G_END_DECLS diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c new file mode 100644 index 000000000..6af8c297c --- /dev/null +++ b/gio/gunixcredentialsmessage.c @@ -0,0 +1,341 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2009 Codethink Limited + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Authors: David Zeuthen + */ + +/** + * SECTION: gunixcredentialsmessage + * @title: GUnixCredentialsMessage + * @short_description: A GSocketControlMessage containing credentials + * @see_also: #GUnixConnection, #GSocketControlMessage + * + * This #GSocketControlMessage contains a #GCredentials instance. It + * may be sent using g_socket_send_message() and received using + * g_socket_receive_message() over UNIX sockets (ie: sockets in the + * %G_SOCKET_ADDRESS_UNIX family). + * + * For an easier way to send and receive credentials over + * stream-oriented UNIX sockets, see g_unix_connection_send_credentials() and + * g_unix_connection_receive_credentials(). + **/ + +#include "config.h" + +#include + +/* ---------------------------------------------------------------------------------------------------- */ +#ifdef __linux__ + +#define _GNU_SOURCE +#define __USE_GNU +#include +#include +#include +#include +#include +#define G_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1 + +#else +/* TODO: please add support for your UNIX flavor */ +#define G_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 0 +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +#include +#include + +#include "gunixcredentialsmessage.h" +#include "gcredentials.h" + +struct _GUnixCredentialsMessagePrivate +{ + GCredentials *credentials; +}; + +enum +{ + PROP_0, + PROP_CREDENTIALS +}; + +G_DEFINE_TYPE (GUnixCredentialsMessage, g_unix_credentials_message, G_TYPE_SOCKET_CONTROL_MESSAGE); + +static gsize +g_unix_credentials_message_get_size (GSocketControlMessage *message) +{ +#ifdef __linux__ + return sizeof (struct ucred); +#else + return 0; +#endif +} + +static int +g_unix_credentials_message_get_level (GSocketControlMessage *message) +{ + return SOL_SOCKET; +} + +static int +g_unix_credentials_message_get_msg_type (GSocketControlMessage *message) +{ +#ifdef __linux__ + return SCM_CREDENTIALS; +#else + return 0; +#endif +} + +static GSocketControlMessage * +g_unix_credentials_message_deserialize (gint level, + gint type, + gsize size, + gpointer data) +{ + GSocketControlMessage *message; + + message = NULL; + +#ifdef __linux__ + { + GCredentials *credentials; + struct ucred *ucred; + + if (level != SOL_SOCKET || type != SCM_CREDENTIALS) + goto out; + + if (size != sizeof (struct ucred)) + { + g_warning ("Expected a struct ucred (%" G_GSIZE_FORMAT " bytes) but " + "got %" G_GSIZE_FORMAT " bytes of data", + sizeof (struct ucred), + size); + goto out; + } + + ucred = data; + + credentials = g_credentials_new (); + g_credentials_set_unix_user (credentials, ucred->uid); + g_credentials_set_unix_group (credentials, ucred->gid); + g_credentials_set_unix_process (credentials, ucred->pid); + message = g_unix_credentials_message_new_with_credentials (credentials); + g_object_unref (credentials); + out: + ; + } +#endif + + return message; +} + +static void +g_unix_credentials_message_serialize (GSocketControlMessage *_message, + gpointer data) +{ + GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (_message); +#ifdef __linux__ + { + struct ucred *ucred = data; + ucred->uid = g_credentials_get_unix_user (message->priv->credentials); + ucred->gid = g_credentials_get_unix_group (message->priv->credentials); + ucred->pid = g_credentials_get_unix_process (message->priv->credentials); + } +#endif +} + +static void +g_unix_credentials_message_finalize (GObject *object) +{ + GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object); + + if (message->priv->credentials != NULL) + g_object_unref (message->priv->credentials); + + if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize (object); +} + +static void +g_unix_credentials_message_init (GUnixCredentialsMessage *message) +{ + message->priv = G_TYPE_INSTANCE_GET_PRIVATE (message, + G_TYPE_UNIX_CREDENTIALS_MESSAGE, + GUnixCredentialsMessagePrivate); +} + +static void +g_unix_credentials_message_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object); + + switch (prop_id) + { + case PROP_CREDENTIALS: + g_value_set_object (value, message->priv->credentials); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_unix_credentials_message_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object); + + switch (prop_id) + { + case PROP_CREDENTIALS: + message->priv->credentials = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_unix_credentials_message_constructed (GObject *object) +{ + GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object); + + if (message->priv->credentials == NULL) + message->priv->credentials = g_credentials_new_for_process (); + + if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed != NULL) + G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed (object); +} + +static void +g_unix_credentials_message_class_init (GUnixCredentialsMessageClass *class) +{ + GSocketControlMessageClass *scm_class; + GObjectClass *gobject_class; + + g_type_class_add_private (class, sizeof (GUnixCredentialsMessagePrivate)); + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->get_property = g_unix_credentials_message_get_property; + gobject_class->set_property = g_unix_credentials_message_set_property; + gobject_class->finalize = g_unix_credentials_message_finalize; + gobject_class->constructed = g_unix_credentials_message_constructed; + + scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class); + scm_class->get_size = g_unix_credentials_message_get_size; + scm_class->get_level = g_unix_credentials_message_get_level; + scm_class->get_type = g_unix_credentials_message_get_msg_type; + scm_class->serialize = g_unix_credentials_message_serialize; + scm_class->deserialize = g_unix_credentials_message_deserialize; + + /** + * GUnixCredentialsMessage:credentials: + * + * The credentials stored in the message. + */ + g_object_class_install_property (gobject_class, + PROP_CREDENTIALS, + g_param_spec_object ("credentials", + _("Credentials"), + _("The credentials stored in the message"), + G_TYPE_CREDENTIALS, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_unix_credentials_message_is_supported: + * + * Checks if passing a #GCredential on a #GSocket is supported on this platform. + * + * Returns: %TRUE if supported, %FALSE otherwise + * + * Since: 2.26 + */ +gboolean +g_unix_credentials_message_is_supported (void) +{ + return G_UNIX_CREDENTIALS_MESSAGE_SUPPORTED; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_unix_credentials_message_new: + * + * Creates a new #GUnixCredentialsMessage with credentials matching the current processes. + * + * Returns: a new #GUnixCredentialsMessage + * + * Since: 2.26 + */ +GSocketControlMessage * +g_unix_credentials_message_new (void) +{ + g_return_val_if_fail (g_unix_credentials_message_is_supported (), NULL); + return g_object_new (G_TYPE_UNIX_CREDENTIALS_MESSAGE, + NULL); +} + +/** + * g_unix_credentials_message_new: + * @credentials: A #GCredentials object. + * + * Creates a new #GUnixCredentialsMessage holding @credentials. + * + * Returns: a new #GUnixCredentialsMessage + * + * Since: 2.26 + */ +GSocketControlMessage * +g_unix_credentials_message_new_with_credentials (GCredentials *credentials) +{ + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL); + g_return_val_if_fail (g_unix_credentials_message_is_supported (), NULL); + return g_object_new (G_TYPE_UNIX_CREDENTIALS_MESSAGE, + "credentials", credentials, + NULL); +} + +/** + * g_unix_credentials_message_get_credentials: + * @message: A #GUnixCredentialsMessage. + * + * Gets the credentials stored in @message. + * + * Returns: A #GCredentials instance. Do not free, it is owned by @message. + */ +GCredentials * +g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message) +{ + g_return_val_if_fail (G_IS_UNIX_CREDENTIALS_MESSAGE (message), NULL); + return message->priv->credentials; +} + diff --git a/gio/gunixcredentialsmessage.h b/gio/gunixcredentialsmessage.h new file mode 100644 index 000000000..1db0146e2 --- /dev/null +++ b/gio/gunixcredentialsmessage.h @@ -0,0 +1,68 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2009 Codethink Limited + * + * This program 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 licence 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. + * + * Authors: David Zeuthen + */ + +#ifndef __G_UNIX_CREDENTIALS_MESSAGE_H__ +#define __G_UNIX_CREDENTIALS_MESSAGE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_UNIX_CREDENTIALS_MESSAGE (g_unix_credentials_message_get_type ()) +#define G_UNIX_CREDENTIALS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_CREDENTIALS_MESSAGE, GUnixCredentialsMessage)) +#define G_UNIX_CREDENTIALS_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), G_TYPE_UNIX_CREDENTIALS_MESSAGE, GUnixCredentialsMessageClass)) +#define G_IS_UNIX_CREDENTIALS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_CREDENTIALS_MESSAGE)) +#define G_IS_UNIX_CREDENTIALS_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), G_TYPE_UNIX_CREDENTIALS_MESSAGE)) +#define G_UNIX_CREDENTIALS_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_UNIX_CREDENTIALS_MESSAGE, GUnixCredentialsMessageClass)) + +typedef struct _GUnixCredentialsMessagePrivate GUnixCredentialsMessagePrivate; +typedef struct _GUnixCredentialsMessageClass GUnixCredentialsMessageClass; + +struct _GUnixCredentialsMessageClass +{ + GSocketControlMessageClass parent_class; + + /*< private >*/ + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); +}; + +struct _GUnixCredentialsMessage +{ + GSocketControlMessage parent_instance; + GUnixCredentialsMessagePrivate *priv; +}; + +GType g_unix_credentials_message_get_type (void) G_GNUC_CONST; +GSocketControlMessage *g_unix_credentials_message_new (void); +GSocketControlMessage *g_unix_credentials_message_new_with_credentials (GCredentials *credentials); +GCredentials *g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message); + +gboolean g_unix_credentials_message_is_supported (void); + +G_END_DECLS + +#endif /* __G_UNIX_CREDENTIALS_MESSAGE_H__ */ diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 8be6313ce..4aab457e4 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -1,3 +1,5 @@ +NULL = + include $(top_srcdir)/Makefile.decl INCLUDES = \ @@ -35,19 +37,39 @@ TEST_PROGS += \ srvtarget \ contexts \ gsettings \ - gschema-compile + gschema-compile \ + gdbus-addresses \ + gdbus-connection \ + gdbus-names \ + gdbus-proxy \ + gdbus-introspection \ + gdbus-threading \ + gdbus-export \ + gdbus-error \ + gdbus-peer \ + gdbus-exit-on-close \ + $(NULL) + +SAMPLE_PROGS = \ + resolver \ + socket-server \ + socket-client \ + echo-server \ + httpd \ + send-data \ + filter-cat \ + gdbus-example-own-name \ + gdbus-example-watch-name \ + gdbus-example-watch-proxy \ + gdbus-example-server \ + gdbus-example-subtree \ + gdbus-example-peer \ + $(NULL) -SAMPLE_PROGS = \ - resolver \ - socket-server \ - socket-client \ - echo-server \ - httpd \ - send-data \ - filter-cat if OS_UNIX TEST_PROGS += live-g-file desktop-app-info unix-fd #unix-streams +SAMPLE_PROGS += gdbus-example-unix-fd-client endif if OS_WIN32 @@ -150,6 +172,64 @@ gsettings_LDADD = $(progs_ldadd) gschema_compile_SOURCES = gschema-compile.c gschema_compile_LDADD = $(progs_ldadd) +if HAVE_DBUS1 +TEST_PROGS += gdbus-serialization +gdbus_serialization_SOURCES = gdbus-serialization.c gdbus-tests.h gdbus-tests.c +gdbus_serialization_CFLAGS = $(DBUS1_CFLAGS) +gdbus_serialization_LDADD = $(progs_ldadd) $(DBUS1_LIBS) +endif + +gdbus_addresses_SOURCES = gdbus-addresses.c +gdbus_addresses_LDADD = $(progs_ldadd) + +gdbus_connection_SOURCES = gdbus-connection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_connection_LDADD = $(progs_ldadd) + +gdbus_names_SOURCES = gdbus-names.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_names_LDADD = $(progs_ldadd) + +gdbus_proxy_SOURCES = gdbus-proxy.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_proxy_LDADD = $(progs_ldadd) + +gdbus_introspection_SOURCES = gdbus-introspection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_introspection_LDADD = $(progs_ldadd) + +gdbus_threading_SOURCES = gdbus-threading.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_threading_LDADD = $(progs_ldadd) + +gdbus_export_SOURCES = gdbus-export.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_export_LDADD = $(progs_ldadd) + +gdbus_error_SOURCES = gdbus-error.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_error_LDADD = $(progs_ldadd) + +gdbus_peer_SOURCES = gdbus-peer.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_peer_LDADD = $(progs_ldadd) + +gdbus_exit_on_close_SOURCES = gdbus-exit-on-close.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_exit_on_close_LDADD = $(progs_ldadd) + +gdbus_example_watch_name_SOURCES = gdbus-example-watch-name.c +gdbus_example_watch_name_LDADD = $(progs_ldadd) + +gdbus_example_watch_proxy_SOURCES = gdbus-example-watch-proxy.c +gdbus_example_watch_proxy_LDADD = $(progs_ldadd) + +gdbus_example_own_name_SOURCES = gdbus-example-own-name.c +gdbus_example_own_name_LDADD = $(progs_ldadd) + +gdbus_example_server_SOURCES = gdbus-example-server.c +gdbus_example_server_LDADD = $(progs_ldadd) + +gdbus_example_unix_fd_client_SOURCES = gdbus-example-unix-fd-client.c +gdbus_example_unix_fd_client_LDADD = $(progs_ldadd) + +gdbus_example_subtree_SOURCES = gdbus-example-subtree.c +gdbus_example_subtree_LDADD = $(progs_ldadd) + +gdbus_example_peer_SOURCES = gdbus-example-peer.c +gdbus_example_peer_LDADD = $(progs_ldadd) + EXTRA_DIST += \ socket-common.c \ org.gtk.test.gschema \ diff --git a/gio/tests/gdbus-addresses.c b/gio/tests/gdbus-addresses.c new file mode 100644 index 000000000..97e5922ff --- /dev/null +++ b/gio/tests/gdbus-addresses.c @@ -0,0 +1,77 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 + +#ifdef G_OS_UNIX +#include +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_UNIX +static void +test_unix_address (void) +{ + g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar", NULL)); + g_assert (g_dbus_is_supported_address ("unix:path=/tmp/dbus-test", NULL)); + g_assert (g_dbus_is_supported_address ("unix:abstract=/tmp/dbus-another-test", NULL)); + g_assert (g_dbus_is_address ("unix:foo=bar")); + g_assert (!g_dbus_is_supported_address ("unix:foo=bar", NULL)); + g_assert (!g_dbus_is_address ("unix:path=/foo;abstract=/bar")); + g_assert (!g_dbus_is_supported_address ("unix:path=/foo;abstract=/bar", NULL)); + g_assert (g_dbus_is_supported_address ("unix:path=/tmp/concrete;unix:abstract=/tmp/abstract", NULL)); + g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar")); + + g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid")); + g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid", NULL)); +} +#endif + +static void +test_nonce_tcp_address (void) +{ + g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar", NULL)); + g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv6", NULL)); + g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv4", NULL)); + + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=blah", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=420000,noncefile=/foo/bar,family=ipv4", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=x42,noncefile=/foo/bar,family=ipv4", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=42x,noncefile=/foo/bar,family=ipv4", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=420000,noncefile=/foo/bar,family=ipv4", NULL)); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + +#ifdef G_OS_UNIX + g_test_add_func ("/gdbus/unix-address", test_unix_address); +#endif + g_test_add_func ("/gdbus/nonce-tcp-address", test_nonce_tcp_address); + return g_test_run(); +} + diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c new file mode 100644 index 000000000..1174d6f29 --- /dev/null +++ b/gio/tests/gdbus-connection.c @@ -0,0 +1,653 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Connection life-cycle testing */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_connection_life_cycle (void) +{ + GDBusConnection *c; + GDBusConnection *c2; + GError *error; + + error = NULL; + + /* + * Check for correct behavior when no bus is present + * + */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + _g_assert_error_domain (error, G_IO_ERROR); + g_assert (!g_dbus_error_is_remote_error (error)); + g_assert (c == NULL); + g_error_free (error); + error = NULL; + + /* + * Check for correct behavior when a bus is present + */ + session_bus_up (); + /* case 1 */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + + /* + * Check that singleton handling work + */ + c2 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (c == c2); + g_object_unref (c2); + + /* + * Check that private connections work + */ + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (c != c2); + g_object_unref (c2); + + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_is_closed (c2)); + g_dbus_connection_close (c2); + _g_assert_signal_received (c2, "closed"); + g_assert (g_dbus_connection_is_closed (c2)); + g_object_unref (c2); + + /* + * Check for correct behavior when the bus goes away + * + */ + g_assert (!g_dbus_connection_is_closed (c)); + g_dbus_connection_set_exit_on_close (c, FALSE); + session_bus_down (); + if (!g_dbus_connection_is_closed (c)) + _g_assert_signal_received (c, "closed"); + g_assert (g_dbus_connection_is_closed (c)); + + _g_object_wait_for_single_ref (c); + g_object_unref (c); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that sending and receiving messages work as expected */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +msg_cb_expect_error_disconnected (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_error_unknown_method (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_success (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_error_cancelled (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_error_cancelled_2 (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_connection_send (void) +{ + GDBusConnection *c; + GCancellable *ca; + + session_bus_up (); + + /* First, get an unopened connection */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + + /* + * Check that we never actually send a message if the GCancellable + * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED + * when the actual connection is not up. + */ + ca = g_cancellable_new (); + g_cancellable_cancel (ca); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + NULL); + g_main_loop_run (loop); + g_object_unref (ca); + + /* + * Check that we get a reply to the GetId() method call. + */ + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_success, + NULL); + g_main_loop_run (loop); + + /* + * Check that we get an error reply to the NonExistantMethod() method call. + */ + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NonExistantMethod", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_error_unknown_method, + NULL); + g_main_loop_run (loop); + + /* + * Check that cancellation works when the message is already in flight. + */ + ca = g_cancellable_new (); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2, + NULL); + g_cancellable_cancel (ca); + g_main_loop_run (loop); + g_object_unref (ca); + + /* + * Check that we get an error when sending to a connection that is disconnected. + */ + g_dbus_connection_set_exit_on_close (c, FALSE); + session_bus_down (); + _g_assert_signal_received (c, "closed"); + g_assert (g_dbus_connection_is_closed (c)); + + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_error_disconnected, + NULL); + g_main_loop_run (loop); + + _g_object_wait_for_single_ref (c); + g_object_unref (c); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Connection signal tests */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_connection_signal_handler (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + gint *counter = user_data; + *counter += 1; + + /*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)", + sender_name, + object_path, + interface_name, + signal_name);*/ + + g_main_loop_quit (loop); +} + +static gboolean +test_connection_signal_quit_mainloop (gpointer user_data) +{ + gboolean *quit_mainloop_fired = user_data; + *quit_mainloop_fired = TRUE; + g_main_loop_quit (loop); + return TRUE; +} + +static void +test_connection_signals (void) +{ + GDBusConnection *c1; + GDBusConnection *c2; + GDBusConnection *c3; + guint s1; + guint s2; + guint s3; + gint count_s1; + gint count_s2; + gint count_name_owner_changed; + GError *error; + gboolean ret; + GVariant *result; + + error = NULL; + + /* + * Bring up first separate connections + */ + session_bus_up (); + /* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor + * emulate this + */ + if (g_getenv ("G_DBUS_MONITOR") == NULL) + { + c1 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c1 != NULL); + g_assert (!g_dbus_connection_is_closed (c1)); + g_object_unref (c1); + } + c1 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c1 != NULL); + g_assert (!g_dbus_connection_is_closed (c1)); + g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1"); + + /* + * Install two signal handlers for the first connection + * + * - Listen to the signal "Foo" from :1.2 (e.g. c2) + * - Listen to the signal "Foo" from anyone (e.g. both c2 and c3) + * + * and then count how many times this signal handler was invoked. + */ + s1 = g_dbus_connection_signal_subscribe (c1, + ":1.2", + "org.gtk.GDBus.ExampleInterface", + "Foo", + "/org/gtk/GDBus/ExampleInterface", + NULL, + test_connection_signal_handler, + &count_s1, + NULL); + s2 = g_dbus_connection_signal_subscribe (c1, + NULL, /* match any sender */ + "org.gtk.GDBus.ExampleInterface", + "Foo", + "/org/gtk/GDBus/ExampleInterface", + NULL, + test_connection_signal_handler, + &count_s2, + NULL); + s3 = g_dbus_connection_signal_subscribe (c1, + "org.freedesktop.DBus", /* sender */ + "org.freedesktop.DBus", /* interface */ + "NameOwnerChanged", /* member */ + "/org/freedesktop/DBus", /* path */ + NULL, + test_connection_signal_handler, + &count_name_owner_changed, + NULL); + g_assert (s1 != 0); + g_assert (s2 != 0); + g_assert (s3 != 0); + + count_s1 = 0; + count_s2 = 0; + count_name_owner_changed = 0; + + /* + * Bring up two other connections + */ + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_is_closed (c2)); + g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2"); + c3 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c3 != NULL); + g_assert (!g_dbus_connection_is_closed (c3)); + g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3"); + + /* + * Make c2 emit "Foo" - we should catch it twice + * + * Note that there is no way to be sure that the signal subscriptions + * on c1 are effective yet - for all we know, the AddMatch() messages + * could sit waiting in a buffer somewhere between this process and + * the message bus. And emitting signals on c2 (a completely other + * socket!) will not necessarily change this. + * + * To ensure this is not the case, do a synchronous call on c1. + */ + result = g_dbus_connection_invoke_method_sync (c1, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, /* parameters */ + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + /* now, emit the signal on c2 */ + ret = g_dbus_connection_emit_signal (c2, + NULL, /* destination bus name */ + "/org/gtk/GDBus/ExampleInterface", + "org.gtk.GDBus.ExampleInterface", + "Foo", + NULL, + &error); + g_assert_no_error (error); + g_assert (ret); + while (!(count_s1 == 1 && count_s2 == 1)) + g_main_loop_run (loop); + g_assert_cmpint (count_s1, ==, 1); + g_assert_cmpint (count_s2, ==, 1); + + /* + * Make c3 emit "Foo" - we should catch it only once + */ + ret = g_dbus_connection_emit_signal (c3, + NULL, /* destination bus name */ + "/org/gtk/GDBus/ExampleInterface", + "org.gtk.GDBus.ExampleInterface", + "Foo", + NULL, + &error); + g_assert_no_error (error); + g_assert (ret); + while (!(count_s1 == 1 && count_s2 == 2)) + g_main_loop_run (loop); + g_assert_cmpint (count_s1, ==, 1); + g_assert_cmpint (count_s2, ==, 2); + + /* + * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling + * to avoid spinning forever + */ + gboolean quit_mainloop_fired; + guint quit_mainloop_id; + quit_mainloop_fired = FALSE; + quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired); + while (count_name_owner_changed != 2 && !quit_mainloop_fired) + g_main_loop_run (loop); + g_source_remove (quit_mainloop_id); + g_assert_cmpint (count_s1, ==, 1); + g_assert_cmpint (count_s2, ==, 2); + g_assert_cmpint (count_name_owner_changed, ==, 2); + + g_dbus_connection_signal_unsubscribe (c1, s1); + g_dbus_connection_signal_unsubscribe (c1, s2); + g_dbus_connection_signal_unsubscribe (c1, s3); + + _g_object_wait_for_single_ref (c1); + _g_object_wait_for_single_ref (c2); + _g_object_wait_for_single_ref (c3); + + g_object_unref (c1); + g_object_unref (c2); + g_object_unref (c3); + + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + guint num_handled; + guint32 serial; +} FilterData; + +static gboolean +filter_func (GDBusConnection *connection, + GDBusMessage *message, + gpointer user_data) +{ + FilterData *data = user_data; + guint32 reply_serial; + + reply_serial = g_dbus_message_get_reply_serial (message); + if (reply_serial == data->serial) + data->num_handled += 1; + + return FALSE; +} + +static void +test_connection_filter (void) +{ + GDBusConnection *c; + FilterData data; + GDBusMessage *m; + GDBusMessage *r; + GError *error; + guint filter_id; + + memset (&data, '\0', sizeof (FilterData)); + + session_bus_up (); + + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + filter_id = g_dbus_connection_add_filter (c, + filter_func, + &data, + NULL); + + m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "GetNameOwner"); + g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus")); + error = NULL; + g_dbus_connection_send_message (c, m, &data.serial, &error); + g_assert_no_error (error); + + while (data.num_handled == 0) + g_thread_yield (); + + g_dbus_connection_send_message (c, m, &data.serial, &error); + g_assert_no_error (error); + + while (data.num_handled == 1) + g_thread_yield (); + + r = g_dbus_connection_send_message_with_reply_sync (c, + m, + -1, + &data.serial, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (r != NULL); + g_object_unref (r); + g_assert_cmpint (data.num_handled, ==, 3); + + g_dbus_connection_remove_filter (c, filter_id); + + r = g_dbus_connection_send_message_with_reply_sync (c, + m, + -1, + &data.serial, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (r != NULL); + g_object_unref (r); + g_assert_cmpint (data.num_handled, ==, 3); + + _g_object_wait_for_single_ref (c); + g_object_unref (c); + g_object_unref (m); + + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/connection-life-cycle", test_connection_life_cycle); + g_test_add_func ("/gdbus/connection-send", test_connection_send); + g_test_add_func ("/gdbus/connection-signals", test_connection_signals); + g_test_add_func ("/gdbus/connection-filter", test_connection_filter); + return g_test_run(); +} diff --git a/gio/tests/gdbus-error.c b/gio/tests/gdbus-error.c new file mode 100644 index 000000000..342771002 --- /dev/null +++ b/gio/tests/gdbus-error.c @@ -0,0 +1,198 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that registered errors are properly mapped */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +check_registered_error (const gchar *given_dbus_error_name, + GQuark error_domain, + gint error_code) +{ + GError *error; + gchar *dbus_error_name; + + error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); + g_assert_error (error, error_domain, error_code); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "test message"); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); + g_free (dbus_error_name); + g_error_free (error); +} + +static void +test_registered_errors (void) +{ + /* Here we check that we are able to map to GError and back for registered + * errors. + * + * For example, if "org.freedesktop.DBus.Error.AddressInUse" is + * associated with (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED), check + * that + * + * - Creating a GError for e.g. "org.freedesktop.DBus.Error.AddressInUse" + * has (error_domain, code) == (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED) + * + * - That it is possible to recover e.g. "org.freedesktop.DBus.Error.AddressInUse" + * as the D-Bus error name when dealing with an error with (error_domain, code) == + * (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED) + * + * We just check a couple of well-known errors. + */ + check_registered_error ("org.freedesktop.DBus.Error.Failed", + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED); + check_registered_error ("org.freedesktop.DBus.Error.AddressInUse", + G_DBUS_ERROR, + G_DBUS_ERROR_ADDRESS_IN_USE); + check_registered_error ("org.freedesktop.DBus.Error.UnknownMethod", + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +check_unregistered_error (const gchar *given_dbus_error_name) +{ + GError *error; + gchar *dbus_error_name; + + error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert (g_dbus_error_is_remote_error (error)); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); + g_free (dbus_error_name); + + /* strip the message */ + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "test message"); + + /* check that we can no longer recover the D-Bus error name */ + g_assert (g_dbus_error_get_remote_error (error) == NULL); + + g_error_free (error); + +} + +static void +test_unregistered_errors (void) +{ + /* Here we check that we are able to map to GError and back for unregistered + * errors. + * + * For example, if "com.example.Error.Failed" is not registered, then check + * + * - Creating a GError for e.g. "com.example.Error.Failed" has (error_domain, code) == + * (G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) + * + * - That it is possible to recover e.g. "com.example.Error.Failed" from that + * GError. + * + * We just check a couple of random errors. + */ + + check_unregistered_error ("com.example.Error.Failed"); + check_unregistered_error ("foobar.buh"); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +check_transparent_gerror (GQuark error_domain, + gint error_code) +{ + GError *error; + gchar *given_dbus_error_name; + gchar *dbus_error_name; + + error = g_error_new (error_domain, error_code, "test message"); + given_dbus_error_name = g_dbus_error_encode_gerror (error); + g_assert (g_str_has_prefix (given_dbus_error_name, "org.gtk.GDBus.UnmappedGError.Quark")); + g_error_free (error); + + error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); + g_assert_error (error, error_domain, error_code); + g_assert (g_dbus_error_is_remote_error (error)); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); + g_free (dbus_error_name); + g_free (given_dbus_error_name); + + /* strip the message */ + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "test message"); + + /* check that we can no longer recover the D-Bus error name */ + g_assert (g_dbus_error_get_remote_error (error) == NULL); + + g_error_free (error); +} + +static void +test_transparent_gerror (void) +{ + /* Here we check that we are able to transparent pass unregistered GError's + * over the wire. + * + * For example, if G_IO_ERROR_FAILED is not registered, then check + * + * - g_dbus_error_encode_gerror() returns something of the form + * org.gtk.GDBus.UnmappedGError.Quark_HEXENCODED_QUARK_NAME_.Code_ERROR_CODE + * + * - mapping back the D-Bus error name gives us G_IO_ERROR_FAILED + * + * - That it is possible to recover the D-Bus error name from the + * GError. + * + * We just check a couple of random errors. + */ + + check_transparent_gerror (G_IO_ERROR, G_IO_ERROR_FAILED); + check_transparent_gerror (G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gdbus/registered-errors", test_registered_errors); + g_test_add_func ("/gdbus/unregistered-errors", test_unregistered_errors); + g_test_add_func ("/gdbus/transparent-gerror", test_transparent_gerror); + + return g_test_run(); +} diff --git a/gio/tests/gdbus-example-own-name.c b/gio/tests/gdbus-example-own-name.c new file mode 100644 index 000000000..733c29ea4 --- /dev/null +++ b/gio/tests/gdbus-example-own-name.c @@ -0,0 +1,99 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +#include + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + /* This is where we'd export some objects on the bus */ +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("Acquired the name %s on the session bus\n", name); +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("Lost the name %s on the session bus\n", name); +} + +int +main (int argc, char *argv[]) +{ + guint owner_id; + GMainLoop *loop; + GBusNameOwnerFlags flags; + gboolean opt_replace; + gboolean opt_allow_replacement; + gchar *opt_name; + GOptionContext *opt_context; + GError *error; + GOptionEntry opt_entries[] = + { + { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, "Replace existing name if possible", NULL }, + { "allow-replacement", 'a', 0, G_OPTION_ARG_NONE, &opt_allow_replacement, "Allow replacement", NULL }, + { "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name to acquire", NULL }, + { NULL} + }; + + g_type_init (); + + error = NULL; + opt_name = NULL; + opt_replace = FALSE; + opt_allow_replacement = FALSE; + opt_context = g_option_context_new ("g_bus_own_name() example"); + g_option_context_add_main_entries (opt_context, opt_entries, NULL); + if (!g_option_context_parse (opt_context, &argc, &argv, &error)) + { + g_printerr ("Error parsing options: %s", error->message); + return 1; + } + if (opt_name == NULL) + { + g_printerr ("Incorrect usage, try --help.\n"); + return 1; + } + + flags = G_BUS_NAME_OWNER_FLAGS_NONE; + if (opt_replace) + flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE; + if (opt_allow_replacement) + flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT; + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + opt_name, + flags, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unown_name (owner_id); + + return 0; +} diff --git a/gio/tests/gdbus-example-peer.c b/gio/tests/gdbus-example-peer.c new file mode 100644 index 000000000..a1041d9c7 --- /dev/null +++ b/gio/tests/gdbus-example-peer.c @@ -0,0 +1,318 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +/* + +Usage examples (modulo addresses / credentials). + +UNIX domain socket transport: + + Server: + $ ./gdbus-example-peer --server --address unix:abstract=myaddr + Server is listening at: unix:abstract=myaddr + Client connected. + Peer credentials: GCredentials:unix-user=500,unix-group=500,unix-process=13378 + Negotiated capabilities: unix-fd-passing=1 + Client said: Hey, it's 1273093080 already! + + Client: + $ ./gdbus-example-peer --address unix:abstract=myaddr + Connected. + Negotiated capabilities: unix-fd-passing=1 + Server said: You said 'Hey, it's 1273093080 already!'. KTHXBYE! + +Nonce-secured TCP transport on the same host: + + Server: + $ ./gdbus-example-peer --server --address nonce-tcp: + Server is listening at: nonce-tcp:host=localhost,port=43077,noncefile=/tmp/gdbus-nonce-file-X1ZNCV + Client connected. + Peer credentials: (no credentials received) + Negotiated capabilities: unix-fd-passing=0 + Client said: Hey, it's 1273093206 already! + + Client: + $ ./gdbus-example-peer -address nonce-tcp:host=localhost,port=43077,noncefile=/tmp/gdbus-nonce-file-X1ZNCV + Connected. + Negotiated capabilities: unix-fd-passing=0 + Server said: You said 'Hey, it's 1273093206 already!'. KTHXBYE! + +TCP transport on two different hosts with a shared home directory: + + Server: + host1 $ ./gdbus-example-peer --server --address tcp:host=0.0.0.0 + Server is listening at: tcp:host=0.0.0.0,port=46314 + Client connected. + Peer credentials: (no credentials received) + Negotiated capabilities: unix-fd-passing=0 + Client said: Hey, it's 1273093337 already! + + Client: + host2 $ ./gdbus-example-peer -a tcp:host=host1,port=46314 + Connected. + Negotiated capabilities: unix-fd-passing=0 + Server said: You said 'Hey, it's 1273093337 already!'. KTHXBYE! + +TCP transport on two different hosts without authentication: + + Server: + host1 $ ./gdbus-example-peer --server --address tcp:host=0.0.0.0 --allow-anonymous + Server is listening at: tcp:host=0.0.0.0,port=59556 + Client connected. + Peer credentials: (no credentials received) + Negotiated capabilities: unix-fd-passing=0 + Client said: Hey, it's 1273093652 already! + + Client: + host2 $ ./gdbus-example-peer -a tcp:host=host1,port=59556 + Connected. + Negotiated capabilities: unix-fd-passing=0 + Server said: You said 'Hey, it's 1273093652 already!'. KTHXBYE! + + */ + +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusNodeInfo *introspection_data = NULL; + +/* Introspection data for the service we are exporting */ +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + ""; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +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) +{ + if (g_strcmp0 (method_name, "HelloWorld") == 0) + { + const gchar *greeting; + gchar *response; + + g_variant_get (parameters, "(s)", &greeting); + response = g_strdup_printf ("You said '%s'. KTHXBYE!", greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); + g_print ("Client said: %s\n", greeting); + } +} + +static const GDBusInterfaceVTable interface_vtable = +{ + handle_method_call, + NULL, + NULL, +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_new_connection (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + guint registration_id; + GCredentials *credentials; + gchar *s; + + credentials = g_dbus_connection_get_peer_credentials (connection); + if (credentials == NULL) + s = g_strdup ("(no credentials received)"); + else + s = g_credentials_to_string (credentials); + + + g_print ("Client connected.\n" + "Peer credentials: %s\n" + "Negotiated capabilities: unix-fd-passing=%d\n", + s, + g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); + + g_object_ref (connection); + registration_id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/TestObject", + "org.gtk.GDBus.TestPeerInterface", + introspection_data->interfaces[0], + &interface_vtable, + NULL, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + g_assert (registration_id > 0); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, char *argv[]) +{ + gint ret; + gboolean opt_server; + gchar *opt_address; + GOptionContext *opt_context; + gboolean opt_allow_anonymous; + GError *error; + GOptionEntry opt_entries[] = + { + { "server", 's', 0, G_OPTION_ARG_NONE, &opt_server, "Start a server instead of a client", NULL }, + { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_address, "D-Bus address to use", NULL }, + { "allow-anonymous", 'n', 0, G_OPTION_ARG_NONE, &opt_allow_anonymous, "Allow anonymous authentication", NULL }, + { NULL} + }; + + ret = 1; + + g_type_init (); + + opt_address = NULL; + opt_server = FALSE; + opt_allow_anonymous = FALSE; + + opt_context = g_option_context_new ("peer-to-peer example"); + error = NULL; + g_option_context_add_main_entries (opt_context, opt_entries, NULL); + if (!g_option_context_parse (opt_context, &argc, &argv, &error)) + { + g_printerr ("Error parsing options: %s\n", error->message); + g_error_free (error); + goto out; + } + if (opt_address == NULL) + { + g_printerr ("Incorrect usage, try --help.\n"); + goto out; + } + if (!opt_server && opt_allow_anonymous) + { + g_printerr ("The --allow-anonymous option only makes sense when used with --server.\n"); + goto out; + } + + /* We are lazy here - we don't want to manually provide + * the introspection data structures - so we just build + * them from XML. + */ + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + if (opt_server) + { + GDBusServer *server; + gchar *guid; + GMainLoop *loop; + GDBusServerFlags server_flags; + + guid = g_dbus_generate_guid (); + + server_flags = G_DBUS_SERVER_FLAGS_NONE; + if (opt_allow_anonymous) + server_flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; + + error = NULL; + server = g_dbus_server_new_sync (opt_address, + server_flags, + guid, + NULL, /* GDBusAuthObserver */ + NULL, /* GCancellable */ + &error); + g_dbus_server_start (server); + g_free (guid); + + if (server == NULL) + { + g_printerr ("Error creating server at address %s: %s\n", opt_address, error->message); + g_error_free (error); + goto out; + } + g_print ("Server is listening at: %s\n", g_dbus_server_get_client_address (server)); + g_signal_connect (server, + "new-connection", + G_CALLBACK (on_new_connection), + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_object_unref (server); + g_main_loop_unref (loop); + } + else + { + GDBusConnection *connection; + const gchar *greeting_response; + GVariant *value; + gchar *greeting; + + error = NULL; + connection = g_dbus_connection_new_for_address_sync (opt_address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GCancellable */ + &error); + if (connection == NULL) + { + g_printerr ("Error connecting to D-Bus address %s: %s\n", opt_address, error->message); + g_error_free (error); + goto out; + } + + g_print ("Connected.\n" + "Negotiated capabilities: unix-fd-passing=%d\n", + g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); + + greeting = g_strdup_printf ("Hey, it's %" G_GUINT64_FORMAT " already!", (guint64) time (NULL)); + value = g_dbus_connection_invoke_method_sync (connection, + NULL, /* bus_name */ + "/org/gtk/GDBus/TestObject", + "org.gtk.GDBus.TestPeerInterface", + "HelloWorld", + g_variant_new ("(s)", greeting), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + if (value == NULL) + { + g_printerr ("Error invoking HelloWorld(): %s\n", error->message); + g_error_free (error); + goto out; + } + g_variant_get (value, "(s)", &greeting_response); + g_print ("Server said: %s\n", greeting_response); + g_variant_unref (value); + + g_object_unref (connection); + } + g_dbus_node_info_unref (introspection_data); + + ret = 0; + + out: + return ret; +} diff --git a/gio/tests/gdbus-example-server.c b/gio/tests/gdbus-example-server.c new file mode 100644 index 000000000..fe667c040 --- /dev/null +++ b/gio/tests/gdbus-example-server.c @@ -0,0 +1,388 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +#include +#include + +#ifdef G_OS_UNIX +/* For STDOUT_FILENO */ +#include +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusNodeInfo *introspection_data = NULL; + +/* Introspection data for the service we are exporting */ +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +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) +{ + if (g_strcmp0 (method_name, "HelloWorld") == 0) + { + const gchar *greeting; + + g_variant_get (parameters, "(s)", &greeting); + + if (g_strcmp0 (greeting, "Return Unregistered") == 0) + { + g_dbus_method_invocation_return_error (invocation, + G_IO_ERROR, + G_IO_ERROR_FAILED_HANDLED, + "As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)"); + } + else if (g_strcmp0 (greeting, "Return Registered") == 0) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, + "As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND)"); + } + else if (g_strcmp0 (greeting, "Return Raw") == 0) + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.SomeErrorName", + "As requested, here's a raw D-Bus error"); + } + else + { + gchar *response; + response = g_strdup_printf ("You greeted me with '%s'. Thanks!", greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); + } + } + else if (g_strcmp0 (method_name, "EmitSignal") == 0) + { + GError *local_error; + gdouble speed_in_mph; + gchar *speed_as_string; + + g_variant_get (parameters, "(d)", &speed_in_mph); + speed_as_string = g_strdup_printf ("%g mph!", speed_in_mph); + + local_error = NULL; + g_dbus_connection_emit_signal (connection, + NULL, + object_path, + interface_name, + "VelocityChanged", + g_variant_new ("(ds)", + speed_in_mph, + speed_as_string), + &local_error); + g_assert_no_error (local_error); + g_free (speed_as_string); + + g_dbus_method_invocation_return_value (invocation, NULL); + } + else if (g_strcmp0 (method_name, "GimmeStdout") == 0) + { +#ifdef G_OS_UNIX + if (g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) + { + GDBusMessage *reply; + GUnixFDList *fd_list; + GError *error; + + fd_list = g_unix_fd_list_new (); + error = NULL; + g_unix_fd_list_append (fd_list, STDOUT_FILENO, &error); + g_assert_no_error (error); + + reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); + g_dbus_message_set_unix_fd_list (reply, fd_list); + + error = NULL; + g_dbus_connection_send_message (connection, + reply, + NULL, /* out_serial */ + &error); + g_assert_no_error (error); + + g_object_unref (invocation); + g_object_unref (fd_list); + g_object_unref (reply); + } + else + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.Failed", + "Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)"); + } +#else + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.NotOnUnix", + "Your OS does not support file descriptor passing"); +#endif + } +} + +static gchar *_global_title = NULL; + +static gboolean swap_a_and_b = FALSE; + +static GVariant * +handle_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GVariant *ret; + + ret = NULL; + if (g_strcmp0 (property_name, "FluxCapicitorName") == 0) + { + ret = g_variant_new_string ("DeLorean"); + } + else if (g_strcmp0 (property_name, "Title") == 0) + { + if (_global_title == NULL) + _global_title = g_strdup ("Back To C!"); + ret = g_variant_new_string (_global_title); + } + else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Hello %s. I thought I said reading this property " + "always results in an error. kthxbye", + sender); + } + else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0) + { + ret = g_variant_new_string ("There's no home like home"); + } + else if (g_strcmp0 (property_name, "Foo") == 0) + { + ret = g_variant_new_string (swap_a_and_b ? "Tock" : "Tick"); + } + else if (g_strcmp0 (property_name, "Bar") == 0) + { + ret = g_variant_new_string (swap_a_and_b ? "Tick" : "Tock"); + } + + return ret; +} + +static gboolean +handle_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + if (g_strcmp0 (property_name, "Title") == 0) + { + if (g_strcmp0 (_global_title, g_variant_get_string (value, NULL)) != 0) + { + GVariantBuilder *builder; + GError *local_error; + + g_free (_global_title); + _global_title = g_variant_dup_string (value, NULL); + + local_error = NULL; + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + g_variant_builder_add (builder, + "{sv}", + "Title", + g_variant_new_string (_global_title)); + g_dbus_connection_emit_signal (connection, + NULL, + object_path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new ("(sa{sv})", + interface_name, + builder), + &local_error); + g_assert_no_error (local_error); + } + } + else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0) + { + /* do nothing - they can't read it after all! */ + } + else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Hello AGAIN %s. I thought I said writing this property " + "always results in an error. kthxbye", + sender); + } + + return *error == NULL; +} + + +/* for now */ +static const GDBusInterfaceVTable interface_vtable = +{ + handle_method_call, + handle_get_property, + handle_set_property +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +on_timeout_cb (gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GVariantBuilder *builder; + GError *error; + + swap_a_and_b = !swap_a_and_b; + + error = NULL; + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + g_variant_builder_add (builder, + "{sv}", + "Foo", + g_variant_new_string (swap_a_and_b ? "Tock" : "Tick")); + g_variant_builder_add (builder, + "{sv}", + "Bar", + g_variant_new_string (swap_a_and_b ? "Tick" : "Tock")); + g_dbus_connection_emit_signal (connection, + NULL, + "/org/gtk/GDBus/TestObject", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new ("(sa{sv})", + "org.gtk.GDBus.TestInterface", + builder), + &error); + g_assert_no_error (error); + + + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + guint registration_id; + + registration_id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/TestObject", + "org.gtk.GDBus.TestInterface", + introspection_data->interfaces[0], + &interface_vtable, + NULL, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + g_assert (registration_id > 0); + + /* swap value of properties Foo and Bar every two seconds */ + g_timeout_add_seconds (2, + on_timeout_cb, + connection); +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + exit (1); +} + +int +main (int argc, char *argv[]) +{ + guint owner_id; + GMainLoop *loop; + + g_type_init (); + + /* We are lazy here - we don't want to manually provide + * the introspection data structures - so we just build + * them from XML. + */ + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.TestServer", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unown_name (owner_id); + + g_dbus_node_info_unref (introspection_data); + + return 0; +} diff --git a/gio/tests/gdbus-example-subtree.c b/gio/tests/gdbus-example-subtree.c new file mode 100644 index 000000000..e8da6da88 --- /dev/null +++ b/gio/tests/gdbus-example-subtree.c @@ -0,0 +1,410 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusNodeInfo *introspection_data = NULL; +static const GDBusInterfaceInfo *manager_interface_info = NULL; +static const GDBusInterfaceInfo *block_interface_info = NULL; +static const GDBusInterfaceInfo *partition_interface_info = NULL; + +/* Introspection data for the service we are exporting */ +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +/* ---------------------------------------------------------------------------------------------------- */ + +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) +{ + const gchar *greeting; + gchar *response; + + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.Example.Manager"); + g_assert_cmpstr (method_name, ==, "Hello"); + + g_variant_get (parameters, "(s)", &greeting); + + response = g_strdup_printf ("Method %s.%s with user_data `%s' on object path %s called with arg '%s'", + interface_name, + method_name, + (const gchar *) user_data, + object_path, + greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); +} + +const GDBusInterfaceVTable manager_vtable = +{ + manager_method_call, + NULL, /* get_property */ + NULL /* set_property */ +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +block_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) +{ + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.Example.Block"); + + if (g_strcmp0 (method_name, "Hello") == 0) + { + const gchar *greeting; + gchar *response; + + g_variant_get (parameters, "(s)", &greeting); + + response = g_strdup_printf ("Method %s.%s with user_data `%s' on object path %s called with arg '%s'", + interface_name, + method_name, + (const gchar *) user_data, + object_path, + greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); + } + else if (g_strcmp0 (method_name, "DoStuff") == 0) + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.TestSubtree.Error.Failed", + "This method intentionally always fails"); + } + else + { + g_assert_not_reached (); + } +} + +static GVariant * +block_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GVariant *ret; + const gchar *node; + gint major; + gint minor; + + node = strrchr (object_path, '/') + 1; + if (g_str_has_prefix (node, "sda")) + major = 8; + else + major = 9; + if (strlen (node) == 4) + minor = node[3] - '0'; + else + minor = 0; + + ret = NULL; + if (g_strcmp0 (property_name, "Major") == 0) + { + ret = g_variant_new_int32 (major); + } + else if (g_strcmp0 (property_name, "Minor") == 0) + { + ret = g_variant_new_int32 (minor); + } + else if (g_strcmp0 (property_name, "Notes") == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Hello %s. I thought I said reading this property " + "always results in an error. kthxbye", + sender); + } + else + { + g_assert_not_reached (); + } + + return ret; +} + +static gboolean +block_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + /* TODO */ + g_assert_not_reached (); +} + +const GDBusInterfaceVTable block_vtable = +{ + block_method_call, + block_get_property, + block_set_property, +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +partition_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) +{ + const gchar *greeting; + gchar *response; + + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.Example.Partition"); + g_assert_cmpstr (method_name, ==, "Hello"); + + g_variant_get (parameters, "(s)", &greeting); + + response = g_strdup_printf ("Method %s.%s with user_data `%s' on object path %s called with arg '%s'", + interface_name, + method_name, + (const gchar *) user_data, + object_path, + greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); +} + +const GDBusInterfaceVTable partition_vtable = +{ + partition_method_call, + //partition_get_property, + //partition_set_property +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar ** +subtree_enumerate (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data) +{ + gchar **nodes; + GPtrArray *p; + + p = g_ptr_array_new (); + g_ptr_array_add (p, g_strdup ("sda")); + g_ptr_array_add (p, g_strdup ("sda1")); + g_ptr_array_add (p, g_strdup ("sda2")); + g_ptr_array_add (p, g_strdup ("sda3")); + g_ptr_array_add (p, g_strdup ("sdb")); + g_ptr_array_add (p, g_strdup ("sdb1")); + g_ptr_array_add (p, g_strdup ("sdc")); + g_ptr_array_add (p, g_strdup ("sdc1")); + g_ptr_array_add (p, NULL); + nodes = (gchar **) g_ptr_array_free (p, FALSE); + + return nodes; +} + +static GPtrArray * +subtree_introspect (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data) +{ + GPtrArray *p; + + p = g_ptr_array_new (); + if (g_strcmp0 (node, "/") == 0) + { + g_ptr_array_add (p, (gpointer) manager_interface_info); + } + else + { + g_ptr_array_add (p, (gpointer) block_interface_info); + if (strlen (node) == 4) + g_ptr_array_add (p, (gpointer) partition_interface_info); + } + + return p; +} + +static const GDBusInterfaceVTable * +subtree_dispatch (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data) +{ + const GDBusInterfaceVTable *vtable_to_return; + gpointer user_data_to_return; + + if (g_strcmp0 (interface_name, "org.gtk.GDBus.Example.Manager") == 0) + { + user_data_to_return = "The Root"; + vtable_to_return = &manager_vtable; + } + else + { + if (strlen (node) == 4) + user_data_to_return = "A partition"; + else + user_data_to_return = "A block device"; + + if (g_strcmp0 (interface_name, "org.gtk.GDBus.Example.Block") == 0) + vtable_to_return = &block_vtable; + else if (g_strcmp0 (interface_name, "org.gtk.GDBus.Example.Partition") == 0) + vtable_to_return = &partition_vtable; + else + g_assert_not_reached (); + } + + *out_user_data = user_data_to_return; + + return vtable_to_return; +} + +const GDBusSubtreeVTable subtree_vtable = +{ + subtree_enumerate, + subtree_introspect, + subtree_dispatch +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + guint registration_id; + + registration_id = g_dbus_connection_register_subtree (connection, + "/org/gtk/GDBus/TestSubtree/Devices", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + NULL, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + g_assert (registration_id > 0); +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + exit (1); +} + +int +main (int argc, char *argv[]) +{ + guint owner_id; + GMainLoop *loop; + + g_type_init (); + + /* We are lazy here - we don't want to manually provide + * the introspection data structures - so we just build + * them from XML. + */ + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + manager_interface_info = g_dbus_node_info_lookup_interface (introspection_data, "org.gtk.GDBus.Example.Manager"); + block_interface_info = g_dbus_node_info_lookup_interface (introspection_data, "org.gtk.GDBus.Example.Block"); + partition_interface_info = g_dbus_node_info_lookup_interface (introspection_data, "org.gtk.GDBus.Example.Partition"); + g_assert (manager_interface_info != NULL); + g_assert (block_interface_info != NULL); + g_assert (partition_interface_info != NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.TestSubtree", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unown_name (owner_id); + + g_dbus_node_info_unref (introspection_data); + + return 0; +} diff --git a/gio/tests/gdbus-example-unix-fd-client.c b/gio/tests/gdbus-example-unix-fd-client.c new file mode 100644 index 000000000..6a66431c3 --- /dev/null +++ b/gio/tests/gdbus-example-unix-fd-client.c @@ -0,0 +1,145 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +#include +#include + +#include +#include + +#include + +#include + +/* see gdbus-example-server.c for the server implementation */ +static gint +get_server_stdout (GDBusConnection *connection, + const gchar *name_owner, + GError **error) +{ + GDBusMessage *method_call_message; + GDBusMessage *method_reply_message; + GUnixFDList *fd_list; + gint fd; + + fd = -1; + method_call_message = NULL; + method_reply_message = NULL; + + method_call_message = g_dbus_message_new_method_call (name_owner, + "/org/gtk/GDBus/TestObject", + "org.gtk.GDBus.TestInterface", + "GimmeStdout"); + method_reply_message = g_dbus_connection_send_message_with_reply_sync (connection, + method_call_message, + -1, + NULL, /* out_serial */ + NULL, /* cancellable */ + error); + if (method_reply_message == NULL) + goto out; + + if (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_ERROR) + { + g_dbus_message_to_gerror (method_reply_message, error); + goto out; + } + + fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); + fd = g_unix_fd_list_get (fd_list, 0, error); + + out: + g_object_unref (method_call_message); + g_object_unref (method_reply_message); + + return fd; +} + +static void +on_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + gint fd; + GError *error; + + error = NULL; + fd = get_server_stdout (connection, name_owner, &error); + if (fd == -1) + { + g_printerr ("Error invoking GimmeStdout(): %s\n", + error->message); + g_error_free (error); + exit (1); + } + else + { + gchar now_buf[256]; + time_t now; + gssize len; + gchar *str; + + now = time (NULL); + strftime (now_buf, + sizeof now_buf, + "%c", + localtime (&now)); + + str = g_strdup_printf ("On %s, gdbus-example-unix-fd-client with pid %d was here!\n", + now_buf, + (gint) getpid ()); + len = strlen (str); + g_warn_if_fail (write (fd, str, len) == len); + close (fd); + + g_print ("Wrote the following on server's stdout:\n%s", str); + + g_free (str); + exit (0); + } +} + +static void +on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_printerr ("Failed to get name owner for %s\n" + "Is ./gdbus-example-server running?\n", + name); + exit (1); +} + +int +main (int argc, char *argv[]) +{ + guint watcher_id; + GMainLoop *loop; + + g_type_init (); + + watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.TestServer", + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_name_appeared, + on_name_vanished, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unwatch_name (watcher_id); + return 0; +} diff --git a/gio/tests/gdbus-example-watch-name.c b/gio/tests/gdbus-example-watch-name.c new file mode 100644 index 000000000..39d0aed8d --- /dev/null +++ b/gio/tests/gdbus-example-watch-name.c @@ -0,0 +1,101 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +#include + +static gchar *opt_name = NULL; +static gboolean opt_system_bus = FALSE; +static gboolean opt_auto_start = FALSE; + +static GOptionEntry opt_entries[] = +{ + { "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name to watch", NULL }, + { "system-bus", 's', 0, G_OPTION_ARG_NONE, &opt_system_bus, "Use the system-bus instead of the session-bus", NULL }, + { "auto-start", 'a', 0, G_OPTION_ARG_NONE, &opt_auto_start, "Instruct the bus to launch an owner for the name", NULL}, + { NULL} +}; + +static void +on_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + g_print ("Name %s on %s is owned by %s\n", + name, + opt_system_bus ? "the system bus" : "the session bus", + name_owner); +} + +static void +on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("Name %s does not exist on %s\n", + name, + opt_system_bus ? "the system bus" : "the session bus"); +} + +int +main (int argc, char *argv[]) +{ + guint watcher_id; + GMainLoop *loop; + GOptionContext *opt_context; + GError *error; + GBusNameWatcherFlags flags; + + g_type_init (); + + error = NULL; + opt_context = g_option_context_new ("g_bus_watch_name() example"); + g_option_context_set_summary (opt_context, + "Example: to watch the power manager on the session bus, use:\n" + "\n" + " ./example-watch-name -n org.gnome.PowerManager"); + g_option_context_add_main_entries (opt_context, opt_entries, NULL); + if (!g_option_context_parse (opt_context, &argc, &argv, &error)) + { + g_printerr ("Error parsing options: %s", error->message); + goto out; + } + if (opt_name == NULL) + { + g_printerr ("Incorrect usage, try --help.\n"); + goto out; + } + + flags = G_BUS_NAME_WATCHER_FLAGS_NONE; + if (opt_auto_start) + flags |= G_BUS_NAME_WATCHER_FLAGS_AUTO_START; + + watcher_id = g_bus_watch_name (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION, + opt_name, + flags, + on_name_appeared, + on_name_vanished, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unwatch_name (watcher_id); + + out: + g_option_context_free (opt_context); + g_free (opt_name); + + return 0; +} diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c new file mode 100644 index 000000000..9a6176ea5 --- /dev/null +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -0,0 +1,205 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: David Zeuthen + */ + +#include + +static gchar *opt_name = NULL; +static gchar *opt_object_path = NULL; +static gchar *opt_interface = NULL; +static gboolean opt_system_bus = FALSE; +static gboolean opt_auto_start = FALSE; +static gboolean opt_no_properties = FALSE; + +static GOptionEntry opt_entries[] = +{ + { "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name of the remote object to watch", NULL }, + { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_object_path, "Object path of the remote object", NULL }, + { "interface", 'i', 0, G_OPTION_ARG_STRING, &opt_interface, "D-Bus interface of remote object", NULL }, + { "system-bus", 's', 0, G_OPTION_ARG_NONE, &opt_system_bus, "Use the system-bus instead of the session-bus", NULL }, + { "auto-start", 'a', 0, G_OPTION_ARG_NONE, &opt_auto_start, "Instruct the bus to launch an owner for the name", NULL}, + { "no-properties", 'p', 0, G_OPTION_ARG_NONE, &opt_no_properties, "Do not load properties", NULL}, + { NULL} +}; + +static void +print_properties (GDBusProxy *proxy) +{ + gchar **property_names; + guint n; + + g_print (" properties:\n"); + + property_names = g_dbus_proxy_get_cached_property_names (proxy, NULL); + for (n = 0; property_names != NULL && property_names[n] != NULL; n++) + { + const gchar *key = property_names[n]; + GVariant *value; + gchar *value_str; + value = g_dbus_proxy_get_cached_property (proxy, key, NULL); + value_str = g_variant_print (value, TRUE); + g_print (" %s -> %s\n", key, value_str); + g_variant_unref (value); + g_free (value_str); + } + g_strfreev (property_names); +} + +static void +on_properties_changed (GDBusProxy *proxy, + GHashTable *changed_properties, + gpointer user_data) +{ + GHashTableIter iter; + const gchar *key; + GVariant *value; + + g_print (" *** Properties Changed:\n"); + + g_hash_table_iter_init (&iter, changed_properties); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) + { + gchar *value_str; + value_str = g_variant_print (value, TRUE); + g_print (" %s -> %s\n", key, value_str); + g_free (value_str); + } +} + +static void +on_signal (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + gchar *parameters_str; + + parameters_str = g_variant_print (parameters, TRUE); + g_print (" *** Received Signal: %s: %s\n", + signal_name, + parameters_str); + g_free (parameters_str); +} + +static void +on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + g_print ("+++ Acquired proxy object for remote object owned by %s\n" + " bus: %s\n" + " name: %s\n" + " object path: %s\n" + " interface: %s\n", + name_owner, + opt_system_bus ? "System Bus" : "Session Bus", + opt_name, + opt_object_path, + opt_interface); + + print_properties (proxy); + + g_signal_connect (proxy, + "g-properties-changed", + G_CALLBACK (on_properties_changed), + NULL); + + g_signal_connect (proxy, + "g-signal", + G_CALLBACK (on_signal), + NULL); +} + +static void +on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("--- Cannot create proxy object for\n" + " bus: %s\n" + " name: %s\n" + " object path: %s\n" + " interface: %s\n", + opt_system_bus ? "System Bus" : "Session Bus", + opt_name, + opt_object_path, + opt_interface); +} + +int +main (int argc, char *argv[]) +{ + guint watcher_id; + GMainLoop *loop; + GOptionContext *opt_context; + GError *error; + GBusNameWatcherFlags flags; + GDBusProxyFlags proxy_flags; + + g_type_init (); + + opt_context = g_option_context_new ("g_bus_watch_proxy() example"); + g_option_context_set_summary (opt_context, + "Example: to watch the object of gdbus-example-server, use:\n" + "\n" + " ./gdbus-example-watch-proxy -n org.gtk.GDBus.TestServer \\\n" + " -o /org/gtk/GDBus/TestObject \\\n" + " -i org.gtk.GDBus.TestInterface"); + g_option_context_add_main_entries (opt_context, opt_entries, NULL); + error = NULL; + if (!g_option_context_parse (opt_context, &argc, &argv, &error)) + { + g_printerr ("Error parsing options: %s", error->message); + goto out; + } + if (opt_name == NULL || opt_object_path == NULL || opt_interface == NULL) + { + g_printerr ("Incorrect usage, try --help.\n"); + goto out; + } + + flags = G_BUS_NAME_WATCHER_FLAGS_NONE; + if (opt_auto_start) + flags |= G_BUS_NAME_WATCHER_FLAGS_AUTO_START; + + proxy_flags = G_DBUS_PROXY_FLAGS_NONE; + if (opt_no_properties) + proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; + + watcher_id = g_bus_watch_proxy (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION, + opt_name, + flags, + opt_object_path, + opt_interface, + G_TYPE_DBUS_PROXY, + proxy_flags, + on_proxy_appeared, + on_proxy_vanished, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); + + out: + g_option_context_free (opt_context); + g_free (opt_name); + g_free (opt_object_path); + g_free (opt_interface); + + return 0; +} diff --git a/gio/tests/gdbus-exit-on-close.c b/gio/tests/gdbus-exit-on-close.c new file mode 100644 index 000000000..7f75519ce --- /dev/null +++ b/gio/tests/gdbus-exit-on-close.c @@ -0,0 +1,82 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +nuke_session_bus_cb (gpointer data) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +test_exit_on_close (void) +{ + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) + { + GDBusConnection *c; + session_bus_up (); + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + g_timeout_add (50, + nuke_session_bus_cb, + NULL); + g_main_loop_run (loop); + session_bus_down (); + g_main_loop_run (loop); + } + g_test_trap_assert_stdout ("*Remote peer vanished. Exiting.*"); + g_test_trap_assert_failed(); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/exit-on-close", test_exit_on_close); + return g_test_run(); +} diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c new file mode 100644 index 000000000..6e33d2f4f --- /dev/null +++ b/gio/tests/gdbus-export.c @@ -0,0 +1,1410 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +static GDBusConnection *c = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that we can export objects, the hierarchy is correct and the right handlers are invoked */ +/* ---------------------------------------------------------------------------------------------------- */ + +static const GDBusArgInfo foo_method1_in_args = +{ + -1, + "an_input_string", + "s", + NULL +}; +static const GDBusArgInfo * const foo_method1_in_arg_pointers[] = {&foo_method1_in_args, NULL}; + +static const GDBusArgInfo foo_method1_out_args = +{ + -1, + "an_output_string", + "s", + NULL +}; +static const GDBusArgInfo * const foo_method1_out_arg_pointers[] = {&foo_method1_out_args, NULL}; + +static const GDBusMethodInfo foo_method_info_method1 = +{ + -1, + "Method1", + (GDBusArgInfo **) &foo_method1_in_arg_pointers, + (GDBusArgInfo **) &foo_method1_out_arg_pointers, + NULL +}; +static const GDBusMethodInfo foo_method_info_method2 = +{ + -1, + "Method2", + NULL, + NULL, + NULL +}; +static const GDBusMethodInfo * const foo_method_info_pointers[] = {&foo_method_info_method1, &foo_method_info_method2, NULL}; + +static const GDBusSignalInfo foo_signal_info = +{ + -1, + "SignalAlpha", + NULL, + NULL +}; +static const GDBusSignalInfo * const foo_signal_info_pointers[] = {&foo_signal_info, NULL}; + +static const GDBusPropertyInfo foo_property_info[] = +{ + { + -1, + "PropertyUno", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + }, + { + -1, + "NotWritable", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + { + -1, + "NotReadable", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + } +}; +static const GDBusPropertyInfo * const foo_property_info_pointers[] = +{ + &foo_property_info[0], + &foo_property_info[1], + &foo_property_info[2], + NULL +}; + +static const GDBusInterfaceInfo foo_interface_info = +{ + -1, + "org.example.Foo", + (GDBusMethodInfo **) &foo_method_info_pointers, + (GDBusSignalInfo **) &foo_signal_info_pointers, + (GDBusPropertyInfo **)&foo_property_info_pointers, + NULL, +}; + +static void +foo_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) +{ + if (g_strcmp0 (method_name, "Method1") == 0) + { + const gchar *input; + gchar *output; + g_variant_get (parameters, "(s)", &input); + output = g_strdup_printf ("You passed the string `%s'. Jolly good!", input); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", output)); + g_free (output); + } + else + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.example.SomeError", + "How do you like them apples, buddy!"); + } +} + +static GVariant * +foo_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GVariant *ret; + gchar *s; + s = g_strdup_printf ("Property `%s' Is What It Is!", property_name); + ret = g_variant_new_string (s); + g_free (s); + return ret; +} + +static gboolean +foo_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + gchar *s; + s = g_variant_print (value, TRUE); + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_SPAWN_FILE_INVALID, + "Returning some error instead of writing the value `%s' to the property `%s'", + property_name, s); + g_free (s); + return FALSE; +} + +static const GDBusInterfaceVTable foo_vtable = +{ + foo_method_call, + foo_get_property, + foo_set_property +}; + +/* -------------------- */ + +static const GDBusMethodInfo bar_method_info[] = +{ + { + -1, + "MethodA", + NULL, + NULL, + NULL + }, + { + -1, + "MethodB", + NULL, + NULL, + NULL + } +}; +static const GDBusMethodInfo * const bar_method_info_pointers[] = {&bar_method_info[0], &bar_method_info[1], NULL}; + +static const GDBusSignalInfo bar_signal_info[] = +{ + { + -1, + "SignalMars", + NULL, + NULL + } +}; +static const GDBusSignalInfo * const bar_signal_info_pointers[] = {&bar_signal_info[0], NULL}; + +static const GDBusPropertyInfo bar_property_info[] = +{ + { + -1, + "PropertyDuo", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + } +}; +static const GDBusPropertyInfo * const bar_property_info_pointers[] = {&bar_property_info[0], NULL}; + +static const GDBusInterfaceInfo bar_interface_info = +{ + -1, + "org.example.Bar", + (GDBusMethodInfo **) bar_method_info_pointers, + (GDBusSignalInfo **) bar_signal_info_pointers, + (GDBusPropertyInfo **) bar_property_info_pointers, + NULL, +}; + +/* -------------------- */ + +static const GDBusMethodInfo dyna_method_info[] = +{ + { + -1, + "DynaCyber", + NULL, + NULL, + NULL + } +}; +static const GDBusMethodInfo * const dyna_method_info_pointers[] = {&dyna_method_info[0], NULL}; + +static const GDBusInterfaceInfo dyna_interface_info = +{ + -1, + "org.example.Dyna", + (GDBusMethodInfo **) dyna_method_info_pointers, + NULL, /* no signals */ + NULL, /* no properties */ + NULL, +}; + +static void +dyna_cyber (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GPtrArray *data = user_data; + gchar *node_name; + guint n; + + node_name = strrchr (object_path, '/') + 1; + + /* Add new node if it is not already known */ + for (n = 0; n < data->len ; n++) + { + if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0) + goto out; + } + g_ptr_array_add (data, g_strdup(node_name)); + + out: + g_dbus_method_invocation_return_value (invocation, NULL); +} + +static const GDBusInterfaceVTable dyna_interface_vtable = +{ + dyna_cyber, + NULL, + NULL +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +introspect_callback (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + const gchar *s; + gchar **xml_data = user_data; + GVariant *result; + GError *error; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(s)", &s); + *xml_data = g_strdup (s); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +static gchar ** +get_nodes_at (GDBusConnection *c, + const gchar *object_path) +{ + GError *error; + GDBusProxy *proxy; + gchar *xml_data; + GPtrArray *p; + GDBusNodeInfo *node_info; + guint n; + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.freedesktop.DBus.Introspectable", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + xml_data = NULL; + g_dbus_proxy_invoke_method (proxy, + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); + g_main_loop_run (loop); + g_assert (xml_data != NULL); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + p = g_ptr_array_new (); + for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++) + { + const GDBusNodeInfo *sub_node_info = node_info->nodes[n]; + g_ptr_array_add (p, g_strdup (sub_node_info->path)); + } + g_ptr_array_add (p, NULL); + + g_object_unref (proxy); + g_free (xml_data); + g_dbus_node_info_unref (node_info); + + return (gchar **) g_ptr_array_free (p, FALSE); +} + +static gboolean +has_interface (GDBusConnection *c, + const gchar *object_path, + const gchar *interface_name) +{ + GError *error; + GDBusProxy *proxy; + gchar *xml_data; + GDBusNodeInfo *node_info; + gboolean ret; + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.freedesktop.DBus.Introspectable", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + xml_data = NULL; + g_dbus_proxy_invoke_method (proxy, + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); + g_main_loop_run (loop); + g_assert (xml_data != NULL); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + ret = (g_dbus_node_info_lookup_interface (node_info, interface_name) != NULL); + + g_object_unref (proxy); + g_free (xml_data); + g_dbus_node_info_unref (node_info); + + return ret; +} + +static guint +count_interfaces (GDBusConnection *c, + const gchar *object_path) +{ + GError *error; + GDBusProxy *proxy; + gchar *xml_data; + GDBusNodeInfo *node_info; + guint ret; + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.freedesktop.DBus.Introspectable", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + xml_data = NULL; + g_dbus_proxy_invoke_method (proxy, + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); + g_main_loop_run (loop); + g_assert (xml_data != NULL); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + ret = 0; + while (node_info->interfaces != NULL && node_info->interfaces[ret] != NULL) + ret++; + + g_object_unref (proxy); + g_free (xml_data); + g_dbus_node_info_unref (node_info); + + return ret; +} + +static void +dyna_create_callback (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + GVariant *result; + GError *error; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +/* Dynamically create @object_name under /foo/dyna */ +static void +dyna_create (GDBusConnection *c, + const gchar *object_name) +{ + GError *error; + GDBusProxy *proxy; + gchar *object_path; + + object_path = g_strconcat ("/foo/dyna/", object_name, NULL); + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.example.Dyna", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + g_dbus_proxy_invoke_method (proxy, + "DynaCyber", + g_variant_new ("()"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) dyna_create_callback, + NULL); + g_main_loop_run (loop); + + g_assert_no_error (error); + + g_object_unref (proxy); + g_free (object_path); + + return; +} + +typedef struct +{ + guint num_unregistered_calls; + guint num_unregistered_subtree_calls; + guint num_subtree_nodes; +} ObjectRegistrationData; + +static void +on_object_unregistered (gpointer user_data) +{ + ObjectRegistrationData *data = user_data; + + data->num_unregistered_calls++; +} + +static void +on_subtree_unregistered (gpointer user_data) +{ + ObjectRegistrationData *data = user_data; + + data->num_unregistered_subtree_calls++; +} + +static gboolean +_g_strv_has_string (const gchar* const * haystack, + const gchar *needle) +{ + guint n; + + for (n = 0; haystack != NULL && haystack[n] != NULL; n++) + { + if (g_strcmp0 (haystack[n], needle) == 0) + return TRUE; + } + return FALSE; +} + +/* -------------------- */ + +static gchar ** +subtree_enumerate (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data) +{ + ObjectRegistrationData *data = user_data; + GPtrArray *p; + gchar **nodes; + guint n; + + p = g_ptr_array_new (); + + for (n = 0; n < data->num_subtree_nodes; n++) + { + g_ptr_array_add (p, g_strdup_printf ("vp%d", n)); + g_ptr_array_add (p, g_strdup_printf ("evp%d", n)); + } + g_ptr_array_add (p, NULL); + + nodes = (gchar **) g_ptr_array_free (p, FALSE); + + return nodes; +} + +/* Only allows certain objects, and aborts on unknowns */ +static GPtrArray * +subtree_introspect (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data) +{ + GPtrArray *interfaces; + + /* VPs implement the Foo interface, EVPs implement the Bar interface. The root + * does not implement any interfaces + */ + interfaces = g_ptr_array_new (); + if (g_str_has_prefix (node, "vp")) + { + g_ptr_array_add (interfaces, (gpointer) &foo_interface_info); + } + else if (g_str_has_prefix (node, "evp")) + { + g_ptr_array_add (interfaces, (gpointer) &bar_interface_info); + } + else if (g_strcmp0 (node, "/") == 0) + { + /* do nothing */ + } + else + { + g_assert_not_reached (); + } + + return interfaces; +} + +static const GDBusInterfaceVTable * +subtree_dispatch (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data) +{ + if (g_strcmp0 (interface_name, "org.example.Foo") == 0) + return &foo_vtable; + else + return NULL; +} + +static const GDBusSubtreeVTable subtree_vtable = +{ + subtree_enumerate, + subtree_introspect, + subtree_dispatch +}; + +/* -------------------- */ + +static gchar ** +dynamic_subtree_enumerate (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data) +{ + GPtrArray *data = user_data; + gchar **nodes = g_new (gchar*, data->len + 1); + guint n; + + for (n = 0; n < data->len; n++) + { + nodes[n] = g_strdup (g_ptr_array_index (data, n)); + } + nodes[data->len] = NULL; + + return nodes; +} + +/* Allow all objects to be introspected */ +static GPtrArray * +dynamic_subtree_introspect (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data) +{ + GPtrArray *interfaces; + + /* All nodes (including the root node) implements the Dyna interface */ + interfaces = g_ptr_array_new (); + g_ptr_array_add (interfaces, (gpointer) &dyna_interface_info); + + return interfaces; +} + +static const GDBusInterfaceVTable * +dynamic_subtree_dispatch (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data) +{ + *out_user_data = user_data; + return &dyna_interface_vtable; +} + +static const GDBusSubtreeVTable dynamic_subtree_vtable = +{ + dynamic_subtree_enumerate, + dynamic_subtree_introspect, + dynamic_subtree_dispatch +}; + +/* -------------------- */ + +static gpointer +test_dispatch_thread_func (gpointer user_data) +{ + const gchar *object_path = user_data; + GDBusProxy *foo_proxy; + GVariant *value; + GVariant *inner; + GError *error; + gchar *s; + const gchar *value_str; + + foo_proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.example.Foo", + NULL, + &error); + g_assert (foo_proxy != NULL); + + /* generic interfaces */ + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Peer.Ping", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_variant_unref (value); + + /* user methods */ + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "Method1", + g_variant_new ("(s)", "winwinwin"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)"))); + g_variant_get (value, "(s)", &value_str); + g_assert_cmpstr (value_str, ==, "You passed the string `winwinwin'. Jolly good!"); + g_variant_unref (value); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "Method2", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.example.SomeError: How do you like them apples, buddy!"); + g_error_free (error); + g_assert (value == NULL); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "Method2", + g_variant_new ("(s)", "failfailfail"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Signature of message, `s', does not match expected signature `'"); + g_error_free (error); + g_assert (value == NULL); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "NonExistantMethod", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method `NonExistantMethod'"); + g_error_free (error); + g_assert (value == NULL); + + /* user properties */ + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "PropertyUno"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(v)"))); + g_variant_get (value, "(v)", &inner); + g_assert (g_variant_is_of_type (inner, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (inner, NULL), ==, "Property `PropertyUno' Is What It Is!"); + g_variant_unref (value); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "ThisDoesntExist"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property `ThisDoesntExist'"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "NotReadable"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotReadable' is not readable"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.example.Foo", + "NotReadable", + g_variant_new_string ("But Writable you are!")), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value `NotReadable' to the property `'But Writable you are!''"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.example.Foo", + "NotWritable", + g_variant_new_uint32 (42)), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotWritable' is not writable"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.GetAll", + g_variant_new ("(s)", + "org.example.Foo"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(a{sv})"))); + s = g_variant_print (value, TRUE); + g_assert_cmpstr (s, ==, "({'PropertyUno': <\"Property `PropertyUno' Is What It Is!\">, 'NotWritable': <\"Property `NotWritable' Is What It Is!\">},)"); + g_free (s); + g_variant_unref (value); + + g_object_unref (foo_proxy); + + g_main_loop_quit (loop); + return NULL; +} + +static void +test_dispatch (const gchar *object_path) +{ + GThread *thread; + GError *error; + + /* run this in a thread to avoid deadlocks */ + error = NULL; + thread = g_thread_create (test_dispatch_thread_func, + (gpointer) object_path, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread != NULL); + g_main_loop_run (loop); + g_thread_join (thread); +} + +static void +test_object_registration (void) +{ + GError *error; + ObjectRegistrationData data; + GPtrArray *dyna_data; + gchar **nodes; + guint boss_foo_reg_id; + guint boss_bar_reg_id; + guint worker1_foo_reg_id; + guint worker2_bar_reg_id; + guint intern1_foo_reg_id; + guint intern2_bar_reg_id; + guint intern2_foo_reg_id; + guint intern3_bar_reg_id; + guint registration_id; + guint subtree_registration_id; + guint non_subtree_object_path_foo_reg_id; + guint non_subtree_object_path_bar_reg_id; + guint dyna_subtree_registration_id; + guint num_successful_registrations; + + data.num_unregistered_calls = 0; + data.num_unregistered_subtree_calls = 0; + data.num_subtree_nodes = 0; + + num_successful_registrations = 0; + + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss", + foo_interface_info.name, + &foo_interface_info, + &foo_vtable, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + boss_foo_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + boss_bar_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/worker1", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + worker1_foo_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/worker2", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + worker2_bar_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern1", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern1_foo_reg_id = registration_id; + num_successful_registrations++; + + /* ... and try again at another path */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern2_bar_reg_id = registration_id; + num_successful_registrations++; + + /* register at the same path/interface - this should fail */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + error = NULL; + g_assert (registration_id == 0); + + /* register at different interface - shouldn't fail */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern2_foo_reg_id = registration_id; + num_successful_registrations++; + + /* unregister it via the id */ + g_assert (g_dbus_connection_unregister_object (c, registration_id)); + g_assert_cmpint (data.num_unregistered_calls, ==, 1); + intern2_foo_reg_id = 0; + + /* register it back */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern2_foo_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern3", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern3_bar_reg_id = registration_id; + num_successful_registrations++; + + /* now register a whole subtree at /foo/boss/executives */ + subtree_registration_id = g_dbus_connection_register_subtree (c, + "/foo/boss/executives", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + &data, + on_subtree_unregistered, + &error); + g_assert_no_error (error); + g_assert (subtree_registration_id > 0); + /* try registering it again.. this should fail */ + registration_id = g_dbus_connection_register_subtree (c, + "/foo/boss/executives", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + &data, + on_subtree_unregistered, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + error = NULL; + g_assert (registration_id == 0); + + /* unregister it, then register it again */ + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 0); + g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id)); + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1); + subtree_registration_id = g_dbus_connection_register_subtree (c, + "/foo/boss/executives", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + &data, + on_subtree_unregistered, + &error); + g_assert_no_error (error); + g_assert (subtree_registration_id > 0); + + /* try to register something under /foo/boss/executives - this should work + * because registered subtrees and registered objects can coexist. + * + * Make the exported object implement *two* interfaces so we can check + * that the right introspection handler is invoked. + */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/executives/non_subtree_object", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + non_subtree_object_path_bar_reg_id = registration_id; + num_successful_registrations++; + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/executives/non_subtree_object", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + non_subtree_object_path_foo_reg_id = registration_id; + num_successful_registrations++; + + /* now register a dynamic subtree, spawning objects as they are called */ + dyna_data = g_ptr_array_new (); + dyna_subtree_registration_id = g_dbus_connection_register_subtree (c, + "/foo/dyna", + &dynamic_subtree_vtable, + G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES, + dyna_data, + (GDestroyNotify)g_ptr_array_unref, + &error); + g_assert_no_error (error); + g_assert (dyna_subtree_registration_id > 0); + + /* First assert that we have no nodes in the dynamic subtree */ + nodes = get_nodes_at (c, "/foo/dyna"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 0); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4); + + /* Install three nodes in the dynamic subtree via the dyna_data backdoor and + * assert that they show up correctly in the introspection data */ + g_ptr_array_add (dyna_data, "lol"); + g_ptr_array_add (dyna_data, "cat"); + g_ptr_array_add (dyna_data, "cheezburger"); + nodes = get_nodes_at (c, "/foo/dyna"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 3); + g_assert_cmpstr (nodes[0], ==, "lol"); + g_assert_cmpstr (nodes[1], ==, "cat"); + g_assert_cmpstr (nodes[2], ==, "cheezburger"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4); + + /* Call a non-existing object path and assert that it has been created */ + dyna_create (c, "dynamicallycreated"); + nodes = get_nodes_at (c, "/foo/dyna"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 4); + g_assert_cmpstr (nodes[0], ==, "lol"); + g_assert_cmpstr (nodes[1], ==, "cat"); + g_assert_cmpstr (nodes[2], ==, "cheezburger"); + g_assert_cmpstr (nodes[3], ==, "dynamicallycreated"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/dynamicallycreated"), ==, 4); + + /* now check that the object hierarachy is properly generated... yes, it's a bit + * perverse that we round-trip to the bus to introspect ourselves ;-) + */ + nodes = get_nodes_at (c, "/"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 1); + g_assert_cmpstr (nodes[0], ==, "foo"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/"), ==, 0); + + nodes = get_nodes_at (c, "/foo"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 2); + g_assert_cmpstr (nodes[0], ==, "boss"); + g_assert_cmpstr (nodes[1], ==, "dyna"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0); + + nodes = get_nodes_at (c, "/foo/boss"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 4); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker2")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "interns")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "executives")); + g_strfreev (nodes); + /* any registered object always implement org.freedesktop.DBus.[Peer,Introspectable,Properties] */ + g_assert_cmpint (count_interfaces (c, "/foo/boss"), ==, 5); + g_assert (has_interface (c, "/foo/boss", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss", bar_interface_info.name)); + + /* check subtree nodes - we should have only non_subtree_object in /foo/boss/executives + * because data.num_subtree_nodes is 0 + */ + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_assert_cmpint (g_strv_length (nodes), ==, 1); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives"), ==, 0); + + /* now change data.num_subtree_nodes and check */ + data.num_subtree_nodes = 2; + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 5); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1")); + /* check that /foo/boss/executives/non_subtree_object is not handled by the + * subtree handlers - we can do this because objects from subtree handlers + * has exactly one interface and non_subtree_object has two + */ + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/non_subtree_object"), ==, 5); + g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", bar_interface_info.name)); + /* check that the vp and evp objects are handled by the subtree handlers */ + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp0"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp1"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp0"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp1"), ==, 4); + g_assert (has_interface (c, "/foo/boss/executives/vp0", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/vp1", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/evp0", bar_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/evp1", bar_interface_info.name)); + g_strfreev (nodes); + data.num_subtree_nodes = 3; + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 7); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp2")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp2")); + g_strfreev (nodes); + + /* check that calls are properly dispatched to the functions in foo_vtable for objects + * implementing the org.example.Foo interface + * + * We do this for both a regular registered object (/foo/boss) and also for an object + * registered through the subtree mechanism. + */ + test_dispatch ("/foo/boss"); + test_dispatch ("/foo/boss/executives/vp0"); + + /* To prevent from exiting and attaching a D-Bus tool like D-Feet; uncomment: */ +#if 0 + g_debug ("Point D-feet or other tool at: %s", session_bus_get_temporary_address()); + g_main_loop_run (loop); +#endif + + /* check that unregistering the subtree handler works */ + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1); + g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id)); + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2); + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 1); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_strfreev (nodes); + + g_assert (g_dbus_connection_unregister_object (c, boss_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, boss_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, worker1_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, worker2_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern1_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern2_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern2_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern3_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_foo_reg_id)); + + g_assert_cmpint (data.num_unregistered_calls, ==, num_successful_registrations); + + /* check that we no longer export any objects - TODO: it looks like there's a bug in + * libdbus-1 here: libdbus still reports the '/foo' object; so disable the test for now + */ +#if 0 + nodes = get_nodes_at (c, "/"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 0); + g_strfreev (nodes); +#endif + + g_object_unref (c); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + g_test_add_func ("/gdbus/object-registration", test_object_registration); + /* TODO: check that we spit out correct introspection data */ + /* TODO: check that registering a whole subtree works */ + + ret = g_test_run(); + + /* tear down bus */ + session_bus_down (); + + return ret; +} diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c new file mode 100644 index 000000000..1b3d7a51f --- /dev/null +++ b/gio/tests/gdbus-introspection.c @@ -0,0 +1,169 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test introspection parser */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +introspection_on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + GError *error; + const gchar *xml_data; + GDBusNodeInfo *node_info; + const GDBusInterfaceInfo *interface_info; + const GDBusMethodInfo *method_info; + const GDBusSignalInfo *signal_info; + GVariant *result; + + error = NULL; + + /* + * Invoke Introspect(), then parse the output. + */ + result = g_dbus_proxy_invoke_method_sync (proxy, + "org.freedesktop.DBus.Introspectable.Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(s)", &xml_data); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + /* for now we only check a couple of things. TODO: check more things */ + + interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.NonExistantInterface"); + g_assert (interface_info == NULL); + + interface_info = g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.DBus.Introspectable"); + g_assert (interface_info != NULL); + method_info = g_dbus_interface_info_lookup_method (interface_info, "NonExistantMethod"); + g_assert (method_info == NULL); + method_info = g_dbus_interface_info_lookup_method (interface_info, "Introspect"); + g_assert (method_info != NULL); + g_assert (method_info->in_args == NULL); + g_assert (method_info->out_args != NULL); + g_assert (method_info->out_args[0] != NULL); + g_assert (method_info->out_args[1] == NULL); + g_assert_cmpstr (method_info->out_args[0]->signature, ==, "s"); + + interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.Frob"); + g_assert (interface_info != NULL); + signal_info = g_dbus_interface_info_lookup_signal (interface_info, "TestSignal"); + g_assert (signal_info != NULL); + g_assert (signal_info->args != NULL); + g_assert (signal_info->args[0] != NULL); + g_assert_cmpstr (signal_info->args[0]->signature, ==, "s"); + g_assert (signal_info->args[1] != NULL); + g_assert_cmpstr (signal_info->args[1]->signature, ==, "o"); + g_assert (signal_info->args[2] != NULL); + g_assert_cmpstr (signal_info->args[2]->signature, ==, "v"); + g_assert (signal_info->args[3] == NULL); + + g_dbus_node_info_unref (node_info); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +static void +introspection_on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +test_introspection_parser (void) +{ + guint watcher_id; + + session_bus_up (); + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + "/com/example/TestObject", + "com.example.Frob", + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + introspection_on_proxy_appeared, + introspection_on_proxy_vanished, + NULL, + NULL); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async ("./gdbus-testserver.py", NULL)); + + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); + + /* tear down bus */ + session_bus_down (); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/introspection-parser", test_introspection_parser); + return g_test_run(); +} diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c new file mode 100644 index 000000000..9008fa018 --- /dev/null +++ b/gio/tests/gdbus-names.c @@ -0,0 +1,749 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that g_bus_own_name() works correctly */ +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean expect_null_connection; + guint num_bus_acquired; + guint num_acquired; + guint num_lost; + guint num_free_func; +} OwnNameData; + +static void +own_name_data_free_func (OwnNameData *data) +{ + data->num_free_func++; + g_main_loop_quit (loop); +} + +static void +bus_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + OwnNameData *data = user_data; + g_dbus_connection_set_exit_on_close (connection, FALSE); + data->num_bus_acquired += 1; + g_main_loop_quit (loop); +} + +static void +name_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + OwnNameData *data = user_data; + data->num_acquired += 1; + g_main_loop_quit (loop); +} + +static void +name_lost_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + OwnNameData *data = user_data; + if (data->expect_null_connection) + { + g_assert (connection == NULL); + } + else + { + g_assert (connection != NULL); + g_dbus_connection_set_exit_on_close (connection, FALSE); + } + data->num_lost += 1; + g_main_loop_quit (loop); +} + +static void +test_bus_own_name (void) +{ + guint id; + guint id2; + OwnNameData data; + OwnNameData data2; + const gchar *name; + GDBusConnection *c; + GError *error; + gboolean name_has_owner_reply; + GDBusConnection *c2; + GVariant *result; + + error = NULL; + name = "org.gtk.GDBus.Name1"; + + /* + * First check that name_lost_handler() is invoked if there is no bus. + * + * Also make sure name_lost_handler() isn't invoked when unowning the name. + */ + data.num_bus_acquired = 0; + data.num_free_func = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = TRUE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 1); + g_bus_unown_name (id); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 1); + + /* + * Bring up a bus, then own a name and check bus_acquired_handler() then name_acquired_handler() is invoked. + */ + session_bus_up (); + data.num_bus_acquired = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + + /* + * Check that the name was actually acquired. + */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + result = g_dbus_connection_invoke_method_sync (c, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NameHasOwner", /* method name */ + g_variant_new ("(s)", name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(b)", &name_has_owner_reply); + g_assert (name_has_owner_reply); + g_variant_unref (result); + + /* + * Stop owning the name - this should invoke our free func + */ + g_bus_unown_name (id); + g_assert_cmpint (data.num_free_func, ==, 2); + + /* + * Check that the name was actually released. + */ + result = g_dbus_connection_invoke_method_sync (c, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NameHasOwner", /* method name */ + g_variant_new ("(s)", name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(b)", &name_has_owner_reply); + g_assert (!name_has_owner_reply); + g_variant_unref (result); + + /* + * Own the name again. + */ + data.num_bus_acquired = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + + /* + * Try owning the name with another object on the same connection - this should + * fail because we already own the name. + */ + data2.num_free_func = 0; + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + id2 = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + + /* + * Create a secondary (e.g. private) connection and try owning the name on that + * connection. This should fail both with and without _REPLACE because we + * didn't specify ALLOW_REPLACEMENT. + */ + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_is_closed (c2)); + /* first without _REPLACE */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + /* then with _REPLACE */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_REPLACE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + + /* + * Stop owning the name and grab it again with _ALLOW_REPLACEMENT. + */ + data.expect_null_connection = FALSE; + g_bus_unown_name (id); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 3); + /* grab it again */ + data.num_bus_acquired = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + + /* + * Now try to grab the name from the secondary connection. + * + */ + /* first without _REPLACE - this won't make us acquire the name */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + /* then with _REPLACE - here we should acquire the name - e.g. owner should lose it + * and owner2 should acquire it */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_REPLACE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + /* wait for handlers for both owner and owner2 to fire */ + while (data.num_lost == 0 || data2.num_acquired == 0) + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 1); + g_assert_cmpint (data2.num_lost, ==, 0); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + /* ok, make owner2 release the name - then wait for owner to automagically reacquire it */ + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_free_func, ==, 1); + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 2); + g_assert_cmpint (data.num_lost, ==, 1); + + /* + * Finally, nuke the bus and check name_lost_handler() is invoked. + * + */ + data.expect_null_connection = TRUE; + session_bus_down (); + while (data.num_lost != 2) + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 2); + g_assert_cmpint (data.num_lost, ==, 2); + g_bus_unown_name (id); + g_assert_cmpint (data.num_free_func, ==, 4); + + g_object_unref (c); + g_object_unref (c2); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that g_bus_watch_name() works correctly */ +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + gboolean expect_null_connection; + guint num_acquired; + guint num_lost; + guint num_appeared; + guint num_vanished; + guint num_free_func; +} WatchNameData; + +static void +watch_name_data_free_func (WatchNameData *data) +{ + data->num_free_func++; + g_main_loop_quit (loop); +} + +static void +w_bus_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +w_name_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WatchNameData *data = user_data; + data->num_acquired += 1; + g_main_loop_quit (loop); +} + +static void +w_name_lost_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WatchNameData *data = user_data; + data->num_lost += 1; + g_main_loop_quit (loop); +} + +static void +name_appeared_handler (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + WatchNameData *data = user_data; + if (data->expect_null_connection) + { + g_assert (connection == NULL); + } + else + { + g_assert (connection != NULL); + g_dbus_connection_set_exit_on_close (connection, FALSE); + } + data->num_appeared += 1; + g_main_loop_quit (loop); +} + +static void +name_vanished_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WatchNameData *data = user_data; + if (data->expect_null_connection) + { + g_assert (connection == NULL); + } + else + { + g_assert (connection != NULL); + g_dbus_connection_set_exit_on_close (connection, FALSE); + } + data->num_vanished += 1; + g_main_loop_quit (loop); +} + +static void +test_bus_watch_name (void) +{ + WatchNameData data; + guint id; + guint owner_id; + + /* + * First check that name_vanished_handler() is invoked if there is no bus. + * + * Also make sure name_vanished_handler() isn't invoked when unwatching the name. + */ + data.num_free_func = 0; + data.num_appeared = 0; + data.num_vanished = 0; + data.expect_null_connection = TRUE; + id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, + name_vanished_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 1); + g_bus_unwatch_name (id); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 1); + + /* + * Now bring up a bus, own a name, and then start watching it. + */ + session_bus_up (); + /* own the name */ + data.num_free_func = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_OWNER_FLAGS_NONE, + w_bus_acquired_handler, + w_name_acquired_handler, + w_name_lost_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + /* now watch the name */ + data.num_appeared = 0; + data.num_vanished = 0; + id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, + name_vanished_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_appeared, ==, 1); + g_assert_cmpint (data.num_vanished, ==, 0); + + /* + * Unwatch the name. + */ + g_bus_unwatch_name (id); + g_assert_cmpint (data.num_free_func, ==, 1); + + /* unown the name */ + g_bus_unown_name (owner_id); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 2); + + /* + * Create a watcher and then make a name be owned. + * + * This should trigger name_appeared_handler() ... + */ + /* watch the name */ + data.num_appeared = 0; + data.num_vanished = 0; + data.num_free_func = 0; + id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, + name_vanished_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 1); + + /* own the name */ + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_OWNER_FLAGS_NONE, + w_bus_acquired_handler, + w_name_acquired_handler, + w_name_lost_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + while (data.num_acquired == 0 || data.num_appeared == 0) + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + g_assert_cmpint (data.num_appeared, ==, 1); + g_assert_cmpint (data.num_vanished, ==, 1); + + /* + * Nuke the bus and check that the name vanishes and is lost. + */ + data.expect_null_connection = TRUE; + session_bus_down (); + g_main_loop_run (loop); + g_assert_cmpint (data.num_lost, ==, 1); + g_assert_cmpint (data.num_vanished, ==, 2); + + g_bus_unwatch_name (id); + g_assert_cmpint (data.num_free_func, ==, 1); + + g_bus_unown_name (owner_id); + g_assert_cmpint (data.num_free_func, ==, 2); + +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_validate_names (void) +{ + guint n; + static const struct + { + gboolean name; + gboolean unique; + gboolean interface; + const gchar *string; + } names[] = { + { 1, 0, 1, "valid.well_known.name"}, + { 1, 0, 0, "valid.well-known.name"}, + { 1, 1, 0, ":valid.unique.name"}, + { 0, 0, 0, "invalid.5well_known.name"}, + { 0, 0, 0, "4invalid.5well_known.name"}, + { 1, 1, 0, ":4valid.5unique.name"}, + { 0, 0, 0, ""}, + { 1, 0, 1, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name1"}, /* 255 */ + { 0, 0, 0, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name12"}, /* 256 - too long! */ + { 0, 0, 0, ".starts.with.a.dot"}, + { 0, 0, 0, "contains.invalid;.characters"}, + { 0, 0, 0, "contains.inva/lid.characters"}, + { 0, 0, 0, "contains.inva[lid.characters"}, + { 0, 0, 0, "contains.inva]lid.characters"}, + { 0, 0, 0, "contains.inva_æøå_lid.characters"}, + { 1, 1, 0, ":1.1"}, + }; + + for (n = 0; n < G_N_ELEMENTS (names); n++) + { + if (names[n].name) + g_assert (g_dbus_is_name (names[n].string)); + else + g_assert (!g_dbus_is_name (names[n].string)); + + if (names[n].unique) + g_assert (g_dbus_is_unique_name (names[n].string)); + else + g_assert (!g_dbus_is_unique_name (names[n].string)); + + if (names[n].interface) + g_assert (g_dbus_is_interface_name (names[n].string)); + else + g_assert (!g_dbus_is_interface_name (names[n].string)); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/validate-names", test_validate_names); + g_test_add_func ("/gdbus/bus-own-name", test_bus_own_name); + g_test_add_func ("/gdbus/bus-watch-name", test_bus_watch_name); + + ret = g_test_run(); + + g_main_loop_unref (loop); + + return ret; +} diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c new file mode 100644 index 000000000..ae6d68dd4 --- /dev/null +++ b/gio/tests/gdbus-peer.c @@ -0,0 +1,746 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +/* for open(2) */ +#include +#include +#include + +#include + +#include "gdbus-tests.h" + + +#ifdef G_OS_UNIX +static gboolean is_unix = TRUE; +#else +static gboolean is_unix = FALSE; +#endif + +static gchar *test_guid = NULL; +static GMainLoop *service_loop = NULL; +static GDBusServer *server = NULL; +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that peer-to-peer connections work */ +/* ---------------------------------------------------------------------------------------------------- */ + + +typedef struct +{ + gboolean accept_connection; + gint num_connection_attempts; + GPtrArray *current_connections; + guint num_method_calls; + gboolean signal_received; +} PeerData; + +static const gchar *test_interface_introspection_xml = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; +static const GDBusInterfaceInfo *test_interface_introspection_data = NULL; + +static void +test_interface_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) +{ + PeerData *data = user_data; + + data->num_method_calls++; + + g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); + + if (g_strcmp0 (method_name, "HelloPeer") == 0) + { + const gchar *greeting; + gchar *response; + + g_variant_get (parameters, "(s)", &greeting); + + response = g_strdup_printf ("You greeted me with '%s'.", + greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); + } + else if (g_strcmp0 (method_name, "EmitSignal") == 0) + { + GError *error; + + error = NULL; + g_dbus_connection_emit_signal (connection, + NULL, + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + "PeerSignal", + NULL, + &error); + g_assert_no_error (error); + g_dbus_method_invocation_return_value (invocation, NULL); + } + else if (g_strcmp0 (method_name, "OpenFile") == 0) + { + const gchar *path; + GDBusMessage *reply; + GError *error; + gint fd; + GUnixFDList *fd_list; + + g_variant_get (parameters, "(s)", &path); + + fd_list = g_unix_fd_list_new (); + + error = NULL; + + fd = open (path, O_RDONLY); + g_unix_fd_list_append (fd_list, fd, &error); + g_assert_no_error (error); + close (fd); + + reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); + g_dbus_message_set_unix_fd_list (reply, fd_list); + g_object_unref (invocation); + + error = NULL; + g_dbus_connection_send_message (connection, + reply, + NULL, /* out_serial */ + &error); + g_assert_no_error (error); + g_object_unref (reply); + } + else + { + g_assert_not_reached (); + } +} + +static GVariant * +test_interface_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); + g_assert_cmpstr (property_name, ==, "PeerProperty"); + + return g_variant_new_string ("ThePropertyValue"); +} + + +static const GDBusInterfaceVTable test_interface_vtable = +{ + test_interface_method_call, + test_interface_get_property, + NULL /* set_property */ +}; + +static void +on_proxy_signal_received (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + PeerData *data = user_data; + + data->signal_received = TRUE; + + g_assert (sender_name == NULL); + g_assert_cmpstr (signal_name, ==, "PeerSignal"); + g_main_loop_quit (loop); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +on_deny_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials, + gpointer user_data) +{ + PeerData *data = user_data; + gboolean deny_peer; + + data->num_connection_attempts++; + + deny_peer = FALSE; + if (!data->accept_connection) + { + deny_peer = TRUE; + g_main_loop_quit (loop); + } + + return deny_peer; +} + +/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ +static void +on_new_connection (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + PeerData *data = user_data; + GError *error; + guint reg_id; + + //g_print ("Client connected.\n" + // "Negotiated capabilities: unix-fd-passing=%d\n", + // g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); + + g_ptr_array_add (data->current_connections, g_object_ref (connection)); + + /* export object on the newly established connection */ + error = NULL; + reg_id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + test_interface_introspection_data, + &test_interface_vtable, + data, + NULL, /* GDestroyNotify for data */ + &error); + g_assert_no_error (error); + g_assert (reg_id > 0); + + g_main_loop_quit (loop); +} + +static gpointer +service_thread_func (gpointer user_data) +{ + PeerData *data = user_data; + GMainContext *service_context; + GDBusAuthObserver *observer; + GError *error; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + error = NULL; + observer = g_dbus_auth_observer_new (); + server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:", + G_DBUS_SERVER_FLAGS_NONE, + test_guid, + observer, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + + g_signal_connect (server, + "new-connection", + G_CALLBACK (on_new_connection), + data); + g_signal_connect (observer, + "deny-authenticated-peer", + G_CALLBACK (on_deny_authenticated_peer), + data); + g_object_unref (observer); + + g_dbus_server_start (server); + + service_loop = g_main_loop_new (service_context, FALSE); + g_main_loop_run (service_loop); + + g_main_context_pop_thread_default (service_context); + + g_main_loop_unref (service_loop); + g_main_context_unref (service_context); + + /* test code specifically unrefs the server - see below */ + g_assert (server == NULL); + + return NULL; +} + +#if 0 +static gboolean +on_incoming_connection (GSocketService *service, + GSocketConnection *socket_connection, + GObject *source_object, + gpointer user_data) +{ + PeerData *data = user_data; + + if (data->accept_connection) + { + GError *error; + guint reg_id; + GDBusConnection *connection; + + error = NULL; + connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), + test_guid, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + + g_ptr_array_add (data->current_connections, connection); + + /* export object on the newly established connection */ + error = NULL; + reg_id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + &test_interface_introspection_data, + &test_interface_vtable, + data, + NULL, /* GDestroyNotify for data */ + &error); + g_assert_no_error (error); + g_assert (reg_id > 0); + + } + else + { + /* don't do anything */ + } + + data->num_connection_attempts++; + + g_main_loop_quit (loop); + + /* stops other signal handlers from being invoked */ + return TRUE; +} + +static gpointer +service_thread_func (gpointer data) +{ + GMainContext *service_context; + gchar *socket_path; + GSocketAddress *address; + GError *error; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ()); + address = g_unix_socket_address_new (socket_path); + + service = g_socket_service_new (); + error = NULL; + g_socket_listener_add_address (G_SOCKET_LISTENER (service), + address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, /* source_object */ + NULL, /* effective_address */ + &error); + g_assert_no_error (error); + g_signal_connect (service, + "incoming", + G_CALLBACK (on_incoming_connection), + data); + g_socket_service_start (service); + + service_loop = g_main_loop_new (service_context, FALSE); + g_main_loop_run (service_loop); + + g_main_context_pop_thread_default (service_context); + + g_main_loop_unref (service_loop); + g_main_context_unref (service_context); + + g_object_unref (address); + g_free (socket_path); + return NULL; +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +#if 0 +static gboolean +check_connection (gpointer user_data) +{ + PeerData *data = user_data; + guint n; + + for (n = 0; n < data->current_connections->len; n++) + { + GDBusConnection *c; + GIOStream *stream; + + c = G_DBUS_CONNECTION (data->current_connections->pdata[n]); + stream = g_dbus_connection_get_stream (c); + + g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream); + g_debug ("closed = %d", g_io_stream_is_closed (stream)); + + GSocket *socket; + socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream)); + g_debug ("socket_closed = %d", g_socket_is_closed (socket)); + g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP)); + + gchar buf[128]; + GError *error; + gssize num_read; + error = NULL; + num_read = g_input_stream_read (g_io_stream_get_input_stream (stream), + buf, + 128, + NULL, + &error); + if (num_read < 0) + { + g_debug ("error: %s", error->message); + g_error_free (error); + } + else + { + g_debug ("no error, read %d bytes", (gint) num_read); + } + } + + return FALSE; +} + +static gboolean +on_do_disconnect_in_idle (gpointer data) +{ + GDBusConnection *c = G_DBUS_CONNECTION (data); + g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count); + g_dbus_connection_disconnect (c); + g_object_unref (c); + return FALSE; +} +#endif + +static void +test_peer (void) +{ + GDBusConnection *c; + GDBusConnection *c2; + GDBusProxy *proxy; + GError *error; + PeerData data; + GVariant *value; + GVariant *result; + const gchar *s; + GThread *service_thread; + + memset (&data, '\0', sizeof (PeerData)); + data.current_connections = g_ptr_array_new_with_free_func (g_object_unref); + + /* first try to connect when there is no server */ + error = NULL; + c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" : + /* NOTE: Even if something is listening on port 12345 the connection + * will fail because the nonce file doesn't exist */ + "nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus", + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + _g_assert_error_domain (error, G_IO_ERROR); + g_assert (!g_dbus_error_is_remote_error (error)); + g_clear_error (&error); + g_assert (c == NULL); + + /* bring up a server - we run the server in a different thread to avoid deadlocks */ + error = NULL; + service_thread = g_thread_create (service_thread_func, + &data, + TRUE, + &error); + while (service_loop == NULL) + g_thread_yield (); + g_assert (server != NULL); + + /* bring up a connection and accept it */ + data.accept_connection = TRUE; + error = NULL; + c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (c != NULL); + while (data.current_connections->len < 1) + g_main_loop_run (loop); + g_assert_cmpint (data.current_connections->len, ==, 1); + g_assert_cmpint (data.num_connection_attempts, ==, 1); + //g_assert (g_dbus_connection_get_bus_type (c) == G_BUS_TYPE_NONE); + g_assert (g_dbus_connection_get_unique_name (c) == NULL); + g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); + + /* check that we create a proxy, read properties, receive signals and invoke + * the HelloPeer() method. Since the server runs in another thread it's fine + * to use synchronous blocking API here. + */ + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + NULL, /* bus_name */ + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + error = NULL; + value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error); + g_assert_no_error (error); + g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue"); + + /* try invoking a method */ + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloPeer", + g_variant_new ("(s)", "Hey Peer!"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (result, "(s)", &s); + g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'."); + g_variant_unref (result); + g_assert_cmpint (data.num_method_calls, ==, 1); + + /* make the other peer emit a signal - catch it */ + g_signal_connect (proxy, + "g-signal", + G_CALLBACK (on_proxy_signal_received), + &data); + g_assert (!data.signal_received); + g_dbus_proxy_invoke_method (proxy, + "EmitSignal", + NULL, /* no arguments */ + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + NULL, /* GAsyncReadyCallback - we don't care about the result */ + NULL); /* user_data */ + g_main_loop_run (loop); + g_assert (data.signal_received); + g_assert_cmpint (data.num_method_calls, ==, 2); + + /* check for UNIX fd passing */ +#ifdef G_OS_UNIX + { + GDBusMessage *method_call_message; + GDBusMessage *method_reply_message; + GUnixFDList *fd_list; + gint fd; + gchar buf[1024]; + gssize len; + gchar *buf2; + gsize len2; + + method_call_message = g_dbus_message_new_method_call (NULL, /* name */ + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + "OpenFile"); + g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", "/etc/hosts")); + error = NULL; + method_reply_message = g_dbus_connection_send_message_with_reply_sync (c, + method_call_message, + -1, + NULL, /* out_serial */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); + fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); + g_assert (fd_list != NULL); + g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); + error = NULL; + fd = g_unix_fd_list_get (fd_list, 0, &error); + g_assert_no_error (error); + g_object_unref (method_call_message); + g_object_unref (method_reply_message); + + memset (buf, '\0', sizeof (buf)); + len = read (fd, buf, sizeof (buf) - 1); + close (fd); + + error = NULL; + g_file_get_contents ("/etc/hosts", + &buf2, + &len2, + &error); + g_assert_no_error (error); + if (len2 > sizeof (buf)) + buf2[sizeof (buf)] = '\0'; + g_assert_cmpstr (buf, ==, buf2); + g_free (buf2); + } +#endif /* G_OS_UNIX */ + + + /* bring up a connection - don't accept it - this should fail + */ + data.accept_connection = FALSE; + error = NULL; + c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + _g_assert_error_domain (error, G_IO_ERROR); + g_assert (c2 == NULL); + +#if 0 + /* TODO: THIS TEST DOESN'T WORK YET */ + + /* bring up a connection - accept it.. then disconnect from the client side - check + * that the server side gets the disconnect signal. + */ + error = NULL; + data.accept_connection = TRUE; + c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_get_is_disconnected (c2)); + while (data.num_connection_attempts < 3) + g_main_loop_run (loop); + g_assert_cmpint (data.current_connections->len, ==, 2); + g_assert_cmpint (data.num_connection_attempts, ==, 3); + g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); + g_idle_add (on_do_disconnect_in_idle, c2); + g_debug ("=================================================="); + g_debug ("=================================================="); + g_debug ("=================================================="); + g_debug ("waiting for disconnect on connection %p, stream %p", + data.current_connections->pdata[1], + g_dbus_connection_get_stream (data.current_connections->pdata[1])); + + g_timeout_add (2000, check_connection, &data); + //_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed"); + g_main_loop_run (loop); + g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); + g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */ +#endif + + /* unref the server and stop listening for new connections + * + * This won't bring down the established connections - check that c is still connected + * by invoking a method + */ + //g_socket_service_stop (service); + //g_object_unref (service); + g_dbus_server_stop (server); + g_object_unref (server); + server = NULL; + + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloPeer", + g_variant_new ("(s)", "Hey Again Peer!"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (result, "(s)", &s); + g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'."); + g_variant_unref (result); + g_assert_cmpint (data.num_method_calls, ==, 4); + +#if 0 + /* TODO: THIS TEST DOESN'T WORK YET */ + + /* now disconnect from the server side - check that the client side gets the signal */ + g_assert_cmpint (data.current_connections->len, ==, 1); + g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c); + g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0])); + if (!g_dbus_connection_get_is_disconnected (c)) + _g_assert_signal_received (c, "closed"); + g_assert (g_dbus_connection_get_is_disconnected (c)); +#endif + + g_object_unref (c); + g_ptr_array_unref (data.current_connections); + g_object_unref (proxy); + + g_main_loop_quit (service_loop); + g_thread_join (service_thread); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + GDBusNodeInfo *introspection_data = NULL; + + g_type_init (); + g_thread_init (NULL); + g_test_init (&argc, &argv, NULL); + + introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL); + g_assert (introspection_data != NULL); + test_interface_introspection_data = introspection_data->interfaces[0]; + + test_guid = g_dbus_generate_guid (); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + g_test_add_func ("/gdbus/peer-to-peer", test_peer); + + ret = g_test_run(); + + g_main_loop_unref (loop); + g_free (test_guid); + g_dbus_node_info_unref (introspection_data); + + return ret; +} diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c new file mode 100644 index 000000000..6e45f4217 --- /dev/null +++ b/gio/tests/gdbus-proxy.c @@ -0,0 +1,455 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that the method aspects of GDBusProxy works */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_methods (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GVariant *result; + GError *error; + const gchar *str; + gchar *dbus_error_name; + + /* check that we can invoke a method */ + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloWorld", + g_variant_new ("(s)", "Hey"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)"); + g_variant_get (result, "(s)", &str); + g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!"); + g_variant_unref (result); + + /* Check that we can completely recover the returned error */ + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloWorld", + g_variant_new ("(s)", "Yo"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException"); + g_free (dbus_error_name); + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting"); + g_clear_error (&error); + + /* Check that we get a timeout if the method handling is taking longer than timeout */ + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 100 /* msec */, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + g_assert (!g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + g_clear_error (&error); + + /* Check that proxy-default timeouts work. */ + g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1); + + /* the default timeout is 25000 msec so this should work */ + result = g_dbus_proxy_invoke_method_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */ + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + + /* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */ + g_dbus_proxy_set_default_timeout (proxy, 250); + g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250); + result = g_dbus_proxy_invoke_method_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, /* use proxy default (e.g. 250 msec) */ + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + g_assert (!g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + g_clear_error (&error); + + /* clean up after ourselves */ + g_dbus_proxy_set_default_timeout (proxy, -1); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that the property aspects of GDBusProxy works */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_properties (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GError *error; + GVariant *variant; + GVariant *variant2; + GVariant *result; + + error = NULL; + + /* + * Check that we can read cached properties. + * + * No need to test all properties - GVariant has already been tested + */ + variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); + g_assert_no_error (error); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (proxy, "o", &error); + g_assert_no_error (error); + g_assert (variant != NULL); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path"); + g_variant_unref (variant); + + /* + * Now ask the service to change a property and check that #GDBusProxy::g-property-changed + * is received. Also check that the cache is updated. + */ + variant2 = g_variant_new_byte (42); + result = g_dbus_proxy_invoke_method_sync (proxy, + "FrobSetProperty", + g_variant_new ("(sv)", + "y", + variant2), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + _g_assert_signal_received (proxy, "g-properties-changed"); + variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); + g_assert_no_error (error); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 42); + g_variant_unref (variant); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that the signal aspects of GDBusProxy works */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_proxy_signals_on_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GString *s = user_data; + + g_assert_cmpstr (signal_name, ==, "TestSignal"); + g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)"); + + g_variant_print_string (parameters, s, TRUE); +} + +typedef struct +{ + GMainLoop *internal_loop; + GString *s; +} TestSignalData; + +static void +test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + TestSignalData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + + /* check that the signal was recieved before we got the method result */ + g_assert (strlen (data->s->str) > 0); + + /* break out of the loop */ + g_main_loop_quit (data->internal_loop); +} + +static void +test_signals (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GError *error; + GString *s; + gulong signal_handler_id; + TestSignalData data; + GVariant *result; + + error = NULL; + + /* + * Ask the service to emit a signal and check that we receive it. + * + * Note that blocking calls don't block in the mainloop so wait for the signal (which + * is dispatched before the method reply) + */ + s = g_string_new (NULL); + signal_handler_id = g_signal_connect (proxy, + "g-signal", + G_CALLBACK (test_proxy_signals_on_signal), + s); + + result = g_dbus_proxy_invoke_method_sync (proxy, + "EmitSignal", + g_variant_new ("(so)", + "Accept the next proposition you hear", + "/some/path"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + /* check that we haven't received the signal just yet */ + g_assert (strlen (s->str) == 0); + /* and now wait for the signal */ + _g_assert_signal_received (proxy, "g-signal"); + g_assert_cmpstr (s->str, + ==, + "('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)"); + g_signal_handler_disconnect (proxy, signal_handler_id); + g_string_free (s, TRUE); + + /* + * Now do this async to check the signal is received before the method returns. + */ + s = g_string_new (NULL); + data.internal_loop = g_main_loop_new (NULL, FALSE); + data.s = s; + signal_handler_id = g_signal_connect (proxy, + "g-signal", + G_CALLBACK (test_proxy_signals_on_signal), + s); + g_dbus_proxy_invoke_method (proxy, + "EmitSignal", + g_variant_new ("(so)", + "You will make a great programmer", + "/some/other/path"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb, + &data); + g_main_loop_run (data.internal_loop); + g_main_loop_unref (data.internal_loop); + g_assert_cmpstr (s->str, + ==, + "('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)"); + g_signal_handler_disconnect (proxy, signal_handler_id); + g_string_free (s, TRUE); +} + +static void +test_bogus_method_return (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GError *error = NULL; + GVariant *result; + + result = g_dbus_proxy_invoke_method_sync (proxy, + "PairReturn", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert (result == NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static const gchar *frob_dbus_interface_xml = + "" + " " + /* Deliberately different from gdbus-testserver.py's definition */ + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; +static GDBusInterfaceInfo *frob_dbus_interface_info; + +static void +on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + test_methods (connection, name, name_owner, proxy); + test_properties (connection, name, name_owner, proxy); + test_signals (connection, name, name_owner, proxy); + + /* Now repeat the method tests, with an expected interface set */ + g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); + test_methods (connection, name, name_owner, proxy); + + /* And now one more test where we deliberately set the expected + * interface definition incorrectly + */ + test_bogus_method_return (connection, name, name_owner, proxy); + + g_main_loop_quit (loop); +} + +static void +on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +test_proxy (void) +{ + guint watcher_id; + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + "/com/example/TestObject", + "com.example.Frob", + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + on_proxy_appeared, + on_proxy_vanished, + NULL, + NULL); + + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async ("./gdbus-testserver.py", NULL)); + + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); + + /* tear down bus */ + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + GDBusNodeInfo *introspection_data = NULL; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL); + g_assert (introspection_data != NULL); + frob_dbus_interface_info = introspection_data->interfaces[0]; + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/proxy", test_proxy); + + ret = g_test_run(); + + g_dbus_node_info_unref (introspection_data); + return ret; +} diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c new file mode 100644 index 000000000..91b79f81b --- /dev/null +++ b/gio/tests/gdbus-serialization.c @@ -0,0 +1,650 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 + +#include +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +hexdump (const guchar *str, gsize len) +{ + const guchar *data = (const guchar *) str; + guint n, m; + + for (n = 0; n < len; n += 16) + { + g_printerr ("%04x: ", n); + + for (m = n; m < n + 16; m++) + { + if (m > n && (m%4) == 0) + g_printerr (" "); + if (m < len) + g_printerr ("%02x ", data[m]); + else + g_printerr (" "); + } + + g_printerr (" "); + + for (m = n; m < len && m < n + 16; m++) + g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.'); + + g_printerr ("\n"); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +append_gv_to_dbus_iter (DBusMessageIter *iter, + GVariant *value, + GError **error) +{ + const GVariantType *type; + + type = g_variant_get_type (value); + if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) + { + dbus_bool_t v = g_variant_get_boolean (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE)) + { + guint8 v = g_variant_get_byte (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) + { + gint16 v = g_variant_get_int16 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) + { + guint16 v = g_variant_get_uint16 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + { + gint32 v = g_variant_get_int32 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) + { + guint32 v = g_variant_get_uint32 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) + { + gint64 v = g_variant_get_int64 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) + { + guint64 v = g_variant_get_uint64 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) + { + gdouble v = g_variant_get_double (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) + { + const gchar *v = g_variant_get_string (value, NULL); + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) + { + const gchar *v = g_variant_get_string (value, NULL); + dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE)) + { + const gchar *v = g_variant_get_string (value, NULL); + dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v); + } + else if (g_variant_type_is_variant (type)) + { + DBusMessageIter sub; + GVariant *child; + + child = g_variant_get_child_value (value, 0); + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, + g_variant_get_type_string (child), + &sub); + if (!append_gv_to_dbus_iter (&sub, child, error)) + { + g_variant_unref (child); + goto fail; + } + dbus_message_iter_close_container (iter, &sub); + g_variant_unref (child); + } + else if (g_variant_type_is_array (type)) + { + DBusMessageIter dbus_iter; + const gchar *type_string; + GVariantIter gv_iter; + GVariant *item; + + type_string = g_variant_get_type_string (value); + type_string++; /* skip the 'a' */ + + dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, + type_string, &dbus_iter); + g_variant_iter_init (&gv_iter, value); + + while ((item = g_variant_iter_next_value (&gv_iter))) + { + if (!append_gv_to_dbus_iter (&dbus_iter, item, error)) + { + goto fail; + } + } + + dbus_message_iter_close_container (iter, &dbus_iter); + } + else if (g_variant_type_is_tuple (type)) + { + DBusMessageIter dbus_iter; + GVariantIter gv_iter; + GVariant *item; + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, + NULL, &dbus_iter); + g_variant_iter_init (&gv_iter, value); + + while ((item = g_variant_iter_next_value (&gv_iter))) + { + if (!append_gv_to_dbus_iter (&dbus_iter, item, error)) + goto fail; + } + + dbus_message_iter_close_container (iter, &dbus_iter); + } + else if (g_variant_type_is_dict_entry (type)) + { + DBusMessageIter dbus_iter; + GVariant *key, *val; + + dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, + NULL, &dbus_iter); + key = g_variant_get_child_value (value, 0); + if (!append_gv_to_dbus_iter (&dbus_iter, key, error)) + { + g_variant_unref (key); + goto fail; + } + g_variant_unref (key); + + val = g_variant_get_child_value (value, 1); + if (!append_gv_to_dbus_iter (&dbus_iter, val, error)) + { + g_variant_unref (val); + goto fail; + } + g_variant_unref (val); + + dbus_message_iter_close_container (iter, &dbus_iter); + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Error serializing GVariant with type-string `%s' to a D-Bus message", + g_variant_get_type_string (value)); + goto fail; + } + + return TRUE; + + fail: + return FALSE; +} + +static gboolean +append_gv_to_dbus_message (DBusMessage *message, + GVariant *value, + GError **error) +{ + gboolean ret; + guint n; + + ret = FALSE; + + if (value != NULL) + { + DBusMessageIter iter; + GVariantIter gv_iter; + GVariant *item; + + dbus_message_iter_init_append (message, &iter); + + g_variant_iter_init (&gv_iter, value); + n = 0; + while ((item = g_variant_iter_next_value (&gv_iter))) + { + if (!append_gv_to_dbus_iter (&iter, item, error)) + { + g_prefix_error (error, + "Error encoding in-arg %d: ", + n); + goto out; + } + n++; + } + } + + ret = TRUE; + + out: + return ret; +} + +static void +print_gv_dbus_message (GVariant *value) +{ + DBusMessage *message; + char *blob; + int blob_len; + GError *error; + + message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL); + dbus_message_set_serial (message, 0x41); + dbus_message_set_path (message, "/foo/bar"); + dbus_message_set_member (message, "Member"); + + error = NULL; + if (!append_gv_to_dbus_message (message, value, &error)) + { + g_printerr ("Error printing GVariant as DBusMessage: %s", error->message); + g_error_free (error); + goto out; + } + + dbus_message_marshal (message, &blob, &blob_len); + g_printerr ("\n"); + hexdump ((guchar *) blob, blob_len); + out: + dbus_message_unref (message); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +dbus_1_message_append (GString *s, + guint indent, + DBusMessageIter *iter) +{ + gint arg_type; + DBusMessageIter sub; + + g_string_append_printf (s, "%*s", indent, ""); + + arg_type = dbus_message_iter_get_arg_type (iter); + switch (arg_type) + { + case DBUS_TYPE_BOOLEAN: + { + dbus_bool_t value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "bool: %s\n", value ? "true" : "false"); + break; + } + + case DBUS_TYPE_BYTE: + { + guchar value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "byte: 0x%02x\n", (guint) value); + break; + } + + case DBUS_TYPE_INT16: + { + gint16 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_UINT16: + { + guint16 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_INT32: + { + gint32 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_UINT32: + { + guint32 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_INT64: + { + gint64 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_UINT64: + { + guint64 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_DOUBLE: + { + gdouble value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "double: %f\n", value); + break; + } + + case DBUS_TYPE_STRING: + { + const gchar *value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "string: `%s'\n", value); + break; + } + + case DBUS_TYPE_OBJECT_PATH: + { + const gchar *value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "object_path: `%s'\n", value); + break; + } + + case DBUS_TYPE_SIGNATURE: + { + const gchar *value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "signature: `%s'\n", value); + break; + } + + case DBUS_TYPE_VARIANT: + g_string_append_printf (s, "variant:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + case DBUS_TYPE_ARRAY: + g_string_append_printf (s, "array:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + case DBUS_TYPE_STRUCT: + g_string_append_printf (s, "struct:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + case DBUS_TYPE_DICT_ENTRY: + g_string_append_printf (s, "dict_entry:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + default: + g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)", + arg_type, + arg_type); + g_assert_not_reached (); + break; + } +} + +static gchar * +dbus_1_message_print (DBusMessage *message) +{ + GString *s; + guint n; + DBusMessageIter iter; + + s = g_string_new (NULL); + n = 0; + dbus_message_iter_init (message, &iter); + while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) + { + g_string_append_printf (s, "value %d: ", n); + dbus_1_message_append (s, 2, &iter); + dbus_message_iter_next (&iter); + n++; + } + + return g_string_free (s, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +get_body_signature (GVariant *value) +{ + const gchar *s; + gsize len; + gchar *ret; + + s = g_variant_get_type_string (value); + len = strlen (s); + g_assert (len>=2); + + ret = g_strndup (s + 1, len - 2); + + return ret; +} + +static void +check_serialization (GVariant *value, + const gchar *expected_dbus_1_output) +{ + guchar *blob; + gsize blob_size; + DBusMessage *dbus_1_message; + GDBusMessage *message; + GDBusMessage *recovered_message; + GError *error; + DBusError dbus_error; + gchar *s; + gchar *s1; + + message = g_dbus_message_new (); + g_dbus_message_set_body (message, value); + g_dbus_message_set_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL); + g_dbus_message_set_serial (message, 0x41); + s = get_body_signature (value); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar")); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member")); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s)); + g_free (s); + + /* First check that the serialization to the D-Bus wire format is correct */ + + error = NULL; + blob = g_dbus_message_to_blob (message, + &blob_size, + &error); + g_assert_no_error (error); + g_assert (blob != NULL); + + dbus_error_init (&dbus_error); + dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error); + if (dbus_error_is_set (&dbus_error)) + { + g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n", + dbus_error.name, + dbus_error.message); + hexdump (blob, blob_size); + dbus_error_free (&dbus_error); + + s = g_variant_print (value, TRUE); + g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s); + g_free (s); + + g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n"); + print_gv_dbus_message (value); + + g_assert_not_reached (); + } + + s = dbus_1_message_print (dbus_1_message); + dbus_message_unref (dbus_1_message); + + g_assert_cmpstr (s, ==, expected_dbus_1_output); + g_free (s); + + /* Then serialize back and check that the body is identical */ + + error = NULL; + recovered_message = g_dbus_message_new_from_blob (blob, blob_size, &error); + g_assert_no_error (error); + g_assert (recovered_message != NULL); + g_assert (g_dbus_message_get_body (recovered_message) != NULL); + + if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value)) + { + s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE); + s1 = g_variant_print (value, TRUE); + g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n", + s, + s1); + g_free (s); + g_free (s1); + g_assert_not_reached (); + } + g_object_unref (message); + g_object_unref (recovered_message); +} + +static void +message_serialize_basic (void) +{ + check_serialization (g_variant_new ("(sogybnqiuxtd)", + "this is a string", + "/this/is/a/path", + "sad", + 42, + TRUE, + -42, + 60000, + -44, + 100000, + -G_GINT64_CONSTANT(2)<<34, + G_GUINT64_CONSTANT(0xffffffffffffffff), + 42.5), + "value 0: string: `this is a string'\n" + "value 1: object_path: `/this/is/a/path'\n" + "value 2: signature: `sad'\n" + "value 3: byte: 0x2a\n" + "value 4: bool: true\n" + "value 5: int16: -42\n" + "value 6: uint16: 60000\n" + "value 7: int32: -44\n" + "value 8: uint32: 100000\n" + "value 9: int64: -34359738368\n" + "value 10: uint64: 18446744073709551615\n" + "value 11: double: 42.500000\n"); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +message_serialize_complex (void) +{ + GError *error; + GVariant *value; + + error = NULL; + value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"), + "([1, 2, 3], {'one': 'white', 'two': 'black'})", + NULL, NULL, &error); + g_assert_no_error (error); + g_assert (value != NULL); + check_serialization (value, + "value 0: array:\n" + " int32: 1\n" + " int32: 2\n" + " int32: 3\n" + "value 1: array:\n" + " dict_entry:\n" + " string: `one'\n" + " string: `white'\n" + " dict_entry:\n" + " string: `two'\n" + " string: `black'\n"); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic); + g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex); + return g_test_run(); +} + diff --git a/gio/tests/gdbus-sessionbus.c b/gio/tests/gdbus-sessionbus.c new file mode 100644 index 000000000..1e16eddc5 --- /dev/null +++ b/gio/tests/gdbus-sessionbus.c @@ -0,0 +1,342 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include +#include +#include +#include +#include + +#include "gdbus-sessionbus.h" + +/* ---------------------------------------------------------------------------------------------------- */ +/* Utilities for bringing up and tearing down session message bus instances */ + +static void +watch_parent (gint fd) +{ + GPollFD fds[1]; + gint num_events; + gchar buf[512]; + guint bytes_read; + GArray *buses_to_kill_array; + + fds[0].fd = fd; + fds[0].events = G_IO_HUP | G_IO_IN; + fds[0].revents = 0; + + buses_to_kill_array = g_array_new (FALSE, TRUE, sizeof (guint)); + + do + { + guint pid; + guint n; + + num_events = g_poll (fds, 1, -1); + if (num_events == 0) + continue; + + if (fds[0].revents == G_IO_HUP) + { + for (n = 0; n < buses_to_kill_array->len; n++) + { + pid = g_array_index (buses_to_kill_array, guint, n); + g_print ("cleaning up bus with pid %d\n", pid); + kill (pid, SIGTERM); + } + g_array_free (buses_to_kill_array, TRUE); + exit (0); + } + + //g_debug ("data from parent"); + + memset (buf, '\0', sizeof buf); + again: + bytes_read = read (fds[0].fd, buf, sizeof buf); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR)) + goto again; + + if (sscanf (buf, "add %d\n", &pid) == 1) + { + g_array_append_val (buses_to_kill_array, pid); + } + else if (sscanf (buf, "remove %d\n", &pid) == 1) + { + for (n = 0; n < buses_to_kill_array->len; n++) + { + if (g_array_index (buses_to_kill_array, guint, n) == pid) + { + g_array_remove_index (buses_to_kill_array, n); + pid = 0; + break; + } + } + if (pid != 0) + { + g_warning ("unknown pid %d to remove", pid); + } + } + else + { + g_warning ("unknown command from parent '%s'", buf); + } + } + while (TRUE); + +} + +static GHashTable *session_bus_address_to_pid = NULL; +static gint pipe_fds[2]; + +const gchar * +session_bus_up_with_address (const gchar *given_address) +{ + gchar *address; + int stdout_fd; + GError *error; + gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL}; + GPid pid; + gchar buf[512]; + ssize_t bytes_read; + gchar *config_file_name; + gint config_file_fd; + GString *config_file_contents; + + address = NULL; + error = NULL; + config_file_name = NULL; + config_file_fd = -1; + argv[2] = NULL; + + config_file_fd = g_file_open_tmp ("g-dbus-tests-XXXXXX", + &config_file_name, + &error); + if (config_file_fd < 0) + { + g_warning ("Error creating temporary config file: %s", error->message); + g_error_free (error); + goto out; + } + + config_file_contents = g_string_new (NULL); + g_string_append (config_file_contents, "\n"); + g_string_append (config_file_contents, " session\n"); + g_string_append_printf (config_file_contents, " %s\n", given_address); + g_string_append (config_file_contents, + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + g_string_append (config_file_contents, "\n"); + + if (write (config_file_fd, config_file_contents->str, config_file_contents->len) != (gssize) config_file_contents->len) + { + g_warning ("Error writing %d bytes to config file: %m", (gint) config_file_contents->len); + g_string_free (config_file_contents, TRUE); + goto out; + } + g_string_free (config_file_contents, TRUE); + + argv[2] = g_strdup_printf ("--config-file=%s", config_file_name); + + if (session_bus_address_to_pid == NULL) + { + /* keep a mapping from session bus address to the pid */ + session_bus_address_to_pid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + /* fork a child to clean up session buses when we are killed */ + if (pipe (pipe_fds) != 0) + { + g_warning ("pipe() failed: %m"); + g_assert_not_reached (); + } + switch (fork ()) + { + case -1: + g_warning ("fork() failed: %m"); + g_assert_not_reached (); + break; + + case 0: + /* child */ + close (pipe_fds[1]); + watch_parent (pipe_fds[0]); + break; + + default: + /* parent */ + close (pipe_fds[0]); + break; + } + + //atexit (cleanup_session_buses); + /* TODO: need to handle the cases where we crash */ + } + else + { + /* check if we already have a bus running for this address */ + if (g_hash_table_lookup (session_bus_address_to_pid, given_address) != NULL) + { + g_warning ("Already have a bus instance for the given address %s", given_address); + goto out; + } + } + + if (!g_spawn_async_with_pipes (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + &pid, + NULL, + &stdout_fd, + NULL, + &error)) + { + g_warning ("Error spawning dbus-daemon: %s", error->message); + g_error_free (error); + goto out; + } + + memset (buf, '\0', sizeof buf); + again: + bytes_read = read (stdout_fd, buf, sizeof buf); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR)) + goto again; + close (stdout_fd); + + if (bytes_read == 0 || bytes_read == sizeof buf) + { + g_warning ("Error reading address from dbus daemon, %d bytes read", (gint) bytes_read); + kill (SIGTERM, pid); + goto out; + } + + address = g_strdup (buf); + g_strstrip (address); + + /* write the pid to the child so it can kill it when we die */ + g_snprintf (buf, sizeof buf, "add %d\n", (guint) pid); + write (pipe_fds[1], buf, strlen (buf)); + + /* start dbus-monitor */ + if (g_getenv ("G_DBUS_MONITOR") != NULL) + { + g_spawn_command_line_async ("dbus-monitor --session", NULL); + usleep (500 * 1000); + } + + g_hash_table_insert (session_bus_address_to_pid, address, GUINT_TO_POINTER (pid)); + + out: + if (config_file_fd > 0) + { + if (close (config_file_fd) != 0) + { + g_warning ("Error closing fd for config file %s: %m", config_file_name); + } + g_assert (config_file_name != NULL); + if (unlink (config_file_name) != 0) + { + g_warning ("Error unlinking config file %s: %m", config_file_name); + } + } + g_free (argv[2]); + g_free (config_file_name); + return address; +} + +void +session_bus_down_with_address (const gchar *address) +{ + gpointer value; + GPid pid; + gchar buf[512]; + + g_assert (address != NULL); + g_assert (session_bus_address_to_pid != NULL); + + value = g_hash_table_lookup (session_bus_address_to_pid, address); + g_assert (value != NULL); + + pid = GPOINTER_TO_UINT (g_hash_table_lookup (session_bus_address_to_pid, address)); + + kill (pid, SIGTERM); + + /* write the pid to the child so it won't kill it when we die */ + g_snprintf (buf, sizeof buf, "remove %d\n", (guint) pid); + write (pipe_fds[1], buf, strlen (buf)); + + g_hash_table_remove (session_bus_address_to_pid, address); +} + +static gchar *temporary_address = NULL; +static gchar *temporary_address_used_by_bus = NULL; + +const gchar * +session_bus_get_temporary_address (void) +{ + if (temporary_address == NULL) + { + /* TODO: maybe use a more random name etc etc */ + temporary_address = g_strdup_printf ("unix:path=/tmp/g-dbus-tests-pid-%d", getpid ()); + } + + return temporary_address; +} + +const gchar * +session_bus_up (void) +{ + if (temporary_address_used_by_bus != NULL) + { + g_warning ("There is already a session bus up"); + goto out; + } + + temporary_address_used_by_bus = g_strdup (session_bus_up_with_address (session_bus_get_temporary_address ())); + + out: + return temporary_address_used_by_bus; +} + +void +session_bus_down (void) +{ + if (temporary_address_used_by_bus == NULL) + { + g_warning ("There is not a session bus up"); + } + else + { + session_bus_down_with_address (temporary_address_used_by_bus); + g_free (temporary_address_used_by_bus); + temporary_address_used_by_bus = NULL; + } +} diff --git a/gio/tests/gdbus-sessionbus.h b/gio/tests/gdbus-sessionbus.h new file mode 100644 index 000000000..e107e0664 --- /dev/null +++ b/gio/tests/gdbus-sessionbus.h @@ -0,0 +1,38 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 __SESSION_BUS_H__ +#define __SESSION_BUS_H__ + +#include + +G_BEGIN_DECLS + +const gchar *session_bus_up_with_address (const gchar *given_address); +void session_bus_down_with_address (const gchar *address); +const gchar *session_bus_get_temporary_address (void); +const gchar *session_bus_up (void); +void session_bus_down (void); + +G_END_DECLS + +#endif /* __SESSION_BUS_H__ */ diff --git a/gio/tests/gdbus-tests.c b/gio/tests/gdbus-tests.c new file mode 100644 index 000000000..b8ac8490d --- /dev/null +++ b/gio/tests/gdbus-tests.c @@ -0,0 +1,218 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include + +#include "gdbus-tests.h" + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean timed_out; +} PropertyNotifyData; + +static void +on_property_notify (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + PropertyNotifyData *data = user_data; + g_main_loop_quit (data->loop); +} + +static gboolean +on_property_notify_timeout (gpointer user_data) +{ + PropertyNotifyData *data = user_data; + data->timed_out = TRUE; + g_main_loop_quit (data->loop); + return TRUE; +} + +gboolean +_g_assert_property_notify_run (gpointer object, + const gchar *property_name) +{ + gchar *s; + gulong handler_id; + guint timeout_id; + PropertyNotifyData data; + + data.loop = g_main_loop_new (NULL, FALSE); + data.timed_out = FALSE; + s = g_strdup_printf ("notify::%s", property_name); + handler_id = g_signal_connect (object, + s, + G_CALLBACK (on_property_notify), + &data); + g_free (s); + timeout_id = g_timeout_add (5 * 1000, + on_property_notify_timeout, + &data); + g_main_loop_run (data.loop); + g_signal_handler_disconnect (object, handler_id); + g_source_remove (timeout_id); + g_main_loop_unref (data.loop); + + return data.timed_out; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean timed_out; +} SignalReceivedData; + +static void +on_signal_received (gpointer user_data) +{ + SignalReceivedData *data = user_data; + g_main_loop_quit (data->loop); +} + +static gboolean +on_signal_received_timeout (gpointer user_data) +{ + SignalReceivedData *data = user_data; + data->timed_out = TRUE; + g_main_loop_quit (data->loop); + return TRUE; +} + +gboolean +_g_assert_signal_received_run (gpointer object, + const gchar *signal_name) +{ + gulong handler_id; + guint timeout_id; + SignalReceivedData data; + + data.loop = g_main_loop_new (NULL, FALSE); + data.timed_out = FALSE; + handler_id = g_signal_connect_swapped (object, + signal_name, + G_CALLBACK (on_signal_received), + &data); + timeout_id = g_timeout_add (5 * 1000, + on_signal_received_timeout, + &data); + g_main_loop_run (data.loop); + g_signal_handler_disconnect (object, handler_id); + g_source_remove (timeout_id); + g_main_loop_unref (data.loop); + + return data.timed_out; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +GDBusConnection * +_g_bus_get_priv (GBusType bus_type, + GCancellable *cancellable, + GError **error) +{ + gchar *address; + GDBusConnection *ret; + + ret = NULL; + + address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error); + if (address == NULL) + goto out; + + ret = g_dbus_connection_new_for_address_sync (address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + cancellable, + error); + g_free (address); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean timed_out; +} WaitSingleRefData; + +static gboolean +on_wait_single_ref_timeout (gpointer user_data) +{ + WaitSingleRefData *data = user_data; + data->timed_out = TRUE; + g_main_loop_quit (data->loop); + return TRUE; +} + +static void +on_wait_for_single_ref_toggled (gpointer user_data, + GObject *object, + gboolean is_last_ref) +{ + WaitSingleRefData *data = user_data; + g_main_loop_quit (data->loop); +} + +gboolean +_g_object_wait_for_single_ref_do (gpointer object) +{ + WaitSingleRefData data; + guint timeout_id; + + data.timed_out = FALSE; + + if (G_OBJECT (object)->ref_count == 1) + goto out; + + data.loop = g_main_loop_new (NULL, FALSE); + timeout_id = g_timeout_add (5 * 1000, + on_wait_single_ref_timeout, + &data); + + g_object_add_toggle_ref (G_OBJECT (object), + on_wait_for_single_ref_toggled, + &data); + g_object_unref (object); + + g_main_loop_run (data.loop); + + g_object_ref (object); + g_object_remove_toggle_ref (object, + on_wait_for_single_ref_toggled, + &data); + + g_source_remove (timeout_id); + g_main_loop_unref (data.loop); + out: + return data.timed_out; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/tests/gdbus-tests.h b/gio/tests/gdbus-tests.h new file mode 100644 index 000000000..ffcc57cfb --- /dev/null +++ b/gio/tests/gdbus-tests.h @@ -0,0 +1,146 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 __TESTS_H__ +#define __TESTS_H__ + +#include +#include "gdbus-sessionbus.h" + +G_BEGIN_DECLS + +/* TODO: clean up and move to gtestutils.c + * + * This is needed because libdbus-1 does not give predictable error messages - e.g. you + * get a different error message on connecting to a bus if the socket file is there vs + * if the socket file is missing. + */ + +#define _g_assert_error_domain(err, dom) do { if (!err || (err)->domain != dom) \ + g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #err, err, dom, -1); } while (0) + +#define _g_assert_property_notify(object, property_name) \ + do \ + { \ + if (!G_IS_OBJECT (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Not a GObject instance"); \ + } \ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), \ + property_name) == NULL) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Property " property_name " does not " \ + "exist on object"); \ + } \ + if (_g_assert_property_notify_run (object, property_name)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Timed out waiting for notification " \ + "on property " property_name); \ + } \ + } \ + while (FALSE) + +#define _g_assert_signal_received(object, signal_name) \ + do \ + { \ + if (!G_IS_OBJECT (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Not a GObject instance"); \ + } \ + if (g_signal_lookup (signal_name, \ + G_TYPE_FROM_INSTANCE (object)) == 0) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Signal `" signal_name "' does not " \ + "exist on object"); \ + } \ + if (_g_assert_signal_received_run (object, signal_name)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Timed out waiting for signal `" \ + signal_name "'"); \ + } \ + } \ + while (FALSE) + +gboolean _g_assert_property_notify_run (gpointer object, + const gchar *property_name); + + +gboolean _g_assert_signal_received_run (gpointer object, + const gchar *signal_name); + +GDBusConnection *_g_bus_get_priv (GBusType bus_type, + GCancellable *cancellable, + GError **error); + + +#define _g_object_wait_for_single_ref(object) \ + do \ + { \ + if (!G_IS_OBJECT (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Not a GObject instance"); \ + } \ + if (_g_object_wait_for_single_ref_do (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Timed out waiting for single ref"); \ + } \ + } \ + while (FALSE) + +gboolean _g_object_wait_for_single_ref_do (gpointer object); + +G_END_DECLS + +#endif /* __TESTS_H__ */ diff --git a/gio/tests/gdbus-testserver.py b/gio/tests/gdbus-testserver.py new file mode 100755 index 000000000..04d654e71 --- /dev/null +++ b/gio/tests/gdbus-testserver.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python + +import gobject +import time + +import dbus +import dbus.service +import dbus.mainloop.glib + +class TestException(dbus.DBusException): + _dbus_error_name = 'com.example.TestException' + + +class TestService(dbus.service.Object): + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='s', out_signature='s') + def HelloWorld(self, hello_message): + if str(hello_message) == 'Yo': + raise TestException('Yo is not a proper greeting') + else: + return "You greeted me with '%s'. Thanks!"%(str(hello_message)) + + @dbus.service.method("com.example.Frob", + in_signature='ss', out_signature='ss') + def DoubleHelloWorld(self, hello1, hello2): + return ("You greeted me with '%s'. Thanks!"%(str(hello1)), "Yo dawg, you uttered '%s'. Thanks!"%(str(hello2))) + + @dbus.service.method("com.example.Frob", + in_signature='', out_signature='su') + def PairReturn(self): + return ("foo", 42) + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='ybnqiuxtdsog', out_signature='ybnqiuxtdsog') + def TestPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double, val_string, val_objpath, val_signature): + return val_byte + 1, not val_boolean, val_int16 + 1, val_uint16 + 1, val_int32 + 1, val_uint32 + 1, val_int64 + 1, val_uint64 + 1, -val_double + 0.123, val_string * 2, val_objpath + "/modified", val_signature * 2 + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='ayabanaqaiauaxatad', out_signature='ayabanaqaiauaxatad') + def TestArrayOfPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double): + return val_byte*2, val_boolean*2, val_int16*2, val_uint16*2, val_int32*2, val_uint32*2, val_int64*2, val_uint64*2, val_double*2 + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='asaoag', out_signature='asaoag') + def TestArrayOfStringTypes(self, val_string, val_objpath, val_signature): + return val_string * 2, val_objpath * 2, val_signature * 2 + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature = 'a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}', + out_signature = 'a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}') + def TestHashTables(self, hyy, hbb, hnn, hqq, hii, huu, hxx, htt, hdd, hss, hoo, hgg): + + ret_hyy = {} + for i in hyy: + ret_hyy[i*2] = (hyy[i]*3) & 255 + + ret_hbb = {} + for i in hbb: + ret_hbb[i] = True + + ret_hnn = {} + for i in hnn: + ret_hnn[i*2] = hnn[i]*3 + + ret_hqq = {} + for i in hqq: + ret_hqq[i*2] = hqq[i]*3 + + ret_hii = {} + for i in hii: + ret_hii[i*2] = hii[i]*3 + + ret_huu = {} + for i in huu: + ret_huu[i*2] = huu[i]*3 + + ret_hxx = {} + for i in hxx: + ret_hxx[i + 2] = hxx[i] + 1 + + ret_htt = {} + for i in htt: + ret_htt[i + 2] = htt[i] + 1 + + ret_hdd = {} + for i in hdd: + ret_hdd[i + 2.5] = hdd[i] + 5.0 + + ret_hss = {} + for i in hss: + ret_hss[i + "mod"] = hss[i]*2 + + ret_hoo = {} + for i in hoo: + ret_hoo[i + "/mod"] = hoo[i] + "/mod2" + + ret_hgg = {} + for i in hgg: + ret_hgg[i + "assgit"] = hgg[i]*2 + + return ret_hyy, ret_hbb, ret_hnn, ret_hqq, ret_hii, ret_huu, ret_hxx, ret_htt, ret_hdd, ret_hss, ret_hoo, ret_hgg + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='(ii)(s(ii)aya{ss})', out_signature='(ii)(s(ii)aya{ss})') + def TestStructureTypes(self, s1, s2): + (x, y) = s1; + (desc, (x1, y1), ay, hss) = s2; + ret_hss = {} + for i in hss: + ret_hss[i] = hss[i] + " ... in bed!" + return (x + 1, y + 1), (desc + " ... in bed!", (x1 + 2, y1 + 2), ay * 2, ret_hss) + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='vb', out_signature='v') + def TestVariant(self, v, modify): + + if modify: + if type(v)==dbus.Boolean: + ret = False + elif type(v)==dbus.Dictionary: + ret = {} + for i in v: + ret[i] = v[i] * 2 + elif type(v)==dbus.Struct: + ret = ["other struct", dbus.Int16(100)] + else: + ret = v * 2 + else: + ret = v + return (type(v))(ret) + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='a(ii)aa(ii)aasaa{ss}aayavaav', out_signature='a(ii)aa(ii)aasaa{ss}aayavaav') + def TestComplexArrays(self, aii, aaii, aas, ahashes, aay, av, aav): + return aii * 2, aaii * 2, aas * 2, ahashes * 2, aay * 2, av *2, aav * 2 + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}', + out_signature='a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}') + def TestComplexHashTables(self, h_str_to_pair, h_str_to_variant, h_str_to_av, h_str_to_aav, + h_str_to_array_of_pairs, hash_of_hashes): + + ret_h_str_to_pair = {} + for i in h_str_to_pair: + ret_h_str_to_pair[i + "_baz"] = h_str_to_pair[i] + + ret_h_str_to_variant = {} + for i in h_str_to_variant: + ret_h_str_to_variant[i + "_baz"] = h_str_to_variant[i] + + return ret_h_str_to_pair, ret_h_str_to_variant, h_str_to_av, h_str_to_aav, h_str_to_array_of_pairs, hash_of_hashes + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='', out_signature='') + def Quit(self): + mainloop.quit() + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", + in_signature='sv', out_signature='') + def FrobSetProperty(self, prop_name, prop_value): + self.frob_props[prop_name] = prop_value + message = dbus.lowlevel.SignalMessage("/com/example/TestObject", + "org.freedesktop.DBus.Properties", + "PropertiesChanged") + message.append("com.example.Frob") + message.append({prop_name : prop_value}) + session_bus.send_message(message) + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.signal("com.example.Frob", + signature="sov") + def TestSignal(self, str1, objpath1, variant1): + pass + + @dbus.service.method("com.example.Frob", + in_signature='so', out_signature='') + def EmitSignal(self, str1, objpath1): + self.TestSignal (str1 + " .. in bed!", objpath1 + "/in/bed", "a variant") + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("com.example.Frob", in_signature='i', out_signature='', + async_callbacks=('return_cb', 'raise_cb')) + def Sleep(self, msec, return_cb, raise_cb): + def return_from_async_wait(): + return_cb() + return False + gobject.timeout_add(msec, return_from_async_wait) + + # ---------------------------------------------------------------------------------------------------- + + @dbus.service.method("org.freedesktop.DBus.Properties", + in_signature = 'ss', + out_signature = 'v') + def Get(self, interface_name, property_name): + + if interface_name == "com.example.Frob": + return self.frob_props[property_name] + else: + raise TestException("No such interface " + interface_name) + + @dbus.service.method("org.freedesktop.DBus.Properties", + in_signature = 's', + out_signature = 'a{sv}') + def GetAll(self, interface_name): + if interface_name == "com.example.Frob": + return self.frob_props + else: + raise TestException("No such interface " + interface_name) + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + session_bus = dbus.SessionBus() + name = dbus.service.BusName("com.example.TestService", session_bus) + + obj = TestService(session_bus, '/com/example/TestObject') + + #print "Our unique name is %s"%(session_bus.get_unique_name()) + + obj.frob_props = {} + obj.frob_props["y"] = dbus.Byte(1) + obj.frob_props["b"] = dbus.Boolean(True) + obj.frob_props["n"] = dbus.Int16(2) + obj.frob_props["q"] = dbus.UInt16(3) + obj.frob_props["i"] = dbus.Int32(4) + obj.frob_props["u"] = dbus.UInt32(5) + obj.frob_props["x"] = dbus.Int64(6) + obj.frob_props["t"] = dbus.UInt64(7) + obj.frob_props["d"] = dbus.Double(7.5) + obj.frob_props["s"] = dbus.String("a string") + obj.frob_props["o"] = dbus.ObjectPath("/some/path") + obj.frob_props["ay"] = [dbus.Byte(1), dbus.Byte(11)] + obj.frob_props["ab"] = [dbus.Boolean(True), dbus.Boolean(False)] + obj.frob_props["an"] = [dbus.Int16(2), dbus.Int16(12)] + obj.frob_props["aq"] = [dbus.UInt16(3), dbus.UInt16(13)] + obj.frob_props["ai"] = [dbus.Int32(4), dbus.Int32(14)] + obj.frob_props["au"] = [dbus.UInt32(5), dbus.UInt32(15)] + obj.frob_props["ax"] = [dbus.Int64(6), dbus.Int64(16)] + obj.frob_props["at"] = [dbus.UInt64(7), dbus.UInt64(17)] + obj.frob_props["ad"] = [dbus.Double(7.5), dbus.Double(17.5)] + obj.frob_props["as"] = [dbus.String("a string"), dbus.String("another string")] + obj.frob_props["ao"] = [dbus.ObjectPath("/some/path"), dbus.ObjectPath("/another/path")] + obj.frob_props["foo"] = "a frobbed string" + + mainloop = gobject.MainLoop() + mainloop.run() diff --git a/gio/tests/gdbus-threading.c b/gio/tests/gdbus-threading.c new file mode 100644 index 000000000..0747907df --- /dev/null +++ b/gio/tests/gdbus-threading.c @@ -0,0 +1,532 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 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 +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a global connection */ +static GDBusConnection *c = NULL; + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Ensure that signal and method replies are delivered in the right thread */ +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct { + GThread *thread; + GMainLoop *thread_loop; + guint signal_count; +} DeliveryData; + +static void +msg_cb_expect_success (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + DeliveryData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + + g_assert (g_thread_self () == data->thread); + + g_main_loop_quit (data->thread_loop); +} + +static void +msg_cb_expect_error_cancelled (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + DeliveryData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_assert (g_thread_self () == data->thread); + + g_main_loop_quit (data->thread_loop); +} + +static void +signal_handler (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + DeliveryData *data = user_data; + + g_assert (g_thread_self () == data->thread); + + data->signal_count++; + + g_main_loop_quit (data->thread_loop); +} + +static gpointer +test_delivery_in_thread_func (gpointer _data) +{ + GMainLoop *thread_loop; + GMainContext *thread_context; + DeliveryData data; + GCancellable *ca; + guint subscription_id; + GDBusConnection *priv_c; + GError *error; + + error = NULL; + + thread_context = g_main_context_new (); + thread_loop = g_main_loop_new (thread_context, FALSE); + g_main_context_push_thread_default (thread_context); + + data.thread = g_thread_self (); + data.thread_loop = thread_loop; + data.signal_count = 0; + + /* ---------------------------------------------------------------------------------------------------- */ + + /* + * Check that we get a reply to the GetId() method call. + */ + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_success, + &data); + g_main_loop_run (thread_loop); + + /* + * Check that we never actually send a message if the GCancellable + * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED + * when the actual connection is not up. + */ + ca = g_cancellable_new (); + g_cancellable_cancel (ca); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + &data); + g_main_loop_run (thread_loop); + g_object_unref (ca); + + /* + * Check that cancellation works when the message is already in flight. + */ + ca = g_cancellable_new (); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + &data); + g_cancellable_cancel (ca); + g_main_loop_run (thread_loop); + g_object_unref (ca); + + /* + * Check that signals are delivered to the correct thread. + * + * First we subscribe to the signal, then we create a a private + * connection. This should cause a NameOwnerChanged message from + * the message bus. + */ + subscription_id = g_dbus_connection_signal_subscribe (c, + "org.freedesktop.DBus", /* sender */ + "org.freedesktop.DBus", /* interface */ + "NameOwnerChanged", /* member */ + "/org/freedesktop/DBus", /* path */ + NULL, + signal_handler, + &data, + NULL); + g_assert (subscription_id != 0); + g_assert (data.signal_count == 0); + + priv_c = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (priv_c != NULL); + + g_main_loop_run (thread_loop); + g_assert (data.signal_count == 1); + + g_object_unref (priv_c); + + g_dbus_connection_signal_unsubscribe (c, subscription_id); + + /* ---------------------------------------------------------------------------------------------------- */ + + g_main_context_pop_thread_default (thread_context); + g_main_loop_unref (thread_loop); + g_main_context_unref (thread_context); + + g_main_loop_quit (loop); + + return NULL; +} + +static void +test_delivery_in_thread (void) +{ + GError *error; + GThread *thread; + + error = NULL; + thread = g_thread_create (test_delivery_in_thread_func, + NULL, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread != NULL); + + /* run the event loop - it is needed to dispatch D-Bus messages */ + g_main_loop_run (loop); + + g_thread_join (thread); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct { + GDBusProxy *proxy; + gint msec; + guint num; + gboolean async; + + GMainLoop *thread_loop; + GThread *thread; + + gboolean done; +} SyncThreadData; + +static void +sleep_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + SyncThreadData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + + g_assert (data->thread == g_thread_self ()); + + g_main_loop_quit (data->thread_loop); + + //g_debug ("async cb (%p)", g_thread_self ()); +} + +static gpointer +test_sleep_in_thread_func (gpointer _data) +{ + SyncThreadData *data = _data; + GMainContext *thread_context; + guint n; + + thread_context = g_main_context_new (); + data->thread_loop = g_main_loop_new (thread_context, FALSE); + g_main_context_push_thread_default (thread_context); + + data->thread = g_thread_self (); + + for (n = 0; n < data->num; n++) + { + if (data->async) + { + //g_debug ("invoking async (%p)", g_thread_self ()); + g_dbus_proxy_invoke_method (data->proxy, + "Sleep", + g_variant_new ("(i)", data->msec), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) sleep_cb, + data); + g_main_loop_run (data->thread_loop); + g_print ("A"); + //g_debug ("done invoking async (%p)", g_thread_self ()); + } + else + { + GError *error; + GVariant *result; + + error = NULL; + //g_debug ("invoking sync (%p)", g_thread_self ()); + result = g_dbus_proxy_invoke_method_sync (data->proxy, + "Sleep", + g_variant_new ("(i)", data->msec), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_print ("S"); + //g_debug ("done invoking sync (%p)", g_thread_self ()); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + } + } + + g_main_context_pop_thread_default (thread_context); + g_main_loop_unref (data->thread_loop); + g_main_context_unref (thread_context); + + data->done = TRUE; + g_main_loop_quit (loop); + + return NULL; +} + +static void +on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + guint n; + + /* + * Check that multiple threads can do calls without interferring with + * each other. We do this by creating three threads that call the + * Sleep() method on the server (which handles it asynchronously, e.g. + * it won't block other requests) with different sleep durations and + * a number of times. We do this so each set of calls add up to 4000 + * milliseconds. + * + * We run this test twice - first with async calls in each thread, then + * again with sync calls + */ + + for (n = 0; n < 2; n++) + { + gboolean do_async; + GThread *thread1; + GThread *thread2; + GThread *thread3; + SyncThreadData data1; + SyncThreadData data2; + SyncThreadData data3; + GError *error; + GTimeVal start_time; + GTimeVal end_time; + guint elapsed_msec; + + error = NULL; + do_async = (n == 0); + + g_get_current_time (&start_time); + + data1.proxy = proxy; + data1.msec = 40; + data1.num = 100; + data1.async = do_async; + data1.done = FALSE; + thread1 = g_thread_create (test_sleep_in_thread_func, + &data1, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread1 != NULL); + + data2.proxy = proxy; + data2.msec = 20; + data2.num = 200; + data2.async = do_async; + data2.done = FALSE; + thread2 = g_thread_create (test_sleep_in_thread_func, + &data2, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread2 != NULL); + + data3.proxy = proxy; + data3.msec = 100; + data3.num = 40; + data3.async = do_async; + data3.done = FALSE; + thread3 = g_thread_create (test_sleep_in_thread_func, + &data3, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread3 != NULL); + + /* we handle messages in the main loop - threads will quit it when they are done */ + while (!(data1.done && data2.done && data3.done)) + g_main_loop_run (loop); + + g_thread_join (thread1); + g_thread_join (thread2); + g_thread_join (thread3); + + g_get_current_time (&end_time); + + elapsed_msec = ((end_time.tv_sec * G_USEC_PER_SEC + end_time.tv_usec) - + (start_time.tv_sec * G_USEC_PER_SEC + start_time.tv_usec)) / 1000; + + //g_debug ("Elapsed time for %s = %d msec", n == 0 ? "async" : "sync", elapsed_msec); + + /* elapsed_msec should be 4000 msec + change for overhead */ + g_assert_cmpint (elapsed_msec, >=, 4000); + g_assert_cmpint (elapsed_msec, <, 5000); + + g_print (" "); + } + + g_main_loop_quit (loop); +} + +static void +on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +test_method_calls_in_thread (void) +{ + guint watcher_id; + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + "/com/example/TestObject", + "com.example.Frob", + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + on_proxy_appeared, + on_proxy_vanished, + NULL, + NULL); + + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + GError *error; + gint ret; + + g_type_init (); + g_thread_init (NULL); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async ("./gdbus-testserver.py", NULL)); + + /* wait for the service to come up */ + usleep (500 * 1000); + + /* Create the connection in the main thread */ + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + g_test_add_func ("/gdbus/delivery-in-thread", test_delivery_in_thread); + g_test_add_func ("/gdbus/method-calls-in-thread", test_method_calls_in_thread); + + ret = g_test_run(); + + g_object_unref (c); + + /* tear down bus */ + session_bus_down (); + + return ret; +} From c490c14f4e3fbbe8c74b26e6cacac31b0e744c92 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 15:31:45 -0400 Subject: [PATCH 02/76] Set up gtk-doc for GDBus Also move send_credentials() and receive_credentials() to GUnixConnection. This code might change, discussion is still ongoing in https://bugzilla.gnome.org/show_bug.cgi?id=617483. --- docs/reference/gio/gio-docs.xml | 21 ++ docs/reference/gio/gio-sections.txt | 390 ++++++++++++++++++++++++++++ docs/reference/gio/gio.types | 11 + gio/gcredentials.c | 4 +- gio/gdbusaddress.c | 2 +- gio/gdbusauth.c | 189 -------------- gio/gdbusauthobserver.c | 2 +- gio/gdbusconnection.c | 8 +- gio/gdbuserror.c | 2 +- gio/gdbusintrospection.c | 2 +- gio/gdbusmessage.c | 2 +- gio/gdbusmethodinvocation.c | 2 +- gio/gdbusnameowning.c | 4 +- gio/gdbusnamewatching.c | 4 +- gio/gdbusproxy.c | 2 +- gio/gdbusproxywatching.c | 4 +- gio/gdbusserver.c | 4 +- gio/gdbusutils.c | 2 +- gio/gunixconnection.c | 258 ++++++++++++++++++ gio/gunixconnection.h | 10 + 20 files changed, 712 insertions(+), 211 deletions(-) diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 8ea9f3874..305dc7a07 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -107,6 +107,8 @@ + + DNS resolution @@ -124,6 +126,25 @@ + + Lowlevel D-Bus Support + + + + + + + + + + + + + Highlevel D-Bus Support + + + + Utilities diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index fd2c33828..06d81999a 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1807,6 +1807,8 @@ g_tcp_connection_get_graceful_disconnect GUnixConnection g_unix_connection_receive_fd g_unix_connection_send_fd +g_unix_connection_receive_credentials +g_unix_connection_send_credentials g_socket_connection_factory_create_connection g_socket_connection_factory_lookup_type @@ -2167,3 +2169,391 @@ G_SETTINGS_SCHEMA_GET_CLASS g_settings_get_type + +
+gunixcredentialsmessage +GUnixCredentialsMessage +GUnixCredentialsMessage +GUnixCredentialsMessageClass +g_unix_credentials_message_new +g_unix_credentials_message_new_with_credentials +g_unix_credentials_message_get_credentials +g_unix_credentials_message_is_supported + +G_IS_UNIX_CREDENTIALS_MESSAGE +G_IS_UNIX_CREDENTIALS_MESSAGE_CLASS +G_TYPE_UNIX_CREDENTIALS_MESSAGE +G_UNIX_CREDENTIALS_MESSAGE +G_UNIX_CREDENTIALS_MESSAGE_CLASS +G_UNIX_CREDENTIALS_MESSAGE_GET_CLASS + +GUnixCredentialsMessagePrivate +g_unix_credentials_message_get_type +
+ +
+gcredentials +GCredentials +GCredentials +GCredentialsClass +GCredentialType +g_credentials_new +g_credentials_new_for_process +g_credentials_new_for_string +g_credentials_to_string +g_credentials_has_unix_user +g_credentials_get_unix_user +g_credentials_set_unix_user +g_credentials_has_unix_group +g_credentials_get_unix_group +g_credentials_set_unix_group +g_credentials_has_unix_process +g_credentials_get_unix_process +g_credentials_set_unix_process +g_credentials_has_windows_user +g_credentials_get_windows_user +g_credentials_set_windows_user + +G_CREDENTIALS +G_IS_CREDENTIALS +G_TYPE_CREDENTIALS +g_credentials_get_type +G_CREDENTIALS_CLASS +G_IS_CREDENTIALS_CLASS +G_CREDENTIALS_GET_CLASS +
+ +
+gdbusaddress +g_dbus_is_address +g_dbus_is_supported_address +g_dbus_address_get_stream +g_dbus_address_get_stream_finish +g_dbus_address_get_stream_sync +g_dbus_address_get_for_bus_sync +
+ +
+gdbusutils +g_dbus_is_activated +g_dbus_generate_guid +g_dbus_is_guid +g_dbus_is_name +g_dbus_is_unique_name +g_dbus_is_member_name +g_dbus_is_interface_name +
+ +
+gdbusauthobserver +GDBusAuthObserver +GDBusAuthObserver +GDBusAuthObserverClass +g_dbus_auth_observer_new +g_dbus_auth_observer_deny_authenticated_peer + +G_DBUS_AUTH_OBSERVER +G_IS_DBUS_AUTH_OBSERVER +G_TYPE_DBUS_AUTH_OBSERVER +g_dbus_server_get_gtype +G_DBUS_AUTH_OBSERVER_CLASS +G_IS_DBUS_AUTH_OBSERVER_CLASS +G_DBUS_AUTH_OBSERVER_GET_CLASS +
+ +
+gdbusserver +GDBusServer +GDBusServer +GDBusServerClass +GDBusServerFlags +g_dbus_server_new_sync +g_dbus_server_start +g_dbus_server_stop +g_dbus_server_is_active +g_dbus_server_get_guid +g_dbus_server_get_flags +g_dbus_server_get_client_address + +G_DBUS_SERVER +G_IS_DBUS_SERVER +G_TYPE_DBUS_SERVER +g_dbus_server_get_gtype +G_DBUS_SERVER_CLASS +G_IS_DBUS_SERVER_CLASS +G_DBUS_SERVER_GET_CLASS +
+ +
+gdbusmessage +GDBusMessage +GDBusMessageType +GDBusMessageFlags +GDBusMessageHeaderField +GDBusMessage +GDBusMessageClass +g_dbus_message_new +g_dbus_message_new_signal +g_dbus_message_new_method_call +g_dbus_message_new_method_reply +g_dbus_message_new_method_error +g_dbus_message_new_method_error_valist +g_dbus_message_new_method_error_literal +g_dbus_message_print +g_dbus_message_get_type +g_dbus_message_set_type +g_dbus_message_get_serial +g_dbus_message_set_serial +g_dbus_message_get_flags +g_dbus_message_set_flags +g_dbus_message_get_body +g_dbus_message_set_body +g_dbus_message_get_unix_fd_list +g_dbus_message_set_unix_fd_list +g_dbus_message_get_header_fields +g_dbus_message_get_header +g_dbus_message_set_header +g_dbus_message_get_destination +g_dbus_message_set_destination +g_dbus_message_get_error_name +g_dbus_message_set_error_name +g_dbus_message_get_interface +g_dbus_message_set_interface +g_dbus_message_get_member +g_dbus_message_set_member +g_dbus_message_get_path +g_dbus_message_set_path +g_dbus_message_get_reply_serial +g_dbus_message_set_reply_serial +g_dbus_message_get_sender +g_dbus_message_set_sender +g_dbus_message_get_signature +g_dbus_message_set_signature +g_dbus_message_get_arg0 +g_dbus_message_to_blob +g_dbus_message_bytes_needed +g_dbus_message_new_from_blob +g_dbus_message_to_gerror + +G_DBUS_MESSAGE +G_IS_DBUS_MESSAGE +G_TYPE_DBUS_MESSAGE +g_dbus_message_get_gtype +G_DBUS_MESSAGE_CLASS +G_IS_DBUS_MESSAGE_CLASS +G_DBUS_MESSAGE_GET_CLASS +
+ +
+gdbusconnection +GDBusConnection +GBusType +g_bus_get +g_bus_get_finish +g_bus_get_sync +GDBusConnection +GDBusConnectionClass +GDBusConnectionFlags +g_dbus_connection_new +g_dbus_connection_new_finish +g_dbus_connection_new_sync +g_dbus_connection_new_for_address +g_dbus_connection_new_for_address_finish +g_dbus_connection_new_for_address_sync +GDBusCapabilityFlags +g_dbus_connection_close +g_dbus_connection_is_closed +g_dbus_connection_get_exit_on_close +g_dbus_connection_set_exit_on_close +g_dbus_connection_get_stream +g_dbus_connection_get_guid +g_dbus_connection_get_unique_name +g_dbus_connection_get_capabilities +g_dbus_connection_get_peer_credentials +GDBusInvokeMethodFlags +g_dbus_connection_invoke_method +g_dbus_connection_invoke_method_finish +g_dbus_connection_invoke_method_sync +g_dbus_connection_emit_signal +GDBusSignalCallback +g_dbus_connection_signal_subscribe +g_dbus_connection_signal_unsubscribe +g_dbus_connection_send_message +g_dbus_connection_send_message_with_reply +g_dbus_connection_send_message_with_reply_finish +g_dbus_connection_send_message_with_reply_sync +GDBusMessageFilterFunction +g_dbus_connection_add_filter +g_dbus_connection_remove_filter +GDBusInterfaceVTable +GDBusInterfaceMethodCallFunc +GDBusInterfaceGetPropertyFunc +GDBusInterfaceSetPropertyFunc +g_dbus_connection_register_object +g_dbus_connection_unregister_object +GDBusSubtreeVTable +GDBusSubtreeEnumerateFunc +GDBusSubtreeIntrospectFunc +GDBusSubtreeDispatchFunc +GDBusSubtreeFlags +g_dbus_connection_register_subtree +g_dbus_connection_unregister_subtree + +G_DBUS_CONNECTION +G_IS_DBUS_CONNECTION +G_TYPE_DBUS_CONNECTION +g_dbus_connection_get_type +G_DBUS_CONNECTION_CLASS +G_IS_DBUS_CONNECTION_CLASS +G_DBUS_CONNECTION_GET_CLASS +
+ +
+gdbusmethodinvocation +GDBusMethodInvocation +GDBusMethodInvocation +GDBusMethodInvocationClass +g_dbus_method_invocation_new +g_dbus_method_invocation_get_sender +g_dbus_method_invocation_get_object_path +g_dbus_method_invocation_get_interface_name +g_dbus_method_invocation_get_method_name +g_dbus_method_invocation_get_method_info +g_dbus_method_invocation_get_connection +g_dbus_method_invocation_get_message +g_dbus_method_invocation_get_parameters +g_dbus_method_invocation_get_user_data +g_dbus_method_invocation_return_value +g_dbus_method_invocation_return_error +g_dbus_method_invocation_return_error_valist +g_dbus_method_invocation_return_error_literal +g_dbus_method_invocation_return_gerror +g_dbus_method_invocation_return_dbus_error + +G_DBUS_METHOD_INVOCATION +G_IS_DBUS_METHOD_INVOCATION +G_TYPE_DBUS_METHOD_INVOCATION +g_dbus_method_invocation_get_type +G_DBUS_METHOD_INVOCATION_CLASS +G_IS_DBUS_METHOD_INVOCATION_CLASS +G_DBUS_METHOD_INVOCATION_GET_CLASS +
+ +
+gdbusnameowning +GBusAcquiredCallback +GBusNameAcquiredCallback +GBusNameLostCallback +GBusNameOwnerFlags +g_bus_own_name +g_bus_own_name_on_connection +g_bus_unown_name +
+ +
+gdbusnamewatching +GBusNameAppearedCallback +GBusNameVanishedCallback +GBusNameWatcherFlags +g_bus_watch_name +g_bus_unwatch_name +
+ +
+gdbusproxywatching +GBusProxyAppearedCallback +GBusProxyVanishedCallback +g_bus_watch_proxy +g_bus_unwatch_proxy +
+ +
+gdbuserror +GDBusError +G_DBUS_ERROR +g_dbus_error_is_remote_error +g_dbus_error_get_remote_error +g_dbus_error_strip_remote_error +GDBusErrorEntry +g_dbus_error_register_error_domain +g_dbus_error_register_error +g_dbus_error_unregister_error +g_dbus_error_new_for_dbus_error +g_dbus_error_set_dbus_error +g_dbus_error_set_dbus_error_valist +g_dbus_error_encode_gerror +
+ +
+gdbusproxy +GDBusProxy +GDBusProxyFlags +GDBusProxy +GDBusProxyClass +g_dbus_proxy_new +g_dbus_proxy_new_finish +g_dbus_proxy_new_sync +g_dbus_proxy_get_flags +g_dbus_proxy_get_connection +g_dbus_proxy_get_unique_bus_name +g_dbus_proxy_get_object_path +g_dbus_proxy_get_interface_name +g_dbus_proxy_get_default_timeout +g_dbus_proxy_set_default_timeout +g_dbus_proxy_get_cached_property_names +g_dbus_proxy_get_cached_property +g_dbus_proxy_set_interface_info +g_dbus_proxy_get_interface_info +g_dbus_proxy_invoke_method +g_dbus_proxy_invoke_method_finish +g_dbus_proxy_invoke_method_sync + +G_DBUS_PROXY +G_IS_DBUS_PROXY +G_TYPE_DBUS_PROXY +g_dbus_proxy_get_type +G_DBUS_PROXY_CLASS +G_IS_DBUS_PROXY_CLASS +G_DBUS_PROXY_GET_CLASS +
+ +
+gdbusintrospection +GDBusAnnotationInfo +GDBusArgInfo +GDBusMethodInfo +GDBusSignalInfo +GDBusPropertyInfoFlags +GDBusPropertyInfo +GDBusInterfaceInfo +GDBusNodeInfo +g_dbus_annotation_info_lookup +g_dbus_interface_info_lookup_method +g_dbus_interface_info_lookup_signal +g_dbus_interface_info_lookup_property +g_dbus_interface_info_generate_xml +g_dbus_node_info_new_for_xml +g_dbus_node_info_lookup_interface +g_dbus_node_info_generate_xml +G_TYPE_DBUS_NODE_INFO +G_TYPE_DBUS_INTERFACE_INFO +G_TYPE_DBUS_METHOD_INFO +G_TYPE_DBUS_SIGNAL_INFO +G_TYPE_DBUS_PROPERTY_INFO +G_TYPE_DBUS_ARG_INFO +G_TYPE_DBUS_ANNOTATION_INFO +g_dbus_node_info_ref +g_dbus_interface_info_ref +g_dbus_method_info_ref +g_dbus_signal_info_ref +g_dbus_property_info_ref +g_dbus_arg_info_ref +g_dbus_annotation_info_ref +g_dbus_node_info_unref +g_dbus_interface_info_unref +g_dbus_method_info_unref +g_dbus_signal_info_unref +g_dbus_property_info_unref +g_dbus_arg_info_unref +g_dbus_annotation_info_unref +
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index e8f973822..e1d65a931 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -107,3 +107,14 @@ g_volume_monitor_get_type g_zlib_compressor_get_type g_zlib_compressor_format_get_type g_zlib_decompressor_get_type +g_dbus_message_get_gtype +g_dbus_connection_get_type +g_bus_type_get_type +g_bus_name_owner_flags_get_type +g_dbus_error_get_type +g_dbus_proxy_get_type +g_dbus_method_invocation_get_type +g_dbus_server_get_type +g_dbus_auth_observer_get_type +g_credentials_get_type +g_unix_credentials_message_get_type diff --git a/gio/gcredentials.c b/gio/gcredentials.c index e1712fe61..1ca25432b 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -36,8 +36,8 @@ /** * SECTION:gcredentials - * @short_description: Credentials - * @include: gdbus/gdbus.h + * @short_description: An object containing credentials + * @include: gio/gio.h * * The #GCredentials type is used for storing information that can be * used for identifying, authenticating and authorizing processes. diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index e50e73781..6cfa5f951 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -40,7 +40,7 @@ * SECTION:gdbusaddress * @title: D-Bus Addresses * @short_description: D-Bus connection endpoints - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Routines for working with D-Bus addresses. */ diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index 850db9aa6..2d47060cd 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -78,195 +78,6 @@ debug_print (const gchar *message, ...) #endif } - -/* ---------------------------------------------------------------------------------------------------- */ -/* TODO: move to gio */ - -/** - * g_unix_connection_send_credentials: - * @connection: A #GUnixConnection. - * @credentials: A #GCredentials to send. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. - * - * Passes the credentials stored in @credentials to the recieving side - * of the connection. The recieving end has to call - * g_unix_connection_receive_credentials() (or similar) to accept the - * credentials. - * - * The credentials which the sender specifies are checked by the - * kernel. A process with effective user ID 0 is allowed to specify - * values that do not match its own. This means that the credentials - * can be used to authenticate other connections. - * - * As well as sending the credentials this also writes a single NUL - * byte to the stream, as this is required for credentials passing to - * work on some implementations. - * - * Returns: %TRUE on success, %FALSE if @error is set. - * - * Since: 2.26 - */ -static gboolean -g_unix_connection_send_credentials (GUnixConnection *connection, - GCredentials *credentials, - GCancellable *cancellable, - GError **error) -{ - GSocketControlMessage *scm; - GSocket *socket; - gboolean ret; - GOutputVector vector; - guchar nul_byte[1] = {'\0'}; - - g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - ret = FALSE; - - vector.buffer = &nul_byte; - vector.size = 1; - scm = g_unix_credentials_message_new_with_credentials (credentials); - g_object_get (connection, "socket", &socket, NULL); - if (g_socket_send_message (socket, - NULL, /* address */ - &vector, - 1, - &scm, - 1, - G_SOCKET_MSG_NONE, - cancellable, - error) != 1) - { - g_prefix_error (error, _("Error sending credentials: ")); - goto out; - } - - ret = TRUE; - - out: - g_object_unref (socket); - g_object_unref (scm); - return ret; -} - -/** - * g_unix_connection_receive_credentials: - * @connection: A #GUnixConnection. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. - * - * Receives credentials from the sending end of the connection. The - * sending end has to call g_unix_connection_send_credentials() (or - * similar) for this to work. - * - * As well as reading the credentials this also reads (and discards) a - * single byte from the stream, as this is required for credentials - * passing to work on some implementations. - * - * Returns: Received credentials on success (free with - * g_object_unref()), %NULL if @error is set. - * - * Since: 2.26 - */ -static GCredentials * -g_unix_connection_receive_credentials (GUnixConnection *connection, - GCancellable *cancellable, - GError **error) -{ - GCredentials *ret; - GSocketControlMessage **scms; - gint nscm; - GSocket *socket; - gint n; - volatile GType credentials_message_gtype; - gssize num_bytes_read; - - g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - ret = NULL; - scms = NULL; - - g_object_get (connection, "socket", &socket, NULL); - -#if 1 - /* TODO: Move this to gsocket.c... */ - { - int opt_val = 1; - if (setsockopt (g_socket_get_fd (socket), - SOL_SOCKET, - SO_PASSCRED, - &opt_val, - sizeof opt_val) != 0) - { - g_warning ("boo, error setting SO_PASSCRED: %m"); - } - } -#endif - - /* ensure the type of GUnixCredentialsMessage has been registered with the type system */ - credentials_message_gtype = G_TYPE_UNIX_CREDENTIALS_MESSAGE; - num_bytes_read = g_socket_receive_message (socket, - NULL, /* GSocketAddress **address */ - NULL, - 0, - &scms, - &nscm, - NULL, - cancellable, - error); - if (num_bytes_read != 1) - { - /* Handle situation where g_socket_receive_message() returns - * 0 bytes and not setting @error - */ - if (num_bytes_read == 0 && error != NULL && *error == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Expecting to read a single byte for receiving credentials but read zero bytes")); - } - goto out; - } - - if (nscm != 1) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Expecting 1 control message, got %d"), - nscm); - goto out; - } - - if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0])) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Unexpected type of ancillary data")); - goto out; - } - - ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0])); - g_object_ref (ret); - - out: - if (scms != NULL) - { - for (n = 0; n < nscm; n++) - g_object_unref (scms[n]); - g_free (scms); - } - g_object_unref (socket); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - typedef struct { const gchar *name; diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index f0411aba8..4de672283 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -33,7 +33,7 @@ /** * SECTION:gdbusauthobserver * @short_description: Object used for authenticating connections - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * The #GDBusAuthObserver type provides a mechanism for participating * in how a #GDBusServer (or a #GDBusConnection) authenticates remote diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 0bcd58f89..fdcb6ed29 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -78,7 +78,7 @@ /** * SECTION:gdbusconnection * @short_description: D-Bus Connections - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * * This class is rarely used directly in D-Bus clients. If you are @@ -89,11 +89,11 @@ * The #GDBusConnection type is used for D-Bus connections to remote * peers such as a message buses. * - * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT + * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT * - * D-Bus subtree exampleFIXME: MISSING XINCLUDE CONTENT + * D-Bus subtree exampleFIXME: MISSING XINCLUDE CONTENT * - * D-Bus UNIX File Descriptor exampleFIXME: MISSING XINCLUDE CONTENT + * D-Bus UNIX File Descriptor exampleFIXME: MISSING XINCLUDE CONTENT */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c index 3b0e080fc..5bc9c69ca 100644 --- a/gio/gdbuserror.c +++ b/gio/gdbuserror.c @@ -36,7 +36,7 @@ * SECTION:gdbuserror * @title: GDBusError * @short_description: Mapping D-Bus errors to and from #GError - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * All facilities that return errors from remote methods (such as * g_dbus_connection_invoke_method_sync()) use #GError to represent diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index 63b945e0e..c76af4eb8 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -32,7 +32,7 @@ * SECTION:gdbusintrospection * @title: Introspection XML * @short_description: Parse and Generate Introspection XML - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Various data structures and convenience routines to parse and * generate D-Bus introspection XML. diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 668b1c856..d1929ae3c 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -48,7 +48,7 @@ /** * SECTION:gdbusmessage * @short_description: D-Bus Message - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * A type for representing D-Bus messages that can be sent or received * on a #GDBusConnection. diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index dc6950bea..5885abcec 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -36,7 +36,7 @@ /** * SECTION:gdbusmethodinvocation * @short_description: Object for handling remote calls - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Instances of the #GDBusMethodInvocation class are used when * handling D-Bus method calls. It provides a way to asynchronously diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index 30901651f..e840353f8 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -36,11 +36,11 @@ * SECTION:gdbusnameowning * @title: Owning Bus Names * @short_description: Simple API for owning bus names - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Convenience API for owning bus names. * - * Simple application owning a nameFIXME: MISSING XINCLUDE CONTENT + * Simple application owning a nameFIXME: MISSING XINCLUDE CONTENT */ G_LOCK_DEFINE_STATIC (lock); diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 92e04cc18..6ae4754da 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -36,11 +36,11 @@ * SECTION:gdbusnamewatching * @title: Watching Bus Names * @short_description: Simple API for watching bus names - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Convenience API for watching bus names. * - * Simple application watching a nameFIXME: MISSING XINCLUDE CONTENT + * Simple application watching a nameFIXME: MISSING XINCLUDE CONTENT */ G_LOCK_DEFINE_STATIC (lock); diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 39094d0b4..89aced924 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -42,7 +42,7 @@ /** * SECTION:gdbusproxy * @short_description: Base class for proxies - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * #GDBusProxy is a base class used for proxies to access a D-Bus * interface on a remote object. A #GDBusProxy can only be constructed diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 4f85c1de5..4025e1f92 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -39,11 +39,11 @@ * SECTION:gdbusproxywatching * @title: Watching Proxies * @short_description: Simple API for watching proxies - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Convenience API for watching bus proxies. * - * Simple application watching a proxyFIXME: MISSING XINCLUDE CONTENT + * Simple application watching a proxyFIXME: MISSING XINCLUDE CONTENT */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index 0d0deeaa2..99e7701a2 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -44,12 +44,12 @@ /** * SECTION:gdbusserver * @short_description: Helper for accepting connections - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * #GDBusServer is a helper for listening to and accepting D-Bus * connections. * - * D-Bus peer-to-peer exampleFIXME: MISSING XINCLUDE CONTENT + * D-Bus peer-to-peer exampleFIXME: MISSING XINCLUDE CONTENT */ struct _GDBusServerPrivate diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c index b1ec0ea1c..00529168a 100644 --- a/gio/gdbusutils.c +++ b/gio/gdbusutils.c @@ -33,7 +33,7 @@ * SECTION:gdbusutils * @title: D-Bus Utilities * @short_description: Various utilities related to D-Bus. - * @include: gdbus/gdbus.h + * @include: gio/gio.h * * Various utility routines related to D-Bus. */ diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c index 92ad246f8..8b618cb0a 100644 --- a/gio/gunixconnection.c +++ b/gio/gunixconnection.c @@ -36,6 +36,14 @@ #include #include +#ifdef __linux__ +/* for getsockopt() and setsockopt() */ +#include /* See NOTES */ +#include +#include +#include +#endif + #include "gioalias.h" G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, @@ -287,5 +295,255 @@ gboolean g_unix_connection_create_pair (GUnixCo GError **error); */ + +/** + * g_unix_connection_send_credentials: + * @connection: A #GUnixConnection. + * @credentials: A #GCredentials to send. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Passes the credentials stored in @credentials to the recieving side + * of the connection. The recieving end has to call + * g_unix_connection_receive_credentials() (or similar) to accept the + * credentials. + * + * The credentials which the sender specifies are checked by the + * kernel. A process with effective user ID 0 is allowed to specify + * values that do not match its own. This means that the credentials + * can be used to authenticate other connections. + * + * As well as sending the credentials this also writes a single NUL + * byte to the stream, as this is required for credentials passing to + * work on some implementations. + * + * Returns: %TRUE on success, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_unix_connection_send_credentials (GUnixConnection *connection, + GCredentials *credentials, + GCancellable *cancellable, + GError **error) +{ + GSocketControlMessage *scm; + GSocket *socket; + gboolean ret; + GOutputVector vector; + guchar nul_byte[1] = {'\0'}; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + + vector.buffer = &nul_byte; + vector.size = 1; + scm = g_unix_credentials_message_new_with_credentials (credentials); + g_object_get (connection, "socket", &socket, NULL); + if (g_socket_send_message (socket, + NULL, /* address */ + &vector, + 1, + &scm, + 1, + G_SOCKET_MSG_NONE, + cancellable, + error) != 1) + { + g_prefix_error (error, _("Error sending credentials: ")); + goto out; + } + + ret = TRUE; + + out: + g_object_unref (socket); + g_object_unref (scm); + return ret; +} + +/** + * g_unix_connection_receive_credentials: + * @connection: A #GUnixConnection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Receives credentials from the sending end of the connection. The + * sending end has to call g_unix_connection_send_credentials() (or + * similar) for this to work. + * + * As well as reading the credentials this also reads (and discards) a + * single byte from the stream, as this is required for credentials + * passing to work on some implementations. + * + * Returns: Received credentials on success (free with + * g_object_unref()), %NULL if @error is set. + * + * Since: 2.26 + */ +GCredentials * +g_unix_connection_receive_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GCredentials *ret; + GSocketControlMessage **scms; + gint nscm; + GSocket *socket; + gint n; + volatile GType credentials_message_gtype; + gssize num_bytes_read; +#ifdef __linux__ + gboolean turn_off_so_passcreds; +#endif + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; + scms = NULL; + + g_object_get (connection, "socket", &socket, NULL); + + /* On Linux, we need to turn on SO_PASSCRED if it isn't enabled + * already. We also need to turn it off when we're done. See + * #617483 for more discussion. + */ +#ifdef __linux__ + { + gint opt_val; + socklen_t opt_len; + + turn_off_so_passcreds = FALSE; + opt_val = 0; + opt_len = sizeof (gint); + if (getsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + &opt_len) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error checking if SO_PASSCRED is enabled for socket: %s"), + strerror (errno)); + goto out; + } + if (opt_len != sizeof (gint)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected option length while checking if SO_PASSCRED is enabled for socket. " + "Expected %d bytes, got %d"), + (gint) sizeof (gint), (gint) opt_len); + goto out; + } + if (opt_val == 0) + { + opt_val = 1; + if (setsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + sizeof opt_val) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error enabling SO_PASSCRED: %s"), + strerror (errno)); + goto out; + } + turn_off_so_passcreds = TRUE; + } + } +#endif + + /* ensure the type of GUnixCredentialsMessage has been registered with the type system */ + credentials_message_gtype = G_TYPE_UNIX_CREDENTIALS_MESSAGE; + num_bytes_read = g_socket_receive_message (socket, + NULL, /* GSocketAddress **address */ + NULL, + 0, + &scms, + &nscm, + NULL, + cancellable, + error); + if (num_bytes_read != 1) + { + /* Handle situation where g_socket_receive_message() returns + * 0 bytes and not setting @error + */ + if (num_bytes_read == 0 && error != NULL && *error == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting to read a single byte for receiving credentials but read zero bytes")); + } + goto out; + } + + if (nscm != 1) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting 1 control message, got %d"), + nscm); + goto out; + } + + if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0])) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected type of ancillary data")); + goto out; + } + + ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0])); + g_object_ref (ret); + + out: + +#ifdef __linux__ + if (turn_off_so_passcreds) + { + gint opt_val; + opt_val = 0; + if (setsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + sizeof opt_val) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error while disabling SO_PASSCRED: %s"), + strerror (errno)); + goto out; + } + } +#endif + + if (scms != NULL) + { + for (n = 0; n < nscm; n++) + g_object_unref (scms[n]); + g_free (scms); + } + g_object_unref (socket); + return ret; +} + #define __G_UNIX_CONNECTION_C__ #include "gioaliasdef.c" diff --git a/gio/gunixconnection.h b/gio/gunixconnection.h index 7435e9742..9c691ea0d 100644 --- a/gio/gunixconnection.h +++ b/gio/gunixconnection.h @@ -71,6 +71,16 @@ gint g_unix_connection_receive_fd (GUnixCo GCancellable *cancellable, GError **error); +gboolean g_unix_connection_send_credentials (GUnixConnection *connection, + GCredentials *credentials, + GCancellable *cancellable, + GError **error); + +GCredentials *g_unix_connection_receive_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error); + + G_END_DECLS #endif /* __G_UNIX_CONNECTION_H__ */ From 0fd6498cd89888023fb2161bfdde9339a4346986 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 16:02:08 -0400 Subject: [PATCH 03/76] Add "Since: 2.26" to all new GDBus API --- docs/reference/gio/gio-docs.xml | 2 +- gio/gcredentials.c | 32 +++++++++++ gio/gcredentials.h | 4 ++ gio/gdbusaddress.c | 12 +++++ gio/gdbusauthobserver.c | 6 +++ gio/gdbusauthobserver.h | 4 ++ gio/gdbusconnection.c | 94 +++++++++++++++++++++++++++++++-- gio/gdbusconnection.h | 24 +++++++++ gio/gdbuserror.c | 20 +++++++ gio/gdbuserror.h | 4 ++ gio/gdbusintrospection.c | 52 ++++++++++++++++-- gio/gdbusintrospection.h | 28 ++++++++++ gio/gdbusmessage.c | 88 ++++++++++++++++++++++++++++++ gio/gdbusmessage.h | 4 ++ gio/gdbusmethodinvocation.c | 50 ++++++++++++++++++ gio/gdbusmethodinvocation.h | 4 ++ gio/gdbusnameowning.c | 10 +++- gio/gdbusnameowning.h | 6 +++ gio/gdbusnamewatching.c | 8 ++- gio/gdbusnamewatching.h | 4 ++ gio/gdbusprivate.c | 2 +- gio/gdbusproxy.c | 70 ++++++++++++++++++++---- gio/gdbusproxy.h | 4 ++ gio/gdbusproxywatching.c | 6 ++- gio/gdbusproxywatching.h | 4 ++ gio/gdbusserver.c | 28 ++++++++++ gio/gdbusserver.h | 4 ++ gio/gdbusutils.c | 15 +++++- gio/gioenums.h | 30 ++++++++++- gio/gunixcredentialsmessage.c | 4 ++ gio/gunixcredentialsmessage.h | 15 ++++++ 31 files changed, 611 insertions(+), 27 deletions(-) diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 305dc7a07..748955b0e 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -135,7 +135,6 @@ -
@@ -144,6 +143,7 @@ + Utilities diff --git a/gio/gcredentials.c b/gio/gcredentials.c index 1ca25432b..2f3c7cebe 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -100,6 +100,8 @@ g_credentials_init (GCredentials *credentials) * Creates a new empty credentials object. * * Returns: A #GCredentials. Free with g_object_unref(). + * + * Since: 2.26 */ GCredentials * g_credentials_new (void) @@ -132,6 +134,8 @@ g_credentials_new_for_unix_process (void) * platform. * * Returns: A #GCredentials. Free with g_object_unref(). + * + * Since: 2.26 */ GCredentials * g_credentials_new_for_process (void) @@ -157,6 +161,8 @@ g_credentials_new_for_process (void) * * Returns: A #GCredentials or %NULL if @error is set. The return * object must be freed with g_object_unref(). + * + * Since: 2.26 */ GCredentials * g_credentials_new_for_string (const gchar *str, @@ -212,6 +218,8 @@ g_credentials_new_for_string (const gchar *str, * g_credentials_new_for_string(). * * Returns: A string that should be freed with g_free(). + * + * Since: 2.26 */ gchar * g_credentials_to_string (GCredentials *credentials) @@ -244,6 +252,8 @@ g_credentials_to_string (GCredentials *credentials) * Checks if @credentials has a UNIX user credential. * * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_credentials_has_unix_user (GCredentials *credentials) @@ -259,6 +269,8 @@ g_credentials_has_unix_user (GCredentials *credentials) * Gets the UNIX user identifier from @credentials. * * Returns: The identifier or -1 if unset. + * + * Since: 2.26 */ gint64 g_credentials_get_unix_user (GCredentials *credentials) @@ -273,6 +285,8 @@ g_credentials_get_unix_user (GCredentials *credentials) * @user_id: A UNIX user identifier (typically type #uid_t) or -1 to unset it. * * Sets the UNIX user identifier. + * + * Since: 2.26 */ void g_credentials_set_unix_user (GCredentials *credentials, @@ -291,6 +305,8 @@ g_credentials_set_unix_user (GCredentials *credentials, * Checks if @credentials has a UNIX group credential. * * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_credentials_has_unix_group (GCredentials *credentials) @@ -306,6 +322,8 @@ g_credentials_has_unix_group (GCredentials *credentials) * Gets the UNIX group identifier from @credentials. * * Returns: The identifier or -1 if unset. + * + * Since: 2.26 */ gint64 g_credentials_get_unix_group (GCredentials *credentials) @@ -320,6 +338,8 @@ g_credentials_get_unix_group (GCredentials *credentials) * @group_id: A UNIX group identifier (typically type #gid_t) or -1 to unset. * * Sets the UNIX group identifier. + * + * Since: 2.26 */ void g_credentials_set_unix_group (GCredentials *credentials, @@ -338,6 +358,8 @@ g_credentials_set_unix_group (GCredentials *credentials, * Checks if @credentials has a UNIX process credential. * * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_credentials_has_unix_process (GCredentials *credentials) @@ -353,6 +375,8 @@ g_credentials_has_unix_process (GCredentials *credentials) * Gets the UNIX process identifier from @credentials. * * Returns: The identifier or -1 if unset. + * + * Since: 2.26 */ gint64 g_credentials_get_unix_process (GCredentials *credentials) @@ -367,6 +391,8 @@ g_credentials_get_unix_process (GCredentials *credentials) * @process_id: A UNIX process identifier (typically type #pid_t/#GPid) or -1 to unset. * * Sets the UNIX process identifier. + * + * Since: 2.26 */ void g_credentials_set_unix_process (GCredentials *credentials, @@ -385,6 +411,8 @@ g_credentials_set_unix_process (GCredentials *credentials, * Checks if @credentials has a Windows user SID (Security Identifier). * * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_credentials_has_windows_user (GCredentials *credentials) @@ -400,6 +428,8 @@ g_credentials_has_windows_user (GCredentials *credentials) * Gets the Windows User SID from @credentials. * * Returns: A string or %NULL if unset. Do not free, the string is owned by @credentials. + * + * Since: 2.26 */ const gchar * g_credentials_get_windows_user (GCredentials *credentials) @@ -414,6 +444,8 @@ g_credentials_get_windows_user (GCredentials *credentials) * @user_sid: The Windows User SID or %NULL to unset. * * Sets the Windows User SID. + * + * Since: 2.26 */ void g_credentials_set_windows_user (GCredentials *credentials, diff --git a/gio/gcredentials.h b/gio/gcredentials.h index a5bab7b7a..e26b0180c 100644 --- a/gio/gcredentials.h +++ b/gio/gcredentials.h @@ -42,6 +42,8 @@ typedef struct _GCredentialsPrivate GCredentialsPrivate; * * The #GCredentials structure contains only private data and * should only be accessed using the provided API. + * + * Since: 2.26 */ struct _GCredentials { @@ -54,6 +56,8 @@ struct _GCredentials * GCredentialsClass: * * Class structure for #GCredentials. + * + * Since: 2.26 */ struct _GCredentialsClass { diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 6cfa5f951..2c1041e58 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -58,6 +58,8 @@ * checks. * * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_address (const gchar *string) @@ -335,6 +337,8 @@ is_valid_tcp (const gchar *address_entry, * * Returns: %TRUE if @string is a valid D-Bus address that is * supported by this library, %FALSE if @error is set. + * + * Since: 2.26 */ gboolean g_dbus_is_supported_address (const gchar *string, @@ -759,6 +763,8 @@ get_stream_thread_func (GSimpleAsyncResult *res, * * This is an asynchronous failable function. See * g_dbus_address_get_stream_sync() for the synchronous version. + * + * Since: 2.26 */ void g_dbus_address_get_stream (const gchar *address, @@ -796,6 +802,8 @@ g_dbus_address_get_stream (const gchar *address, * Finishes an operation started with g_dbus_address_get_stream(). * * Returns: A #GIOStream or %NULL if @error is set. + * + * Since: 2.26 */ GIOStream * g_dbus_address_get_stream_finish (GAsyncResult *res, @@ -840,6 +848,8 @@ g_dbus_address_get_stream_finish (GAsyncResult *res, * g_dbus_address_get_stream() for the asynchronous version. * * Returns: A #GIOStream or %NULL if @error is set. + * + * Since: 2.26 */ GIOStream * g_dbus_address_get_stream_sync (const gchar *address, @@ -918,6 +928,8 @@ get_session_address_platform_specific (void) * platform specific mechanisms. * * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set. + * + * Since: 2.26 */ gchar * g_dbus_address_get_for_bus_sync (GBusType bus_type, diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index 4de672283..b114da9f7 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -146,6 +146,8 @@ g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass) * should be denied. * * Returns: %TRUE if the peer should be denied, %FALSE otherwise. + * + * Since: 2.26 */ signals[DENY_AUTHENTICATED_PEER_SIGNAL] = g_signal_new ("deny-authenticated-peer", @@ -179,6 +181,8 @@ g_dbus_auth_observer_init (GDBusAuthObserver *observer) * Creates a new #GDBusAuthObserver object. * * Returns: A #GDBusAuthObserver. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusAuthObserver * g_dbus_auth_observer_new (void) @@ -197,6 +201,8 @@ g_dbus_auth_observer_new (void) * Emits the #GDBusAuthObserver::deny-authenticated-peer signal on @observer. * * Returns: %TRUE if the peer should be denied, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer, diff --git a/gio/gdbusauthobserver.h b/gio/gdbusauthobserver.h index 6ed52e8f9..f3c88d020 100644 --- a/gio/gdbusauthobserver.h +++ b/gio/gdbusauthobserver.h @@ -43,6 +43,8 @@ typedef struct _GDBusAuthObserverPrivate GDBusAuthObserverPrivate; * @deny_authenticated_peer: Signal class handler for the #GDBusAuthObserver::deny-authenticated-peer signal. * * Class structure for #GDBusAuthObserverClass. + * + * Since: 2.26 */ struct _GDBusAuthObserverClass { @@ -82,6 +84,8 @@ struct _GDBusAuthObserverClass * * The #GDBusAuthObserver structure contains only private data and * should only be accessed using the provided API. + * + * Since: 2.26 */ struct _GDBusAuthObserver { diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index fdcb6ed29..77c36903c 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -483,6 +483,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * GDBusConnection:stream: * * The underlying #GIOStream used for I/O. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_STREAM, @@ -502,6 +504,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * * A D-Bus address specifying potential endpoints that can be used * when establishing the connection. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_ADDRESS, @@ -519,6 +523,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * GDBusConnection:flags: * * Flags from the #GDBusConnectionFlags enumeration. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_FLAGS, @@ -549,6 +555,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * #GDBusConnection:flags property you will be able to read the GUID * of the other peer here after the connection has been succesfully * initialized. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_GUID, @@ -568,6 +576,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * * The unique name as assigned by the message bus or %NULL if the * connection is not open or not a message bus connection. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_UNIQUE_NAME, @@ -584,6 +594,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * GDBusConnection:closed: * * A boolean specifying whether the connection has been closed. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_CLOSED, @@ -602,6 +614,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * A boolean specifying whether the process will be terminated (by * calling raise(SIGTERM)) if the connection * is closed by the remote peer. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_EXIT_ON_CLOSE, @@ -620,6 +634,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * * Flags from the #GDBusCapabilityFlags enumeration * representing connection features negotiated with the other peer. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_CAPABILITY_FLAGS, @@ -637,6 +653,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * GDBusConnection:authentication-observer: * * A #GDBusAuthObserver object to assist in the authentication process or %NULL. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_AUTHENTICATION_OBSERVER, @@ -679,6 +697,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * Upon receiving this signal, you should give up your reference to * @connection. You are guaranteed that this signal is emitted only * once. + * + * Since: 2.26 */ signals[CLOSED_SIGNAL] = g_signal_new ("closed", G_TYPE_DBUS_CONNECTION, @@ -750,7 +770,9 @@ g_dbus_connection_get_stream (GDBusConnection *connection) * Gets whether @connection is closed. * * Returns: %TRUE if the connection is closed, %FALSE otherwise. - **/ + * + * Since: 2.26 + */ gboolean g_dbus_connection_is_closed (GDBusConnection *connection) { @@ -765,6 +787,8 @@ g_dbus_connection_is_closed (GDBusConnection *connection) * Gets the capabilities negotiated with the remote peer * * Returns: One or more flags from the #GDBusCapabilityFlags enumeration. + * + * Since: 2.26 */ GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection) @@ -849,6 +873,8 @@ set_closed_unlocked (GDBusConnection *connection, * bus connection disconnects). * * If @connection is already closed, this method does nothing. + * + * Since: 2.26 */ void g_dbus_connection_close (GDBusConnection *connection) @@ -983,6 +1009,8 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, * * Returns: %TRUE if the message was well-formed and queued for * transmission, %FALSE if @error is set. + * + * Since: 2.26 */ gboolean g_dbus_connection_send_message (GDBusConnection *connection, @@ -1297,6 +1325,8 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * See and for an example of how to use this * low-level API to send and receive UNIX file descriptors. + * + * Since: 2.26 */ void g_dbus_connection_send_message_with_reply (GDBusConnection *connection, @@ -1339,6 +1369,8 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, * low-level API to send and receive UNIX file descriptors. * * Returns: A #GDBusMessage or %NULL if @error is set. + * + * Since: 2.26 */ GDBusMessage * g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, @@ -1426,6 +1458,8 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * low-level API to send and receive UNIX file descriptors. * * Returns: A #GDBusMessage that is the reply to @message or %NULL if @error is set. + * + * Since: 2.26 */ GDBusMessage * g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, @@ -1856,6 +1890,8 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * This is a asynchronous failable constructor. See * g_dbus_connection_new_sync() for the synchronous * version. + * + * Since: 2.26 */ void g_dbus_connection_new (GIOStream *stream, @@ -1887,6 +1923,8 @@ g_dbus_connection_new (GIOStream *stream, * Finishes an operation started with g_dbus_connection_new(). * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusConnection * g_dbus_connection_new_finish (GAsyncResult *res, @@ -1930,6 +1968,8 @@ g_dbus_connection_new_finish (GAsyncResult *res, * g_dbus_connection_new() for the asynchronous version. * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusConnection * g_dbus_connection_new_sync (GIOStream *stream, @@ -1978,6 +2018,8 @@ g_dbus_connection_new_sync (GIOStream *stream, * This is a asynchronous failable constructor. See * g_dbus_connection_new_for_address_sync() for the synchronous * version. + * + * Since: 2.26 */ void g_dbus_connection_new_for_address (const gchar *address, @@ -2005,6 +2047,8 @@ g_dbus_connection_new_for_address (const gchar *address, * Finishes an operation started with g_dbus_connection_new_for_address(). * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusConnection * g_dbus_connection_new_for_address_finish (GAsyncResult *res, @@ -2049,6 +2093,8 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, * g_dbus_connection_new_for_address() for the asynchronous version. * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusConnection * g_dbus_connection_new_for_address_sync (const gchar *address, @@ -2077,6 +2123,8 @@ g_dbus_connection_new_for_address_sync (const gchar *address, * Sets whether the process should be terminated when @connection is * closed by the remote peer. See #GDBusConnection:exit-on-close for * more details. + * + * Since: 2.26 */ void g_dbus_connection_set_exit_on_close (GDBusConnection *connection, @@ -2096,6 +2144,8 @@ g_dbus_connection_set_exit_on_close (GDBusConnection *connection, * * Returns: Whether the process is terminated when @connection is * closed by the remote peer. + * + * Since: 2.26 */ gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection) @@ -2113,7 +2163,9 @@ g_dbus_connection_get_exit_on_close (GDBusConnection *connection) * * Returns: The GUID. Do not free this string, it is owned by * @connection. - **/ + * + * Since: 2.26 + */ const gchar * g_dbus_connection_get_guid (GDBusConnection *connection) { @@ -2132,7 +2184,9 @@ g_dbus_connection_get_guid (GDBusConnection *connection) * Returns: The unique name or %NULL if @connection is not a message * bus connection. Do not free this string, it is owned by * @connection. - **/ + * + * Since: 2.26 + */ const gchar * g_dbus_connection_get_unique_name (GDBusConnection *connection) { @@ -2156,6 +2210,8 @@ g_dbus_connection_get_unique_name (GDBusConnection *connection) * * Returns: A #GCredentials or %NULL if not available. Do not free * this object, it is owned by @connection. + * + * Since: 2.26 */ GCredentials * g_dbus_connection_get_peer_credentials (GDBusConnection *connection) @@ -2192,6 +2248,8 @@ static guint _global_filter_id = 1; * * Returns: A filter identifier that can be used with * g_dbus_connection_remove_filter(). + * + * Since: 2.26 */ guint g_dbus_connection_add_filter (GDBusConnection *connection, @@ -2427,7 +2485,9 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) * call g_dbus_connection_signal_unsubscribe() to remove a subscription. * * Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe(). - **/ + * + * Since: 2.26 + */ guint g_dbus_connection_signal_subscribe (GDBusConnection *connection, const gchar *sender, @@ -2607,7 +2667,9 @@ unsubscribe_id_internal (GDBusConnection *connection, * @subscription_id: A subscription id obtained from g_dbus_connection_signal_subscribe(). * * Unsubscribes from signals. - **/ + * + * Since: 2.26 + */ void g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, guint subscription_id) @@ -3803,6 +3865,8 @@ obj_message_func (GDBusConnection *connection, * * Returns: 0 if @error is set, otherwise a registration id (never 0) * that can be used with g_dbus_connection_unregister_object() . + * + * Since: 2.26 */ guint g_dbus_connection_register_object (GDBusConnection *connection, @@ -3889,6 +3953,8 @@ g_dbus_connection_register_object (GDBusConnection *connection, * Unregisters an object. * * Returns: %TRUE if the object was unregistered, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_connection_unregister_object (GDBusConnection *connection, @@ -3947,6 +4013,8 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, * This can only fail if @parameters is not compatible with the D-Bus protocol. * * Returns: %TRUE unless @error is set. + * + * Since: 2.26 */ gboolean g_dbus_connection_emit_signal (GDBusConnection *connection, @@ -4029,6 +4097,8 @@ add_invoke_method_flags (GDBusMessage *message, * g_dbus_connection_invoke_method_finish() to get the result of the operation. * See g_dbus_connection_invoke_method_sync() for the synchronous version of this * function. + * + * Since: 2.26 */ void g_dbus_connection_invoke_method (GDBusConnection *connection, @@ -4115,6 +4185,8 @@ decode_method_reply (GDBusMessage *reply, GError **error) * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with * return values. Free with g_variant_unref(). + * + * Since: 2.26 */ GVariant * g_dbus_connection_invoke_method_finish (GDBusConnection *connection, @@ -4173,6 +4245,8 @@ g_dbus_connection_invoke_method_finish (GDBusConnection *connection, * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with * return values. Free with g_variant_unref(). + * + * Since: 2.26 */ GVariant * g_dbus_connection_invoke_method_sync (GDBusConnection *connection, @@ -4695,6 +4769,8 @@ subtree_message_func (GDBusConnection *connection, * * Returns: 0 if @error is set, otherwise a subtree registration id (never 0) * that can be used with g_dbus_connection_unregister_subtree() . + * + * Since: 2.26 */ guint g_dbus_connection_register_subtree (GDBusConnection *connection, @@ -4764,6 +4840,8 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, * Unregisters a subtree. * * Returns: %TRUE if the subtree was unregistered, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_connection_unregister_subtree (GDBusConnection *connection, @@ -5132,6 +5210,8 @@ get_uninitialized_connection (GBusType bus_type, * the #GDBusConnection:exit-on-close property set to %TRUE. * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusConnection * g_bus_get_sync (GBusType bus_type, @@ -5198,6 +5278,8 @@ bus_get_async_initable_cb (GObject *source_object, * * This is a asynchronous failable function. See g_bus_get_sync() for * the synchronous version. + * + * Since: 2.26 */ void g_bus_get (GBusType bus_type, @@ -5251,6 +5333,8 @@ g_bus_get (GBusType bus_type, * the #GDBusConnection:exit-on-close property set to %TRUE. * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusConnection * g_bus_get_finish (GAsyncResult *res, diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index 4eeed48b4..5a2b278bd 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -42,6 +42,8 @@ typedef struct _GDBusConnectionPrivate GDBusConnectionPrivate; * * The #GDBusConnection structure contains only private data and * should only be accessed using the provided API. + * + * Since: 2.26 */ struct _GDBusConnection { @@ -55,6 +57,8 @@ struct _GDBusConnection * @closed: Signal class handler for the #GDBusConnection::closed signal. * * Class structure for #GDBusConnection. + * + * Since: 2.26 */ struct _GDBusConnectionClass { @@ -207,6 +211,8 @@ GVariant *g_dbus_connection_invoke_method_sync (GDBusConnection * @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object(). * * The type of the @method_call function in #GDBusInterfaceVTable. + * + * Since: 2.26 */ typedef void (*GDBusInterfaceMethodCallFunc) (GDBusConnection *connection, const gchar *sender, @@ -230,6 +236,8 @@ typedef void (*GDBusInterfaceMethodCallFunc) (GDBusConnection *connection, * The type of the @get_property function in #GDBusInterfaceVTable. * * Returns: A newly-allocated #GVariant with the value for @property_name or %NULL if @error is set. + * + * Since: 2.26 */ typedef GVariant *(*GDBusInterfaceGetPropertyFunc) (GDBusConnection *connection, const gchar *sender, @@ -253,6 +261,8 @@ typedef GVariant *(*GDBusInterfaceGetPropertyFunc) (GDBusConnection *conne * The type of the @set_property function in #GDBusInterfaceVTable. * * Returns: %TRUE if the property was set to @value, %FALSE if @error is set. + * + * Since: 2.26 */ typedef gboolean (*GDBusInterfaceSetPropertyFunc) (GDBusConnection *connection, const gchar *sender, @@ -275,6 +285,8 @@ typedef gboolean (*GDBusInterfaceSetPropertyFunc) (GDBusConnection *conne * If you want to handle getting/setting D-Bus properties asynchronously, simply * register an object with the org.freedesktop.DBus.Properties * D-Bus interface using g_dbus_connection_register_object(). + * + * Since: 2.26 */ struct _GDBusInterfaceVTable { @@ -317,6 +329,8 @@ gboolean g_dbus_connection_unregister_object (GDBusConnection * The type of the @enumerate function in #GDBusSubtreeVTable. * * Returns: A newly allocated array of strings for node names that are children of @object_path. + * + * Since: 2.26 */ typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection, const gchar *sender, @@ -335,6 +349,8 @@ typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection, * * Returns: A newly-allocated #GPtrArray with pointers to #GDBusInterfaceInfo describing * the interfaces implemented by @node. + * + * Since: 2.26 */ typedef GPtrArray *(*GDBusSubtreeIntrospectFunc) (GDBusConnection *connection, const gchar *sender, @@ -355,6 +371,8 @@ typedef GPtrArray *(*GDBusSubtreeIntrospectFunc) (GDBusConnection *connect * The type of the @dispatch function in #GDBusSubtreeVTable. * * Returns: A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods. + * + * Since: 2.26 */ typedef const GDBusInterfaceVTable * (*GDBusSubtreeDispatchFunc) (GDBusConnection *connection, const gchar *sender, @@ -371,6 +389,8 @@ typedef const GDBusInterfaceVTable * (*GDBusSubtreeDispatchFunc) (GDBusConnectio * @dispatch: Function for dispatching a remote call on a child node. * * Virtual table for handling subtrees registered with g_dbus_connection_register_subtree(). + * + * Since: 2.26 */ struct _GDBusSubtreeVTable { @@ -413,6 +433,8 @@ gboolean g_dbus_connection_unregister_subtree (GDBusConnection * @user_data: User data passed when subscribing to the signal. * * Signature for callback function used in g_dbus_connection_signal_subscribe(). + * + * Since: 2.26 */ typedef void (*GDBusSignalCallback) (GDBusConnection *connection, const gchar *sender_name, @@ -446,6 +468,8 @@ void g_dbus_connection_signal_unsubscribe (GDBusConnection * * Returns: %TRUE if the filter handled @message, %FALSE to let other * handlers run. + * + * Since: 2.26 */ typedef gboolean (*GDBusMessageFilterFunction) (GDBusConnection *connection, GDBusMessage *message, diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c index 5bc9c69ca..5a372ffa5 100644 --- a/gio/gdbuserror.c +++ b/gio/gdbuserror.c @@ -167,6 +167,8 @@ g_dbus_error_quark (void) * @num_entries: Number of items to register. * * Helper function for associating a #GError error domain with D-Bus error names. + * + * Since: 2.26 */ void g_dbus_error_register_error_domain (const gchar *error_domain_quark_name, @@ -336,6 +338,8 @@ static GHashTable *dbus_error_name_to_re = NULL; * * Returns: %TRUE if the association was created, %FALSE if it already * exists. + * + * Since: 2.26 */ gboolean g_dbus_error_register_error (GQuark error_domain, @@ -395,6 +399,8 @@ g_dbus_error_register_error (GQuark error_domain, * Destroys an association previously set up with g_dbus_error_register_error(). * * Returns: %TRUE if the association was destroyed, %FALSE if it wasn't found. + * + * Since: 2.26 */ gboolean g_dbus_error_unregister_error (GQuark error_domain, @@ -463,6 +469,8 @@ g_dbus_error_unregister_error (GQuark error_domain, * * Returns: %TRUE if @error represents an error from a remote peer, * %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_error_is_remote_error (const GError *error) @@ -483,6 +491,8 @@ g_dbus_error_is_remote_error (const GError *error) * unless g_dbus_error_strip_remote_error() has been used on @error. * * Returns: An allocated string or %NULL if the D-Bus error name could not be found. Free with g_free(). + * + * Since: 2.26 */ gchar * g_dbus_error_get_remote_error (const GError *error) @@ -568,6 +578,8 @@ g_dbus_error_get_remote_error (const GError *error) * it. * * Returns: An allocated #GError. Free with g_error_free(). + * + * Since: 2.26 */ GError * g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name, @@ -639,6 +651,8 @@ g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name, * Does nothing if @error is %NULL. Otherwise sets *@error to * a new #GError created with g_dbus_error_new_for_dbus_error() * with @dbus_error_message prepend with @format (unless %NULL). + * + * Since: 2.26 */ void g_dbus_error_set_dbus_error (GError **error, @@ -680,6 +694,8 @@ g_dbus_error_set_dbus_error (GError **error, * @var_args: Arguments for @format. * * Like g_dbus_error_set_dbus_error() but intended for language bindings. + * + * Since: 2.26 */ void g_dbus_error_set_dbus_error_valist (GError **error, @@ -723,6 +739,8 @@ g_dbus_error_set_dbus_error_valist (GError **error, * This is typically used when presenting errors to the end user. * * Returns: %TRUE if information was stripped, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_error_strip_remote_error (GError *error) @@ -770,6 +788,8 @@ g_dbus_error_strip_remote_error (GError *error) * #GError on the wire. Regular applications should not use it. * * Returns: A D-Bus error name (never %NULL). Free with g_free(). + * + * Since: 2.26 */ gchar * g_dbus_error_encode_gerror (const GError *error) diff --git a/gio/gdbuserror.h b/gio/gdbuserror.h index d15724cf5..73c97aa83 100644 --- a/gio/gdbuserror.h +++ b/gio/gdbuserror.h @@ -38,6 +38,8 @@ G_BEGIN_DECLS * returning errors from a remote message bus process. Errors * generated locally in-process by e.g. #GDBusConnection is from the * %G_IO_ERROR domain. + * + * Since: 2.26 */ #define G_DBUS_ERROR g_dbus_error_quark() @@ -54,6 +56,8 @@ gboolean g_dbus_error_strip_remote_error (GError *error); * @dbus_error_name: The D-Bus error name to associate with @error_code. * * Struct used in g_dbus_error_register_error_domain(). + * + * Since: 2.26 */ struct _GDBusErrorEntry { diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index c76af4eb8..fd26d40e7 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -106,6 +106,8 @@ typedef struct * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusNodeInfo * g_dbus_node_info_ref (GDBusNodeInfo *info) @@ -124,6 +126,8 @@ g_dbus_node_info_ref (GDBusNodeInfo *info) * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusInterfaceInfo * g_dbus_interface_info_ref (GDBusInterfaceInfo *info) @@ -142,6 +146,8 @@ g_dbus_interface_info_ref (GDBusInterfaceInfo *info) * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusMethodInfo * g_dbus_method_info_ref (GDBusMethodInfo *info) @@ -160,6 +166,8 @@ g_dbus_method_info_ref (GDBusMethodInfo *info) * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusSignalInfo * g_dbus_signal_info_ref (GDBusSignalInfo *info) @@ -178,6 +186,8 @@ g_dbus_signal_info_ref (GDBusSignalInfo *info) * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusPropertyInfo * g_dbus_property_info_ref (GDBusPropertyInfo *info) @@ -196,6 +206,8 @@ g_dbus_property_info_ref (GDBusPropertyInfo *info) * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusArgInfo * g_dbus_arg_info_ref (GDBusArgInfo *info) @@ -214,6 +226,8 @@ g_dbus_arg_info_ref (GDBusArgInfo *info) * the reference count. * * Returns: The same @info. + * + * Since: 2.26 */ GDBusAnnotationInfo * g_dbus_annotation_info_ref (GDBusAnnotationInfo *info) @@ -245,6 +259,8 @@ free_null_terminated_array (gpointer array, GDestroyNotify unref_func) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_annotation_info_unref (GDBusAnnotationInfo *info) @@ -267,6 +283,8 @@ g_dbus_annotation_info_unref (GDBusAnnotationInfo *info) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_arg_info_unref (GDBusArgInfo *info) @@ -289,6 +307,8 @@ g_dbus_arg_info_unref (GDBusArgInfo *info) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_method_info_unref (GDBusMethodInfo *info) @@ -312,6 +332,8 @@ g_dbus_method_info_unref (GDBusMethodInfo *info) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_signal_info_unref (GDBusSignalInfo *info) @@ -334,6 +356,8 @@ g_dbus_signal_info_unref (GDBusSignalInfo *info) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_property_info_unref (GDBusPropertyInfo *info) @@ -356,6 +380,8 @@ g_dbus_property_info_unref (GDBusPropertyInfo *info) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_interface_info_unref (GDBusInterfaceInfo *info) @@ -380,6 +406,8 @@ g_dbus_interface_info_unref (GDBusInterfaceInfo *info) * If @info is statically allocated, does nothing. Otherwise decreases * the reference count of @info. When its reference count drops to 0, * the memory used is freed. + * + * Since: 2.26 */ void g_dbus_node_info_unref (GDBusNodeInfo *info) @@ -805,6 +833,8 @@ g_dbus_property_info_generate_xml (const GDBusPropertyInfo *info, * documents at run-time for handling the * org.freedesktop.DBus.Introspectable.Introspect * method. + * + * Since: 2.26 */ void g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info, @@ -850,6 +880,8 @@ g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info, * * This function is typically used for generating introspection XML documents at run-time for * handling the org.freedesktop.DBus.Introspectable.Introspect method. + * + * Since: 2.26 */ void g_dbus_node_info_generate_xml (const GDBusNodeInfo *node_info, @@ -1759,6 +1791,8 @@ parser_error (GMarkupParseContext *context, * * Returns: A #GDBusNodeInfo structure or %NULL if @error is set. Free * with g_dbus_node_info_unref(). + * + * Since: 2.26 */ GDBusNodeInfo * g_dbus_node_info_new_for_xml (const gchar *xml_data, @@ -1838,6 +1872,8 @@ g_dbus_node_info_new_for_xml (const gchar *xml_data, * This cost of this function is O(n) in number of annotations. * * Returns: The value or %NULL if not found. Do not free, it is owned by @annotations. + * + * Since: 2.26 */ const gchar * g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations, @@ -1872,7 +1908,9 @@ g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations, * This cost of this function is O(n) in number of methods. * * Returns: A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info. - **/ + * + * Since: 2.26 + */ const GDBusMethodInfo * g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info, const gchar *name) @@ -1909,7 +1947,9 @@ g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info, * This cost of this function is O(n) in number of signals. * * Returns: A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info. - **/ + * + * Since: 2.26 + */ const GDBusSignalInfo * g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info, const gchar *name) @@ -1946,7 +1986,9 @@ g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info, * This cost of this function is O(n) in number of properties. * * Returns: A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info. - **/ + * + * Since: 2.26 + */ const GDBusPropertyInfo * g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info, const gchar *name) @@ -1983,7 +2025,9 @@ g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info, * This cost of this function is O(n) in number of interfaces. * * Returns: A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @node_info. - **/ + * + * Since: 2.26 + */ const GDBusInterfaceInfo * g_dbus_node_info_lookup_interface (const GDBusNodeInfo *node_info, const gchar *name) diff --git a/gio/gdbusintrospection.h b/gio/gdbusintrospection.h index 7aea8d9cb..897041f3d 100644 --- a/gio/gdbusintrospection.h +++ b/gio/gdbusintrospection.h @@ -35,6 +35,8 @@ G_BEGIN_DECLS * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about an annotation. + * + * Since: 2.26 */ struct _GDBusAnnotationInfo { @@ -52,6 +54,8 @@ struct _GDBusAnnotationInfo * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about an argument for a method or a signal. + * + * Since: 2.26 */ struct _GDBusArgInfo { @@ -70,6 +74,8 @@ struct _GDBusArgInfo * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about a method on an D-Bus interface. + * + * Since: 2.26 */ struct _GDBusMethodInfo { @@ -88,6 +94,8 @@ struct _GDBusMethodInfo * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about a signal on a D-Bus interface. + * + * Since: 2.26 */ struct _GDBusSignalInfo { @@ -106,6 +114,8 @@ struct _GDBusSignalInfo * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about a D-Bus property on a D-Bus interface. + * + * Since: 2.26 */ struct _GDBusPropertyInfo { @@ -126,6 +136,8 @@ struct _GDBusPropertyInfo * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about a D-Bus interface. + * + * Since: 2.26 */ struct _GDBusInterfaceInfo { @@ -146,6 +158,8 @@ struct _GDBusInterfaceInfo * @annotations: A pointer to a %NULL-terminated array of pointers to #GDBusAnnotationInfo structures or %NULL if there are no annotations. * * Information about nodes in a remote object hierarchy. + * + * Since: 2.26 */ struct _GDBusNodeInfo { @@ -197,6 +211,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_NODE_INFO: * * The #GType for a boxed type holding a #GDBusNodeInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_NODE_INFO (g_dbus_node_info_get_type ()) @@ -204,6 +220,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_INTERFACE_INFO: * * The #GType for a boxed type holding a #GDBusInterfaceInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_INTERFACE_INFO (g_dbus_interface_info_get_type ()) @@ -211,6 +229,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_METHOD_INFO: * * The #GType for a boxed type holding a #GDBusMethodInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_METHOD_INFO (g_dbus_method_info_get_type ()) @@ -218,6 +238,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_SIGNAL_INFO: * * The #GType for a boxed type holding a #GDBusSignalInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_SIGNAL_INFO (g_dbus_signal_info_get_type ()) @@ -225,6 +247,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_PROPERTY_INFO: * * The #GType for a boxed type holding a #GDBusPropertyInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_PROPERTY_INFO (g_dbus_property_info_get_type ()) @@ -232,6 +256,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_ARG_INFO: * * The #GType for a boxed type holding a #GDBusArgInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_ARG_INFO (g_dbus_arg_info_get_type ()) @@ -239,6 +265,8 @@ void g_dbus_annotation_info_unref (GDBusAnnotatio * G_TYPE_DBUS_ANNOTATION_INFO: * * The #GType for a boxed type holding a #GDBusAnnotationInfo. + * + * Since: 2.26 */ #define G_TYPE_DBUS_ANNOTATION_INFO (g_dbus_annotation_info_get_type ()) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index d1929ae3c..9eff732a2 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -118,6 +118,8 @@ g_dbus_message_init (GDBusMessage *message) * Creates a new empty #GDBusMessage. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new (void) @@ -135,6 +137,8 @@ g_dbus_message_new (void) * Creates a new #GDBusMessage for a method call. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_method_call (const gchar *name, @@ -171,6 +175,8 @@ g_dbus_message_new_method_call (const gchar *name, * Creates a new #GDBusMessage for a signal emission. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_signal (const gchar *path, @@ -205,6 +211,8 @@ g_dbus_message_new_signal (const gchar *path, * Creates a new #GDBusMessage that is a reply to @method_call_message. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_method_reply (GDBusMessage *method_call_message) @@ -239,6 +247,8 @@ g_dbus_message_new_method_reply (GDBusMessage *method_call_message) * Creates a new #GDBusMessage that is an error reply to @method_call_message. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_method_error (GDBusMessage *method_call_message, @@ -269,6 +279,8 @@ g_dbus_message_new_method_error (GDBusMessage *method_call_message, * Creates a new #GDBusMessage that is an error reply to @method_call_message. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message, @@ -310,6 +322,8 @@ g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message, * Like g_dbus_message_new_method_error() but intended for language bindings. * * Returns: A #GDBusMessage. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_method_error_valist (GDBusMessage *method_call_message, @@ -338,6 +352,8 @@ g_dbus_message_new_method_error_valist (GDBusMessage *method_call_me * Gets the type of @message. * * Returns: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration). + * + * Since: 2.26 */ GDBusMessageType g_dbus_message_get_type (GDBusMessage *message) @@ -352,6 +368,8 @@ g_dbus_message_get_type (GDBusMessage *message) * @type: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration). * * Sets @message to be of @type. + * + * Since: 2.26 */ void g_dbus_message_set_type (GDBusMessage *message, @@ -373,6 +391,8 @@ g_dbus_message_set_type (GDBusMessage *message, * Gets the flags for @message. * * Returns: Flags that are set (typically values from the #GDBusMessageFlags enumeration bitwise ORed together). + * + * Since: 2.26 */ GDBusMessageFlags g_dbus_message_get_flags (GDBusMessage *message) @@ -388,6 +408,8 @@ g_dbus_message_get_flags (GDBusMessage *message) * enumeration bitwise ORed together). * * Sets the flags to set on @message. + * + * Since: 2.26 */ void g_dbus_message_set_flags (GDBusMessage *message, @@ -407,6 +429,8 @@ g_dbus_message_set_flags (GDBusMessage *message, * Gets the serial for @message. * * Returns: A #guint32. + * + * Since: 2.26 */ guint32 g_dbus_message_get_serial (GDBusMessage *message) @@ -421,6 +445,8 @@ g_dbus_message_get_serial (GDBusMessage *message) * @serial: A #guint32. * * Sets the serial for @message. + * + * Since: 2.26 */ void g_dbus_message_set_serial (GDBusMessage *message, @@ -443,6 +469,8 @@ g_dbus_message_set_serial (GDBusMessage *message, * * Returns: A #GVariant with the value if the header was found, %NULL * otherwise. Do not free, it is owned by @message. + * + * Since: 2.26 */ GVariant * g_dbus_message_get_header (GDBusMessage *message, @@ -462,6 +490,8 @@ g_dbus_message_get_header (GDBusMessage *message, * Sets a header field on @message. * * If @value is floating, @message assumes ownership of @value. + * + * Since: 2.26 */ void g_dbus_message_set_header (GDBusMessage *message, @@ -489,6 +519,8 @@ g_dbus_message_set_header (GDBusMessage *message, * Returns: An array of header fields terminated by * %G_DBUS_MESSAGE_HEADER_FIELD_INVALID. Each element is a * #guchar. Free with g_free(). + * + * Since: 2.26 */ guchar * g_dbus_message_get_header_fields (GDBusMessage *message) @@ -522,6 +554,8 @@ g_dbus_message_get_header_fields (GDBusMessage *message) * Gets the body of a message. * * Returns: A #GVariant or %NULL if the body is empty. Do not free, it is owned by @message. + * + * Since: 2.26 */ GVariant * g_dbus_message_get_body (GDBusMessage *message) @@ -540,6 +574,8 @@ g_dbus_message_get_body (GDBusMessage *message) * type string of @body (or cleared if @body is %NULL). * * If @body is floating, @message assumes ownership of @body. + * + * Since: 2.26 */ void g_dbus_message_set_body (GDBusMessage *message, @@ -585,6 +621,8 @@ g_dbus_message_set_body (GDBusMessage *message, * * Returns: A #GUnixFDList or %NULL if no file descriptors are * associated. Do not free, this object is owned by @message. + * + * Since: 2.26 */ GUnixFDList * g_dbus_message_get_unix_fd_list (GDBusMessage *message) @@ -604,6 +642,8 @@ g_dbus_message_get_unix_fd_list (GDBusMessage *message) * @fd_list is %NULL). * * This method is only available on UNIX. + * + * Since: 2.26 */ void g_dbus_message_set_unix_fd_list (GDBusMessage *message, @@ -1026,6 +1066,8 @@ parse_value_from_blob (GMemoryInputStream *mis, * Returns: Number of bytes needed or -1 if @error is set (e.g. if * @blob contains invalid data or not enough data is available to * determine the size). + * + * Since: 2.26 */ gssize g_dbus_message_bytes_needed (guchar *blob, @@ -1086,6 +1128,8 @@ g_dbus_message_bytes_needed (guchar *blob, * * Returns: A new #GDBusMessage or %NULL if @error is set. Free with * g_object_unref(). + * + * Since: 2.26 */ GDBusMessage * g_dbus_message_new_from_blob (guchar *blob, @@ -1527,6 +1571,8 @@ append_body_to_blob (GVariant *value, * * Returns: A pointer to a valid binary D-Bus message of @out_size bytes * generated by @message or %NULL if @error is set. Free with g_free(). + * + * Since: 2.26 */ guchar * g_dbus_message_to_blob (GDBusMessage *message, @@ -1792,6 +1838,8 @@ set_signature_header (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL header field. * * Returns: The value. + * + * Since: 2.26 */ guint32 g_dbus_message_get_reply_serial (GDBusMessage *message) @@ -1806,6 +1854,8 @@ g_dbus_message_get_reply_serial (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL header field. + * + * Since: 2.26 */ void g_dbus_message_set_reply_serial (GDBusMessage *message, @@ -1824,6 +1874,8 @@ g_dbus_message_set_reply_serial (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_interface (GDBusMessage *message) @@ -1838,6 +1890,8 @@ g_dbus_message_get_interface (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE header field. + * + * Since: 2.26 */ void g_dbus_message_set_interface (GDBusMessage *message, @@ -1857,6 +1911,8 @@ g_dbus_message_set_interface (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_MEMBER header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_member (GDBusMessage *message) @@ -1871,6 +1927,8 @@ g_dbus_message_get_member (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_MEMBER header field. + * + * Since: 2.26 */ void g_dbus_message_set_member (GDBusMessage *message, @@ -1890,6 +1948,8 @@ g_dbus_message_set_member (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_PATH header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_path (GDBusMessage *message) @@ -1904,6 +1964,8 @@ g_dbus_message_get_path (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_PATH header field. + * + * Since: 2.26 */ void g_dbus_message_set_path (GDBusMessage *message, @@ -1923,6 +1985,8 @@ g_dbus_message_set_path (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_SENDER header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_sender (GDBusMessage *message) @@ -1937,6 +2001,8 @@ g_dbus_message_get_sender (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_SENDER header field. + * + * Since: 2.26 */ void g_dbus_message_set_sender (GDBusMessage *message, @@ -1956,6 +2022,8 @@ g_dbus_message_set_sender (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_destination (GDBusMessage *message) @@ -1970,6 +2038,8 @@ g_dbus_message_get_destination (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION header field. + * + * Since: 2.26 */ void g_dbus_message_set_destination (GDBusMessage *message, @@ -1989,6 +2059,8 @@ g_dbus_message_set_destination (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_error_name (GDBusMessage *message) @@ -2003,6 +2075,8 @@ g_dbus_message_get_error_name (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field. + * + * Since: 2.26 */ void g_dbus_message_set_error_name (GDBusMessage *message, @@ -2022,6 +2096,8 @@ g_dbus_message_set_error_name (GDBusMessage *message, * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field. * * Returns: The value. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_signature (GDBusMessage *message) @@ -2040,6 +2116,8 @@ g_dbus_message_get_signature (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field. + * + * Since: 2.26 */ void g_dbus_message_set_signature (GDBusMessage *message, @@ -2060,6 +2138,8 @@ g_dbus_message_set_signature (GDBusMessage *message, * * Returns: The string item or %NULL if the first item in the body of * @message is not a string. + * + * Since: 2.26 */ const gchar * g_dbus_message_get_arg0 (GDBusMessage *message) @@ -2090,6 +2170,8 @@ g_dbus_message_get_arg0 (GDBusMessage *message) * Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header field. * * Returns: The value. + * + * Since: 2.26 */ guint32 g_dbus_message_get_num_unix_fds (GDBusMessage *message) @@ -2104,6 +2186,8 @@ g_dbus_message_get_num_unix_fds (GDBusMessage *message) * @value: The value to set. * * Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header field. + * + * Since: 2.26 */ void g_dbus_message_set_num_unix_fds (GDBusMessage *message, @@ -2129,6 +2213,8 @@ g_dbus_message_set_num_unix_fds (GDBusMessage *message, * well as the first string item in @message's body. * * Returns: %TRUE if @error was set, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_message_to_gerror (GDBusMessage *message, @@ -2298,6 +2384,8 @@ _sort_keys_func (gconstpointer a, * * * Returns: A string that should be freed with g_free(). + * + * Since: 2.26 */ gchar * g_dbus_message_print (GDBusMessage *message, diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h index 4fb1873b5..747345d80 100644 --- a/gio/gdbusmessage.h +++ b/gio/gdbusmessage.h @@ -45,6 +45,8 @@ typedef struct _GDBusMessagePrivate GDBusMessagePrivate; * GDBusMessageClass: * * Class structure for #GDBusMessage. + * + * Since: 2.26 */ struct _GDBusMessageClass { @@ -57,6 +59,8 @@ struct _GDBusMessageClass * * The #GDBusMessage structure contains only private data and should * only be accessed using the provided API. + * + * Since: 2.26 */ struct _GDBusMessage { diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index 5885abcec..1dfb315d0 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -208,6 +208,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:sender: * * The bus name that invoked the method or %NULL if the connection is not a bus connection. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_SENDER, @@ -226,6 +228,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:object-path: * * The object path the method was invoked on. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_OBJECT_PATH, @@ -244,6 +248,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:interface-name: * * The name of the D-Bus interface the method was invoked on. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_INTERFACE_NAME, @@ -262,6 +268,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:method-name: * * The name of the method that was invoked. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_METHOD_NAME, @@ -280,6 +288,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:method-info: * * Information about the method that was invoked, if any. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_METHOD_INFO, @@ -298,6 +308,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:connection: * * The #GDBusConnection the method was invoked on. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_CONNECTION, @@ -316,6 +328,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:message: * * The D-Bus message. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_MESSAGE, @@ -334,6 +348,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:parameters: * * The parameters as a #GVariant tuple. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_PARAMETERS, @@ -352,6 +368,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) * GDBusMethodInvocation:user-data: * * The @user_data #gpointer passed to g_dbus_connection_register_object(). + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_USER_DATA, @@ -383,6 +401,8 @@ g_dbus_method_invocation_init (GDBusMethodInvocation *invocation) * Gets the bus name that invoked the method. * * Returns: A string. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ const gchar * g_dbus_method_invocation_get_sender (GDBusMethodInvocation *invocation) @@ -398,6 +418,8 @@ g_dbus_method_invocation_get_sender (GDBusMethodInvocation *invocation) * Gets the object path the method was invoked on. * * Returns: A string. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ const gchar * g_dbus_method_invocation_get_object_path (GDBusMethodInvocation *invocation) @@ -413,6 +435,8 @@ g_dbus_method_invocation_get_object_path (GDBusMethodInvocation *invocation) * Gets the name of the D-Bus interface the method was invoked on. * * Returns: A string. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ const gchar * g_dbus_method_invocation_get_interface_name (GDBusMethodInvocation *invocation) @@ -428,6 +452,8 @@ g_dbus_method_invocation_get_interface_name (GDBusMethodInvocation *invocation) * Gets information about the method call, if any. * * Returns: A #GDBusMethodInfo or %NULL. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ const GDBusMethodInfo * g_dbus_method_invocation_get_method_info (GDBusMethodInvocation *invocation) @@ -443,6 +469,8 @@ g_dbus_method_invocation_get_method_info (GDBusMethodInvocation *invocation) * Gets the name of the method that was invoked. * * Returns: A string. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ const gchar * g_dbus_method_invocation_get_method_name (GDBusMethodInvocation *invocation) @@ -458,6 +486,8 @@ g_dbus_method_invocation_get_method_name (GDBusMethodInvocation *invocation) * Gets the #GDBusConnection the method was invoked on. * * Returns: A #GDBusConnection. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ GDBusConnection * g_dbus_method_invocation_get_connection (GDBusMethodInvocation *invocation) @@ -480,6 +510,8 @@ g_dbus_method_invocation_get_connection (GDBusMethodInvocation *invocation) * low-level API to send and receive UNIX file descriptors. * * Returns: A #GDBusMessage. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ GDBusMessage * g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation) @@ -495,6 +527,8 @@ g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation) * Gets the parameters of the method invocation. * * Returns: A #GVariant. Do not free, it is owned by @invocation. + * + * Since: 2.26 */ GVariant * g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation) @@ -510,6 +544,8 @@ g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation) * Gets the @user_data #gpointer passed to g_dbus_connection_register_object(). * * Returns: A #gpointer. + * + * Since: 2.26 */ gpointer g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation) @@ -533,6 +569,8 @@ g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation) * Creates a new #GDBusMethodInvocation object. * * Returns: A #GDBusMethodInvocation. Free with g_object_unref(). + * + * Since: 2.26 */ GDBusMethodInvocation * g_dbus_method_invocation_new (const gchar *sender, @@ -578,6 +616,8 @@ g_dbus_method_invocation_new (const gchar *sender, * It is an error if @parameters is not of the right format. * * This method will free @invocation, you cannot use it afterwards. + * + * Since: 2.26 */ void g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation, @@ -655,6 +695,8 @@ g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation, * or use g_dbus_method_invocation_return_dbus_error(). * * This method will free @invocation, you cannot use it afterwards. + * + * Since: 2.26 */ void g_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation, @@ -689,6 +731,8 @@ g_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation, * language bindings. * * This method will free @invocation, you cannot use it afterwards. + * + * Since: 2.26 */ void g_dbus_method_invocation_return_error_valist (GDBusMethodInvocation *invocation, @@ -720,6 +764,8 @@ g_dbus_method_invocation_return_error_valist (GDBusMethodInvocation *invocation, * Like g_dbus_method_invocation_return_error() but without printf()-style formatting. * * This method will free @invocation, you cannot use it afterwards. + * + * Since: 2.26 */ void g_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation, @@ -746,6 +792,8 @@ g_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation * instead of the error domain, error code and message. * * This method will free @invocation, you cannot use it afterwards. + * + * Since: 2.26 */ void g_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation, @@ -773,6 +821,8 @@ g_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation, * Finishes handling a D-Bus method call by returning an error. * * This method will free @invocation, you cannot use it afterwards. + * + * Since: 2.26 */ void g_dbus_method_invocation_return_dbus_error (GDBusMethodInvocation *invocation, diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h index 65d0f9924..2c6563c02 100644 --- a/gio/gdbusmethodinvocation.h +++ b/gio/gdbusmethodinvocation.h @@ -42,6 +42,8 @@ typedef struct _GDBusMethodInvocationPrivate GDBusMethodInvocationPrivate; * * The #GDBusMethodInvocation structure contains only private data and * should only be accessed using the provided API. + * + * Since: 2.26 */ struct _GDBusMethodInvocation { @@ -54,6 +56,8 @@ struct _GDBusMethodInvocation * GDBusMethodInvocationClass: * * Class structure for #GDBusMethodInvocation. + * + * Since: 2.26 */ struct _GDBusMethodInvocationClass { diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index e840353f8..dab48015d 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -461,7 +461,9 @@ connection_get_cb (GObject *source_object, * * Returns: An identifier (never 0) that an be used with * g_bus_unown_name() to stop owning the name. - **/ + * + * Since: 2.26 + */ guint g_bus_own_name_on_connection (GDBusConnection *connection, const gchar *name, @@ -571,7 +573,9 @@ g_bus_own_name_on_connection (GDBusConnection *connection, * * Returns: An identifier (never 0) that an be used with * g_bus_unown_name() to stop owning the name. - **/ + * + * Since: 2.26 + */ guint g_bus_own_name (GBusType bus_type, const gchar *name, @@ -626,6 +630,8 @@ g_bus_own_name (GBusType bus_type, * @owner_id: An identifier obtained from g_bus_own_name() * * Stops owning a name. + * + * Since: 2.26 */ void g_bus_unown_name (guint owner_id) diff --git a/gio/gdbusnameowning.h b/gio/gdbusnameowning.h index fc063e036..3a7518cf0 100644 --- a/gio/gdbusnameowning.h +++ b/gio/gdbusnameowning.h @@ -34,6 +34,8 @@ G_BEGIN_DECLS * @user_data: User data passed to g_bus_own_name(). * * Invoked when a connection to a message bus has been obtained. + * + * Since: 2.26 */ typedef void (*GBusAcquiredCallback) (GDBusConnection *connection, const gchar *name, @@ -46,6 +48,8 @@ typedef void (*GBusAcquiredCallback) (GDBusConnection *connection, * @user_data: User data passed to g_bus_own_name() or g_bus_own_name_on_connection(). * * Invoked when the name is acquired. + * + * Since: 2.26 */ typedef void (*GBusNameAcquiredCallback) (GDBusConnection *connection, const gchar *name, @@ -59,6 +63,8 @@ typedef void (*GBusNameAcquiredCallback) (GDBusConnection *connection, * @user_data: User data passed to g_bus_own_name() or g_bus_own_name_on_connection(). * * Invoked when the name is lost or @connection has been closed. + * + * Since: 2.26 */ typedef void (*GBusNameLostCallback) (GDBusConnection *connection, const gchar *name, diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 6ae4754da..f6126e5ae 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -534,7 +534,9 @@ connection_get_cb (GObject *source_object, * * Returns: An identifier (never 0) that an be used with * g_bus_unwatch_name() to stop watching the name. - **/ + * + * Since: 2.26 + */ guint g_bus_watch_name (GBusType bus_type, const gchar *name, @@ -587,7 +589,9 @@ g_bus_watch_name (GBusType bus_type, * @watcher_id: An identifier obtained from g_bus_watch_name() * * Stops watching a name. - **/ + * + * Since: 2.26 + */ void g_bus_unwatch_name (guint watcher_id) { diff --git a/gio/gdbusnamewatching.h b/gio/gdbusnamewatching.h index a724d4257..28805ceac 100644 --- a/gio/gdbusnamewatching.h +++ b/gio/gdbusnamewatching.h @@ -35,6 +35,8 @@ G_BEGIN_DECLS * @user_data: User data passed to g_bus_watch_name(). * * Invoked when the name being watched is known to have to have a owner. + * + * Since: 2.26 */ typedef void (*GBusNameAppearedCallback) (GDBusConnection *connection, const gchar *name, @@ -48,6 +50,8 @@ typedef void (*GBusNameAppearedCallback) (GDBusConnection *connection, * @user_data: User data passed to g_bus_watch_name(). * * Invoked when the name being watched is known not to have to have a owner. + * + * Since: 2.26 */ typedef void (*GBusNameVanishedCallback) (GDBusConnection *connection, const gchar *name, diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index adc6e12f3..8f7ac8d1b 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -974,7 +974,7 @@ _g_dbus_debug_message (void) return (_gdbus_debug_flags & G_DBUS_DEBUG_MESSAGE) != 0; } -/** +/* * _g_dbus_initialize: * * Does various one-time init things such as diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 89aced924..204aa74f6 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -240,6 +240,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * type signature of the message isn't what's expected, the given * #GError is set. Signals that have a type signature mismatch are * simply dropped. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_INTERFACE_INFO, @@ -257,6 +259,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * GDBusProxy:g-connection: * * The #GDBusConnection the proxy is for. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_CONNECTION, @@ -275,6 +279,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * GDBusProxy:g-flags: * * Flags from the #GDBusProxyFlags enumeration. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_FLAGS, @@ -294,6 +300,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * GDBusProxy:g-unique-bus-name: * * The unique bus name the proxy is for. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_UNIQUE_BUS_NAME, @@ -312,6 +320,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * GDBusProxy:g-object-path: * * The object path the proxy is for. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_OBJECT_PATH, @@ -330,6 +340,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * GDBusProxy:g-interface-name: * * The D-Bus interface name the proxy is for. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_INTERFACE_NAME, @@ -355,6 +367,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * remote method invocations on the proxy. If this property is -1, * the default timeout (typically 25 seconds) is used. If set to * %G_MAXINT, then no timeout is used. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_G_DEFAULT_TIMEOUT, @@ -378,6 +392,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * * Emitted when one or more D-Bus properties on @proxy changes. The cached properties * are already replaced when this signal fires. + * + * Since: 2.26 */ signals[PROPERTIES_CHANGED_SIGNAL] = g_signal_new ("g-properties-changed", G_TYPE_DBUS_PROXY, @@ -398,7 +414,9 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * @parameters: A #GVariant tuple with parameters for the signal. * * Emitted when a signal from the remote object and interface that @proxy is for, has been received. - **/ + * + * Since: 2.26 + */ signals[SIGNAL_SIGNAL] = g_signal_new ("g-signal", G_TYPE_DBUS_PROXY, G_SIGNAL_RUN_LAST, @@ -433,6 +451,8 @@ g_dbus_proxy_init (GDBusProxy *proxy) * * Returns: A %NULL-terminated array of strings or %NULL if @error is set. Free with * g_strfreev(). + * + * Since: 2.26 */ gchar ** g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, @@ -491,6 +511,8 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, * * Returns: A reference to the #GVariant instance that holds the value for @property_name or * %NULL if @error is set. Free the reference with g_variant_unref(). + * + * Since: 2.26 */ GVariant * g_dbus_proxy_get_cached_property (GDBusProxy *proxy, @@ -903,7 +925,9 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * g_dbus_proxy_new_finish() to get the result. * * See g_dbus_proxy_new_sync() and for a synchronous version of this constructor. - **/ + * + * Since: 2.26 + */ void g_dbus_proxy_new (GDBusConnection *connection, GType object_type, @@ -945,7 +969,9 @@ g_dbus_proxy_new (GDBusConnection *connection, * Finishes creating a #GDBusProxy. * * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref(). - **/ + * + * Since: 2.26 + */ GDBusProxy * g_dbus_proxy_new_finish (GAsyncResult *res, GError **error) @@ -994,7 +1020,9 @@ g_dbus_proxy_new_finish (GAsyncResult *res, * and g_dbus_proxy_new_finish() for the asynchronous version. * * Returns: A #GDBusProxy or %NULL if error is set. Free with g_object_unref(). - **/ + * + * Since: 2.26 + */ GDBusProxy * g_dbus_proxy_new_sync (GDBusConnection *connection, GType object_type, @@ -1040,7 +1068,9 @@ g_dbus_proxy_new_sync (GDBusConnection *connection, * Gets the connection @proxy is for. * * Returns: A #GDBusConnection owned by @proxy. Do not free. - **/ + * + * Since: 2.26 + */ GDBusConnection * g_dbus_proxy_get_connection (GDBusProxy *proxy) { @@ -1055,7 +1085,9 @@ g_dbus_proxy_get_connection (GDBusProxy *proxy) * Gets the flags that @proxy was constructed with. * * Returns: Flags from the #GDBusProxyFlags enumeration. - **/ + * + * Since: 2.26 + */ GDBusProxyFlags g_dbus_proxy_get_flags (GDBusProxy *proxy) { @@ -1070,7 +1102,9 @@ g_dbus_proxy_get_flags (GDBusProxy *proxy) * Gets the unique bus name @proxy is for. * * Returns: A string owned by @proxy. Do not free. - **/ + * + * Since: 2.26 + */ const gchar * g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy) { @@ -1085,7 +1119,9 @@ g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy) * Gets the object path @proxy is for. * * Returns: A string owned by @proxy. Do not free. - **/ + * + * Since: 2.26 + */ const gchar * g_dbus_proxy_get_object_path (GDBusProxy *proxy) { @@ -1100,7 +1136,9 @@ g_dbus_proxy_get_object_path (GDBusProxy *proxy) * Gets the D-Bus interface name @proxy is for. * * Returns: A string owned by @proxy. Do not free. - **/ + * + * Since: 2.26 + */ const gchar * g_dbus_proxy_get_interface_name (GDBusProxy *proxy) { @@ -1119,6 +1157,8 @@ g_dbus_proxy_get_interface_name (GDBusProxy *proxy) * See the #GDBusProxy:g-default-timeout property for more details. * * Returns: Timeout to use for @proxy. + * + * Since: 2.26 */ gint g_dbus_proxy_get_default_timeout (GDBusProxy *proxy) @@ -1137,6 +1177,8 @@ g_dbus_proxy_get_default_timeout (GDBusProxy *proxy) * g_dbus_proxy_invoke_method_sync() functions. * * See the #GDBusProxy:g-default-timeout property for more details. + * + * Since: 2.26 */ void g_dbus_proxy_set_default_timeout (GDBusProxy *proxy, @@ -1164,6 +1206,8 @@ g_dbus_proxy_set_default_timeout (GDBusProxy *proxy, * * Returns: A #GDBusInterfaceInfo or %NULL. Do not unref the returned * object, it is owned by @proxy. + * + * Since: 2.26 */ GDBusInterfaceInfo * g_dbus_proxy_get_interface_info (GDBusProxy *proxy) @@ -1184,6 +1228,8 @@ g_dbus_proxy_get_interface_info (GDBusProxy *proxy) * dropped. * * See the #GDBusProxy:g-interface-info property for more details. + * + * Since: 2.26 */ void g_dbus_proxy_set_interface_info (GDBusProxy *proxy, @@ -1346,6 +1392,8 @@ validate_method_return (const char *method_name, * g_dbus_proxy_invoke_method_finish() to get the result of the operation. * See g_dbus_proxy_invoke_method_sync() for the * synchronous version of this method. + * + * Since: 2.26 */ void g_dbus_proxy_invoke_method (GDBusProxy *proxy, @@ -1409,6 +1457,8 @@ g_dbus_proxy_invoke_method (GDBusProxy *proxy, * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with * return values. Free with g_variant_unref(). + * + * Since: 2.26 */ GVariant * g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, @@ -1478,6 +1528,8 @@ g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with * return values. Free with g_variant_unref(). + * + * Since: 2.26 */ GVariant * g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index 1bc9902d0..560d106f8 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -43,6 +43,8 @@ typedef struct _GDBusProxyPrivate GDBusProxyPrivate; * * The #GDBusProxy structure contains only private data and * should only be accessed using the provided API. + * + * Since: 2.26 */ struct _GDBusProxy { @@ -57,6 +59,8 @@ struct _GDBusProxy * @g_signal: Signal class handler for the #GDBusProxy::g-signal signal. * * Class structure for #GDBusProxy. + * + * Since: 2.26 */ struct _GDBusProxyClass { diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 4025e1f92..9ac7da8ff 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -298,7 +298,9 @@ on_name_vanished (GDBusConnection *connection, * * Returns: An identifier (never 0) that can be used with * g_bus_unwatch_proxy() to stop watching the remote object. - **/ + * + * Since: 2.26 + */ guint g_bus_watch_proxy (GBusType bus_type, const gchar *name, @@ -364,6 +366,8 @@ g_bus_watch_proxy (GBusType bus_type, * @watcher_id: An identifier obtained from g_bus_watch_proxy() * * Stops watching proxy. + * + * Since: 2.26 */ void g_bus_unwatch_proxy (guint watcher_id) diff --git a/gio/gdbusproxywatching.h b/gio/gdbusproxywatching.h index 3544269d3..119311baf 100644 --- a/gio/gdbusproxywatching.h +++ b/gio/gdbusproxywatching.h @@ -38,6 +38,8 @@ G_BEGIN_DECLS * Invoked when the proxy being watched is ready for use - the passed * @proxy object is valid until the #GBusProxyVanishedCallback * callback is invoked. + * + * Since: 2.26 */ typedef void (*GBusProxyAppearedCallback) (GDBusConnection *connection, const gchar *name, @@ -54,6 +56,8 @@ typedef void (*GBusProxyAppearedCallback) (GDBusConnection *connection, * Invoked when the proxy being watched has vanished. The #GDBusProxy * object passed in the #GBusProxyAppearedCallback callback is no * longer valid. + * + * Since: 2.26 */ typedef void (*GBusProxyVanishedCallback) (GDBusConnection *connection, const gchar *name, diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index 99e7701a2..bee5c90a0 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -218,6 +218,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * GDBusServer:flags: * * Flags from the #GDBusServerFlags enumeration. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_FLAGS, @@ -237,6 +239,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * GDBusServer:guid: * * The guid of the server. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_GUID, @@ -255,6 +259,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * GDBusServer:address: * * The D-Bus address to listen on. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_ADDRESS, @@ -273,6 +279,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * GDBusServer:client-address: * * The D-Bus address that clients can use. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_CLIENT_ADDRESS, @@ -289,6 +297,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * GDBusServer:active: * * Whether the server is currently active. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_ACTIVE, @@ -305,6 +315,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * GDBusServer:authentication-observer: * * A #GDBusAuthObserver object to assist in the authentication process or %NULL. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_AUTHENTICATION_OBSERVER, @@ -338,6 +350,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) * connection. Otherwise the signal is emitted in the thread-default main * loop of the thread that @server was constructed in. + * + * Since: 2.26 */ _signals[NEW_CONNECTION_SIGNAL] = g_signal_new ("new-connection", G_TYPE_DBUS_SERVER, @@ -399,6 +413,8 @@ on_run (GSocketService *service, * * Returns: A #GDBusServer or %NULL if @error is set. Free with * g_object_unref(). + * + * Since: 2.26 */ GDBusServer * g_dbus_server_new_sync (const gchar *address, @@ -444,6 +460,8 @@ g_dbus_server_new_sync (const gchar *address, * * Returns: A D-Bus address string. Do not free, the string is owned * by @server. + * + * Since: 2.26 */ const gchar * g_dbus_server_get_client_address (GDBusServer *server) @@ -459,6 +477,8 @@ g_dbus_server_get_client_address (GDBusServer *server) * Gets the GUID for @server. * * Returns: A D-Bus GUID. Do not free this string, it is owned by @server. + * + * Since: 2.26 */ const gchar * g_dbus_server_get_guid (GDBusServer *server) @@ -474,6 +494,8 @@ g_dbus_server_get_guid (GDBusServer *server) * Gets the flags for @server. * * Returns: A set of flags from the #GDBusServerFlags enumeration. + * + * Since: 2.26 */ GDBusServerFlags g_dbus_server_get_flags (GDBusServer *server) @@ -489,6 +511,8 @@ g_dbus_server_get_flags (GDBusServer *server) * Gets whether @server is active. * * Returns: %TRUE if server is active, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_server_is_active (GDBusServer *server) @@ -502,6 +526,8 @@ g_dbus_server_is_active (GDBusServer *server) * @server: A #GDBusServer. * * Starts @server. + * + * Since: 2.26 */ void g_dbus_server_start (GDBusServer *server) @@ -521,6 +547,8 @@ g_dbus_server_start (GDBusServer *server) * @server: A #GDBusServer. * * Stops @server. + * + * Since: 2.26 */ void g_dbus_server_stop (GDBusServer *server) diff --git a/gio/gdbusserver.h b/gio/gdbusserver.h index 404385459..dd1333f45 100644 --- a/gio/gdbusserver.h +++ b/gio/gdbusserver.h @@ -42,6 +42,8 @@ typedef struct _GDBusServerPrivate GDBusServerPrivate; * * The #GDBusServer structure contains only private data and * should only be accessed using the provided API. + * + * Since: 2.26 */ struct _GDBusServer { @@ -55,6 +57,8 @@ struct _GDBusServer * @new_connection: Signal class handler for the #GDBusServer::new-connection signal. * * Class structure for #GDBusServer. + * + * Since: 2.26 */ struct _GDBusServerClass { diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c index 00529168a..08770c6d6 100644 --- a/gio/gdbusutils.c +++ b/gio/gdbusutils.c @@ -116,6 +116,8 @@ is_valid_name (const gchar *start, * Checks if @string is a valid D-Bus bus name (either unique or well-known). * * Returns: %TRUE if valid, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_name (const gchar *string) @@ -164,6 +166,8 @@ g_dbus_is_name (const gchar *string) * Checks if @string is a valid D-Bus unique bus name. * * Returns: %TRUE if valid, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_unique_name (const gchar *string) @@ -198,6 +202,8 @@ g_dbus_is_unique_name (const gchar *string) * Checks if @string is a valid D-Bus member (e.g. signal or method) name. * * Returns: %TRUE if valid, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_member_name (const gchar *string) @@ -233,6 +239,8 @@ g_dbus_is_member_name (const gchar *string) * Checks if @string is a valid D-Bus interface name. * * Returns: %TRUE if valid, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_interface_name (const gchar *string) @@ -282,6 +290,8 @@ g_dbus_is_interface_name (const gchar *string) * GUID (for example, D-Bus GUIDs are not RFC-4122 compliant). * * Returns: A valid D-Bus GUID. Free with g_free(). + * + * Since: 2.26 */ gchar * g_dbus_generate_guid (void) @@ -317,6 +327,8 @@ g_dbus_generate_guid (void) * GUID (for example, D-Bus GUIDs are not RFC-4122 compliant). * * Returns: %TRUE if @string is a guid, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_guid (const gchar *string) @@ -344,13 +356,14 @@ g_dbus_is_guid (const gchar *string) /* ---------------------------------------------------------------------------------------------------- */ - /** * g_dbus_is_activated: * * Determine if the process has been activated by a message bus. * * Returns: %TRUE if this process has been started by a message bus, %FALSE otherwise. + * + * Since: 2.26 */ gboolean g_dbus_is_activated (void) diff --git a/gio/gioenums.h b/gio/gioenums.h index c1d2e9f60..f7b222fba 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -746,6 +746,8 @@ typedef enum { * @G_BUS_TYPE_STARTER: Connect to the bus that activated the program. * * An enumeration to specify the type of a #GDBusConnection. + * + * Since: 2.26 */ typedef enum { @@ -763,6 +765,8 @@ typedef enum * specified #G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, then take the name from the other connection. * * Flags used in g_bus_own_name(). + * + * Since: 2.26 */ typedef enum { @@ -779,6 +783,8 @@ typedef enum * name. * * Flags used in g_bus_watch_name(). + * + * Since: 2.26 */ typedef enum { @@ -793,6 +799,8 @@ typedef enum * @G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS: Don't connect to signals on the remote object. * * Flags used when constructing an instance of a #GDBusProxy derived class. + * + * Since: 2.26 */ typedef enum { @@ -897,6 +905,8 @@ typedef enum * There's already an object with the requested object path. * * Error codes for the %G_DBUS_ERROR error domain. + * + * Since: 2.26 */ typedef enum { @@ -943,7 +953,7 @@ typedef enum G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN, /* org.freedesktop.DBus.Error.AdtAuditDataUnknown */ G_DBUS_ERROR_OBJECT_PATH_IN_USE, /* org.freedesktop.DBus.Error.ObjectPathInUse */ } GDBusError; -/* TODO: remember to update g_dbus_error_quark() in gdbuserror.c if you extend this enumeration */ +/* Remember to update g_dbus_error_quark() in gdbuserror.c if you extend this enumeration */ /** * GDBusConnectionFlags: @@ -957,6 +967,8 @@ typedef enum * message bus. This means that the Hello() method will be invoked as part of the connection setup. * * Flags used when creating a new #GDBusConnection. + * + * Since: 2.26 */ typedef enum { G_DBUS_CONNECTION_FLAGS_NONE = 0, @@ -973,6 +985,8 @@ typedef enum { * supports exchanging UNIX file descriptors with the remote peer. * * Capabilities negotiated with the remote peer. + * + * Since: 2.26 */ typedef enum { G_DBUS_CAPABILITY_FLAGS_NONE = 0, @@ -987,6 +1001,8 @@ typedef enum { * invocation. * * Flags used in g_dbus_connection_invoke_method() and similar APIs. + * + * Since: 2.26 */ typedef enum { G_DBUS_INVOKE_METHOD_FLAGS_NONE = 0, @@ -1002,6 +1018,8 @@ typedef enum { * @G_DBUS_MESSAGE_TYPE_SIGNAL: Signal emission. * * Message types used in #GDBusMessage. + * + * Since: 2.26 */ typedef enum { G_DBUS_MESSAGE_TYPE_INVALID, @@ -1019,6 +1037,8 @@ typedef enum { * owner for the destination name in response to this message. * * Message flags used in #GDBusMessage. + * + * Since: 2.26 */ typedef enum { G_DBUS_MESSAGE_FLAGS_NONE = 0, @@ -1040,6 +1060,8 @@ typedef enum { * @G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS: The number of UNIX file descriptors that accompany the message. * * Header fields used in #GDBusMessage. + * + * Since: 2.26 */ typedef enum { G_DBUS_MESSAGE_HEADER_FIELD_INVALID, @@ -1061,6 +1083,8 @@ typedef enum { * @G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE: Property is writable. * * Flags describing the access control of a D-Bus property. + * + * Since: 2.26 */ typedef enum { @@ -1077,6 +1101,8 @@ typedef enum * to dynamically spawn objects in the subtree. * * Flags passed to g_dbus_connection_register_subtree(). + * + * Since: 2.26 */ typedef enum { @@ -1094,6 +1120,8 @@ typedef enum * authentication method. * * Flags used when creating a #GDBusServer. + * + * Since: 2.26 */ typedef enum { diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index 6af8c297c..156a124ea 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -252,6 +252,8 @@ g_unix_credentials_message_class_init (GUnixCredentialsMessageClass *class) * GUnixCredentialsMessage:credentials: * * The credentials stored in the message. + * + * Since: 2.26 */ g_object_class_install_property (gobject_class, PROP_CREDENTIALS, @@ -331,6 +333,8 @@ g_unix_credentials_message_new_with_credentials (GCredentials *credentials) * Gets the credentials stored in @message. * * Returns: A #GCredentials instance. Do not free, it is owned by @message. + * + * Since: 2.26 */ GCredentials * g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message) diff --git a/gio/gunixcredentialsmessage.h b/gio/gunixcredentialsmessage.h index 1db0146e2..7f444d5bf 100644 --- a/gio/gunixcredentialsmessage.h +++ b/gio/gunixcredentialsmessage.h @@ -39,6 +39,13 @@ G_BEGIN_DECLS typedef struct _GUnixCredentialsMessagePrivate GUnixCredentialsMessagePrivate; typedef struct _GUnixCredentialsMessageClass GUnixCredentialsMessageClass; +/** + * GUnixCredentialsMessageClass: + * + * Class structure for #GUnixCredentialsMessage. + * + * Since: 2.26 + */ struct _GUnixCredentialsMessageClass { GSocketControlMessageClass parent_class; @@ -50,6 +57,14 @@ struct _GUnixCredentialsMessageClass void (*_g_reserved2) (void); }; +/** + * GUnixCredentialsMessage: + * + * The #GUnixCredentialsMessage structure contains only private data + * and should only be accessed using the provided API. + * + * Since: 2.26 + */ struct _GUnixCredentialsMessage { GSocketControlMessage parent_instance; From fb1b4599a0effe728f42da8748b469a62f91ed8d Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 16:34:23 -0400 Subject: [PATCH 04/76] GDBus: Fix up i18n --- gio/gcredentials.c | 3 ++- gio/gdbus-tool.c | 4 +++- gio/gdbusaddress.c | 5 +++-- gio/gdbusauth.c | 4 ++-- gio/gdbusauthmechanism.c | 4 ++-- gio/gdbusauthmechanismanon.c | 4 ++-- gio/gdbusauthmechanismexternal.c | 4 +++- gio/gdbusauthmechanismsha1.c | 4 +++- gio/gdbusauthobserver.c | 4 ++-- gio/gdbusconnection.c | 27 +++++++++++++++------------ gio/gdbuserror.c | 5 +++-- gio/gdbusintrospection.c | 5 +++-- gio/gdbusmessage.c | 3 ++- gio/gdbusmethodinvocation.c | 3 ++- gio/gdbusnameowning.c | 4 ++-- gio/gdbusnamewatching.c | 5 +++-- gio/gdbusprivate.c | 16 ++++++++++++++-- gio/gdbusproxy.c | 5 ++++- gio/gdbusproxywatching.c | 4 ++-- gio/gdbusserver.c | 16 ++++++++++------ gio/gdbusutils.c | 4 ++-- gio/gunixcredentialsmessage.c | 4 ++-- 22 files changed, 86 insertions(+), 51 deletions(-) diff --git a/gio/gcredentials.c b/gio/gcredentials.c index 2f3c7cebe..7a572251b 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -23,7 +23,6 @@ #include "config.h" #include -#include #include #include "gcredentials.h" @@ -34,6 +33,8 @@ #include #endif +#include "glibintl.h" + /** * SECTION:gcredentials * @short_description: An object containing credentials diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index b3233cf45..573b53d8a 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -22,12 +22,14 @@ #include "config.h" -#include +#include #include #include #include +#include + /* ---------------------------------------------------------------------------------------------------- */ G_GNUC_UNUSED static void completion_debug (const gchar *format, ...); diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 2c1041e58..8379b046e 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -23,8 +23,7 @@ #include "config.h" #include - -#include +#include #include "gdbusutils.h" #include "gdbusaddress.h" @@ -36,6 +35,8 @@ #include #endif +#include "glibintl.h" + /** * SECTION:gdbusaddress * @title: D-Bus Addresses diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index 2d47060cd..2cfb74ee2 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -22,8 +22,6 @@ #include "config.h" -#include - #include "gdbusauth.h" #include "gdbusauthmechanismanon.h" #include "gdbusauthmechanismexternal.h" @@ -44,6 +42,8 @@ #include #endif +#include "glibintl.h" + #define DEBUG_ENABLED 1 static void diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c index 72eff74be..a62b26775 100644 --- a/gio/gdbusauthmechanism.c +++ b/gio/gdbusauthmechanism.c @@ -22,14 +22,14 @@ #include "config.h" -#include - #include "gdbusauthmechanism.h" #include "gcredentials.h" #include "gdbuserror.h" #include "gioenumtypes.h" #include "giostream.h" +#include "glibintl.h" + /* ---------------------------------------------------------------------------------------------------- */ struct _GDBusAuthMechanismPrivate diff --git a/gio/gdbusauthmechanismanon.c b/gio/gdbusauthmechanismanon.c index 4c666ec1f..c5a0f3c7f 100644 --- a/gio/gdbusauthmechanismanon.c +++ b/gio/gdbusauthmechanismanon.c @@ -22,12 +22,12 @@ #include "config.h" -#include - #include "gdbusauthmechanismanon.h" #include "gdbuserror.h" #include "gioenumtypes.h" +#include "glibintl.h" + struct _GDBusAuthMechanismAnonPrivate { gboolean is_client; diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c index bf8d9318a..3307e85ed 100644 --- a/gio/gdbusauthmechanismexternal.c +++ b/gio/gdbusauthmechanismexternal.c @@ -22,7 +22,7 @@ #include "config.h" -#include +#include #include "gdbusauthmechanismexternal.h" #include "gcredentials.h" @@ -34,6 +34,8 @@ #include #endif +#include "glibintl.h" + struct _GDBusAuthMechanismExternalPrivate { gboolean is_client; diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c index ee94e49a0..bc9ed2596 100644 --- a/gio/gdbusauthmechanismsha1.c +++ b/gio/gdbusauthmechanismsha1.c @@ -22,7 +22,7 @@ #include "config.h" -#include +#include #include "gdbusauthmechanismsha1.h" #include "gcredentials.h" @@ -40,6 +40,8 @@ #include +#include "glibintl.h" + struct _GDBusAuthMechanismSha1Private { gboolean is_client; diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index b114da9f7..54d335ffd 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -22,14 +22,14 @@ #include "config.h" -#include - #include "gdbusauthobserver.h" #include "gio-marshal.h" #include "gcredentials.h" #include "gioenumtypes.h" #include "giostream.h" +#include "glibintl.h" + /** * SECTION:gdbusauthobserver * @short_description: Object used for authenticating connections diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 77c36903c..835291e66 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -49,20 +49,9 @@ #include "config.h" #include - -#include - -#ifdef G_OS_UNIX -#include -#include -#endif - -#include -#include -#include +#include #include "gdbusauth.h" - #include "gdbusutils.h" #include "gdbusaddress.h" #include "gdbusmessage.h" @@ -74,6 +63,20 @@ #include "gdbusprivate.h" #include "gdbusauthobserver.h" #include "gio-marshal.h" +#include "ginitable.h" +#include "gasyncinitable.h" +#include "giostream.h" +#include "gasyncresult.h" +#include "gsimpleasyncresult.h" + +#ifdef G_OS_UNIX +#include +#include +#include +#include +#endif + +#include "glibintl.h" /** * SECTION:gdbusconnection diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c index 5a372ffa5..0bfd9067c 100644 --- a/gio/gdbuserror.c +++ b/gio/gdbuserror.c @@ -23,8 +23,7 @@ #include "config.h" #include - -#include +#include #include "gdbuserror.h" #include "gioenums.h" @@ -32,6 +31,8 @@ #include "gioerror.h" #include "gdbusprivate.h" +#include "glibintl.h" + /** * SECTION:gdbuserror * @title: GDBusError diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index fd26d40e7..b128d9f87 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -23,11 +23,12 @@ #include "config.h" #include - -#include +#include #include "gdbusintrospection.h" +#include "glibintl.h" + /** * SECTION:gdbusintrospection * @title: Introspection XML diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 9eff732a2..839ef55a5 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -22,7 +22,7 @@ #include "config.h" -#include +#include #include "gdbusutils.h" #include "gdbusmessage.h" @@ -44,6 +44,7 @@ #include #endif +#include "glibintl.h" /** * SECTION:gdbusmessage diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index 1dfb315d0..b2f99ef1b 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -23,7 +23,6 @@ #include "config.h" #include -#include #include "gdbusutils.h" #include "gdbusconnection.h" @@ -33,6 +32,8 @@ #include "gdbuserror.h" #include "gdbusprivate.h" +#include "glibintl.h" + /** * SECTION:gdbusmethodinvocation * @short_description: Object for handling remote calls diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index dab48015d..e9474298a 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -24,14 +24,14 @@ #include -#include - #include "gdbusutils.h" #include "gdbusnameowning.h" #include "gdbuserror.h" #include "gdbusprivate.h" #include "gdbusconnection.h" +#include "glibintl.h" + /** * SECTION:gdbusnameowning * @title: Owning Bus Names diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index f6126e5ae..a68255bfb 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -23,8 +23,7 @@ #include "config.h" #include - -#include +#include #include "gdbusutils.h" #include "gdbusnamewatching.h" @@ -32,6 +31,8 @@ #include "gdbusprivate.h" #include "gdbusconnection.h" +#include "glibintl.h" + /** * SECTION:gdbusnamewatching * @title: Watching Bus Names diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 8f7ac8d1b..64ea7d95e 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -25,8 +25,6 @@ #include #include -#include - #ifdef G_OS_UNIX #include #include @@ -35,10 +33,24 @@ #endif #include "giotypes.h" +#include "gsocket.h" #include "gdbusprivate.h" #include "gdbusmessage.h" #include "gdbuserror.h" #include "gdbusintrospection.h" +#include "gasyncresult.h" +#include "gsimpleasyncresult.h" +#include "ginputstream.h" +#include "giostream.h" +#include "gsocketcontrolmessage.h" + +#ifdef G_OS_UNIX +#include +#include "gunixfdmessage.h" +#include "gunixconnection.h" +#endif + +#include "glibintl.h" /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 204aa74f6..ded62c96c 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -23,7 +23,8 @@ #include "config.h" #include -#include +#include + #include #include "gdbusutils.h" @@ -39,6 +40,8 @@ #include "gasyncresult.h" #include "gsimpleasyncresult.h" +#include "glibintl.h" + /** * SECTION:gdbusproxy * @short_description: Base class for proxies diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 9ac7da8ff..a3baa2244 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -24,8 +24,6 @@ #include -#include - #include "gdbusutils.h" #include "gdbusnamewatching.h" #include "gdbusproxywatching.h" @@ -35,6 +33,8 @@ #include "gdbusnamewatching.h" #include "gcancellable.h" +#include "glibintl.h" + /** * SECTION:gdbusproxywatching * @title: Watching Proxies diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index bee5c90a0..fda660af2 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -23,14 +23,9 @@ #include "config.h" #include -#include -#include +#include #include -#ifdef G_OS_UNIX -#include -#endif - #include "giotypes.h" #include "gdbusaddress.h" #include "gdbusutils.h" @@ -40,6 +35,15 @@ #include "gdbusprivate.h" #include "gdbusauthobserver.h" #include "gio-marshal.h" +#include "ginitable.h" +#include "gsocketservice.h" + +#ifdef G_OS_UNIX +#include +#include "gunixsocketaddress.h" +#endif + +#include "glibintl.h" /** * SECTION:gdbusserver diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c index 08770c6d6..265ec2ab5 100644 --- a/gio/gdbusutils.c +++ b/gio/gdbusutils.c @@ -25,10 +25,10 @@ #include #include -#include - #include "gdbusutils.h" +#include "glibintl.h" + /** * SECTION:gdbusutils * @title: D-Bus Utilities diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index 156a124ea..2be5c5d8d 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -31,8 +31,6 @@ #include "config.h" -#include - /* ---------------------------------------------------------------------------------------------------- */ #ifdef __linux__ @@ -58,6 +56,8 @@ #include "gunixcredentialsmessage.h" #include "gcredentials.h" +#include "glibintl.h" + struct _GUnixCredentialsMessagePrivate { GCredentials *credentials; From 46ce134d516f4a246996328c980efe16195ab429 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 17:31:51 -0400 Subject: [PATCH 05/76] GDBus: Add new symbols to gio.symbols --- docs/reference/gio/gio-sections.txt | 6 +- docs/reference/gio/gio.types | 2 +- gio/gcredentials.c | 4 + gio/gdbusaddress.c | 4 + gio/gdbusauth.c | 4 + gio/gdbusauthmechanism.c | 4 + gio/gdbusauthmechanismanon.c | 4 + gio/gdbusauthmechanismexternal.c | 4 + gio/gdbusauthmechanismsha1.c | 4 + gio/gdbusauthobserver.c | 4 + gio/gdbusconnection.c | 10 +- gio/gdbuserror.c | 4 + gio/gdbusintrospection.c | 5 + gio/gdbusmessage.c | 20 +- gio/gdbusmessage.h | 8 +- gio/gdbusmethodinvocation.c | 4 + gio/gdbusnameowning.c | 4 + gio/gdbusnamewatching.c | 4 + gio/gdbusprivate.c | 4 + gio/gdbusproxy.c | 4 + gio/gdbusproxywatching.c | 4 + gio/gdbusserver.c | 4 + gio/gdbusutils.c | 4 + gio/gio.symbols | 299 +++++++++++++++++++++++ gio/gunixcredentialsmessage.c | 4 + gio/tests/gdbus-example-unix-fd-client.c | 2 +- gio/tests/gdbus-peer.c | 2 +- gio/tests/gdbus-serialization.c | 2 +- 28 files changed, 405 insertions(+), 23 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 06d81999a..5fbeaeb33 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2300,8 +2300,8 @@ g_dbus_message_new_method_error g_dbus_message_new_method_error_valist g_dbus_message_new_method_error_literal g_dbus_message_print -g_dbus_message_get_type -g_dbus_message_set_type +g_dbus_message_get_message_type +g_dbus_message_set_message_type g_dbus_message_get_serial g_dbus_message_set_serial g_dbus_message_get_flags @@ -2338,7 +2338,7 @@ g_dbus_message_to_gerror G_DBUS_MESSAGE G_IS_DBUS_MESSAGE G_TYPE_DBUS_MESSAGE -g_dbus_message_get_gtype +g_dbus_message_get_type G_DBUS_MESSAGE_CLASS G_IS_DBUS_MESSAGE_CLASS G_DBUS_MESSAGE_GET_CLASS diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index e1d65a931..38ffb556f 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -107,7 +107,7 @@ g_volume_monitor_get_type g_zlib_compressor_get_type g_zlib_compressor_format_get_type g_zlib_decompressor_get_type -g_dbus_message_get_gtype +g_dbus_message_get_type g_dbus_connection_get_type g_bus_type_get_type g_bus_name_owner_flags_get_type diff --git a/gio/gcredentials.c b/gio/gcredentials.c index 7a572251b..6adef3919 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -34,6 +34,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gcredentials @@ -458,3 +459,6 @@ g_credentials_set_windows_user (GCredentials *credentials, } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_CREDENTIALS_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 8379b046e..692c551ea 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -36,6 +36,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusaddress @@ -1015,3 +1016,6 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, out: return ret; } + +#define __G_DBUS_ADDRESS_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index 2cfb74ee2..e983f8df5 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -43,6 +43,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" #define DEBUG_ENABLED 1 @@ -1347,3 +1348,6 @@ _g_dbus_auth_run_server (GDBusAuth *auth, } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_AUTH_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c index a62b26775..23d315959 100644 --- a/gio/gdbusauthmechanism.c +++ b/gio/gdbusauthmechanism.c @@ -29,6 +29,7 @@ #include "giostream.h" #include "glibintl.h" +#include "gioalias.h" /* ---------------------------------------------------------------------------------------------------- */ @@ -340,3 +341,6 @@ _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism) } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_AUTH_MECHANISM_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusauthmechanismanon.c b/gio/gdbusauthmechanismanon.c index c5a0f3c7f..fd6597d63 100644 --- a/gio/gdbusauthmechanismanon.c +++ b/gio/gdbusauthmechanismanon.c @@ -27,6 +27,7 @@ #include "gioenumtypes.h" #include "glibintl.h" +#include "gioalias.h" struct _GDBusAuthMechanismAnonPrivate { @@ -325,3 +326,6 @@ mechanism_client_shutdown (GDBusAuthMechanism *mechanism) } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_AUTH_MECHANISM_ANON_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c index 3307e85ed..0bc94c138 100644 --- a/gio/gdbusauthmechanismexternal.c +++ b/gio/gdbusauthmechanismexternal.c @@ -35,6 +35,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" struct _GDBusAuthMechanismExternalPrivate { @@ -416,3 +417,6 @@ mechanism_client_shutdown (GDBusAuthMechanism *mechanism) } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_AUTH_MECHANISM_EXTERNAL_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c index bc9ed2596..e36c9f782 100644 --- a/gio/gdbusauthmechanismsha1.c +++ b/gio/gdbusauthmechanismsha1.c @@ -41,6 +41,7 @@ #include #include "glibintl.h" +#include "gioalias.h" struct _GDBusAuthMechanismSha1Private { @@ -1216,3 +1217,6 @@ mechanism_client_shutdown (GDBusAuthMechanism *mechanism) } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_AUTH_MECHANISM_SHA1_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index 54d335ffd..f32605844 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -29,6 +29,7 @@ #include "giostream.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusauthobserver @@ -222,3 +223,6 @@ g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer, } + +#define __G_DBUS_AUTH_OBSERVER_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 835291e66..24dbeac90 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -77,6 +77,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusconnection @@ -1568,7 +1569,7 @@ on_worker_message_received (GDBusWorker *worker, { GDBusMessageType message_type; - message_type = g_dbus_message_get_type (message); + message_type = g_dbus_message_get_message_type (message); if (message_type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN || message_type == G_DBUS_MESSAGE_TYPE_ERROR) { guint32 reply_serial; @@ -4152,7 +4153,7 @@ decode_method_reply (GDBusMessage *reply, GError **error) GVariant *result; result = NULL; - switch (g_dbus_message_get_type (reply)) + switch (g_dbus_message_get_message_type (reply)) { case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: result = g_dbus_message_get_body (reply); @@ -5024,7 +5025,7 @@ distribute_method_call (GDBusConnection *connection, gchar *subtree_path; gchar *needle; - g_assert (g_dbus_message_get_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL); + g_assert (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL); interface_name = g_dbus_message_get_interface (message); member = g_dbus_message_get_member (message); @@ -5365,3 +5366,6 @@ g_bus_get_finish (GAsyncResult *res, } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_CONNECTION_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c index 0bfd9067c..675fbcb00 100644 --- a/gio/gdbuserror.c +++ b/gio/gdbuserror.c @@ -32,6 +32,7 @@ #include "gdbusprivate.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbuserror @@ -866,3 +867,6 @@ g_dbus_error_encode_gerror (const GError *error) return error_name; } + +#define __G_DBUS_ERROR_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index b128d9f87..1f13f861b 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -28,6 +28,7 @@ #include "gdbusintrospection.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusintrospection @@ -60,6 +61,7 @@ _MY_DEFINE_BOXED_TYPE (GDBusNodeInfo, g_dbus_node_info); _MY_DEFINE_BOXED_TYPE (GDBusInterfaceInfo, g_dbus_interface_info); _MY_DEFINE_BOXED_TYPE (GDBusMethodInfo, g_dbus_method_info); +_MY_DEFINE_BOXED_TYPE (GDBusSignalInfo, g_dbus_signal_info); _MY_DEFINE_BOXED_TYPE (GDBusPropertyInfo, g_dbus_property_info); _MY_DEFINE_BOXED_TYPE (GDBusArgInfo, g_dbus_arg_info); _MY_DEFINE_BOXED_TYPE (GDBusAnnotationInfo, g_dbus_annotation_info); @@ -2052,3 +2054,6 @@ g_dbus_node_info_lookup_interface (const GDBusNodeInfo *node_info, out: return result; } + +#define __G_DBUS_INTROSPECTION_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 839ef55a5..557e2b674 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -45,6 +45,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusmessage @@ -68,9 +69,7 @@ struct _GDBusMessagePrivate #endif }; -#define g_dbus_message_get_type g_dbus_message_get_gtype G_DEFINE_TYPE (GDBusMessage, g_dbus_message, G_TYPE_OBJECT); -#undef g_dbus_message_get_type static void g_dbus_message_finalize (GObject *object) @@ -222,7 +221,7 @@ g_dbus_message_new_method_reply (GDBusMessage *method_call_message) const gchar *sender; g_return_val_if_fail (G_IS_DBUS_MESSAGE (method_call_message), NULL); - g_return_val_if_fail (g_dbus_message_get_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL); + g_return_val_if_fail (g_dbus_message_get_message_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL); g_return_val_if_fail (g_dbus_message_get_serial (method_call_message) != 0, NULL); message = g_dbus_message_new (); @@ -292,7 +291,7 @@ g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message, const gchar *sender; g_return_val_if_fail (G_IS_DBUS_MESSAGE (method_call_message), NULL); - g_return_val_if_fail (g_dbus_message_get_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL); + g_return_val_if_fail (g_dbus_message_get_message_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL); g_return_val_if_fail (g_dbus_message_get_serial (method_call_message) != 0, NULL); g_return_val_if_fail (g_dbus_is_name (error_name), NULL); g_return_val_if_fail (error_message != NULL, NULL); @@ -347,7 +346,7 @@ g_dbus_message_new_method_error_valist (GDBusMessage *method_call_me /* TODO: need GI annotations to specify that any guchar value goes for the type */ /** - * g_dbus_message_get_type: + * g_dbus_message_get_message_type: * @message: A #GDBusMessage. * * Gets the type of @message. @@ -357,14 +356,14 @@ g_dbus_message_new_method_error_valist (GDBusMessage *method_call_me * Since: 2.26 */ GDBusMessageType -g_dbus_message_get_type (GDBusMessage *message) +g_dbus_message_get_message_type (GDBusMessage *message) { g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), G_DBUS_MESSAGE_TYPE_INVALID); return message->priv->type; } /** - * g_dbus_message_set_type: + * g_dbus_message_set_message_type: * @message: A #GDBusMessage. * @type: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration). * @@ -373,8 +372,8 @@ g_dbus_message_get_type (GDBusMessage *message) * Since: 2.26 */ void -g_dbus_message_set_type (GDBusMessage *message, - GDBusMessageType type) +g_dbus_message_set_message_type (GDBusMessage *message, + GDBusMessageType type) { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); g_return_if_fail (type >=0 && type < 256); @@ -2508,3 +2507,6 @@ g_dbus_message_print (GDBusMessage *message, return g_string_free (str, FALSE); } + +#define __G_DBUS_MESSAGE_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h index 747345d80..16b2a6c94 100644 --- a/gio/gdbusmessage.h +++ b/gio/gdbusmessage.h @@ -31,7 +31,7 @@ G_BEGIN_DECLS -#define G_TYPE_DBUS_MESSAGE (g_dbus_message_get_gtype ()) +#define G_TYPE_DBUS_MESSAGE (g_dbus_message_get_type ()) #define G_DBUS_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_MESSAGE, GDBusMessage)) #define G_DBUS_MESSAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_MESSAGE, GDBusMessageClass)) #define G_DBUS_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_MESSAGE, GDBusMessageClass)) @@ -69,7 +69,7 @@ struct _GDBusMessage GDBusMessagePrivate *priv; }; -GType g_dbus_message_get_gtype (void) G_GNUC_CONST; +GType g_dbus_message_get_type (void) G_GNUC_CONST; GDBusMessage *g_dbus_message_new (void); GDBusMessage *g_dbus_message_new_signal (const gchar *path, const gchar *interface, @@ -93,8 +93,8 @@ GDBusMessage *g_dbus_message_new_method_error_literal (GDBusMessage gchar *g_dbus_message_print (GDBusMessage *message, guint indent); -GDBusMessageType g_dbus_message_get_type (GDBusMessage *message); -void g_dbus_message_set_type (GDBusMessage *message, +GDBusMessageType g_dbus_message_get_message_type (GDBusMessage *message); +void g_dbus_message_set_message_type (GDBusMessage *message, GDBusMessageType type); GDBusMessageFlags g_dbus_message_get_flags (GDBusMessage *message); void g_dbus_message_set_flags (GDBusMessage *message, diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index b2f99ef1b..4ff9b1807 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -33,6 +33,7 @@ #include "gdbusprivate.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusmethodinvocation @@ -844,3 +845,6 @@ g_dbus_method_invocation_return_dbus_error (GDBusMethodInvocation *invocation, g_object_unref (invocation); } + +#define __G_DBUS_METHOD_INVOCATION_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index e9474298a..3199d79db 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -31,6 +31,7 @@ #include "gdbusconnection.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusnameowning @@ -717,3 +718,6 @@ g_bus_unown_name (guint owner_id) client_unref (client); } } + +#define __G_DBUS_NAME_OWNING_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index a68255bfb..875e7295e 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -32,6 +32,7 @@ #include "gdbusconnection.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusnamewatching @@ -623,3 +624,6 @@ g_bus_unwatch_name (guint watcher_id) client_unref (client); } } + +#define __G_DBUS_NAME_WATCHING_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 64ea7d95e..20c6129f9 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -51,6 +51,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" /* ---------------------------------------------------------------------------------------------------- */ @@ -1050,3 +1051,6 @@ _g_dbus_compute_complete_signature (GDBusArgInfo **args, return g_string_free (s, FALSE); } + +#define __G_DBUS_PRIVATE_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index ded62c96c..fcb817300 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -41,6 +41,7 @@ #include "gsimpleasyncresult.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusproxy @@ -1595,3 +1596,6 @@ g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_PROXY_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index a3baa2244..28384042b 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -34,6 +34,7 @@ #include "gcancellable.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusproxywatching @@ -399,3 +400,6 @@ g_bus_unwatch_proxy (guint watcher_id) client_unref (client); } } + +#define __G_DBUS_PROXY_WATCHING_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index fda660af2..9d276596a 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -44,6 +44,7 @@ #endif #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusserver @@ -1073,3 +1074,6 @@ initable_iface_init (GInitableIface *initable_iface) } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_SERVER_C__ +#include "gioaliasdef.c" diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c index 265ec2ab5..d75081420 100644 --- a/gio/gdbusutils.c +++ b/gio/gdbusutils.c @@ -28,6 +28,7 @@ #include "gdbusutils.h" #include "glibintl.h" +#include "gioalias.h" /** * SECTION:gdbusutils @@ -375,3 +376,6 @@ g_dbus_is_activated (void) } /* ---------------------------------------------------------------------------------------------------- */ + +#define __G_DBUS_UTILS_C__ +#include "gioaliasdef.c" diff --git a/gio/gio.symbols b/gio/gio.symbols index 8c278fa77..daf71a0eb 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -987,6 +987,20 @@ g_unix_socket_address_type_get_type G_GNUC_CONST g_resolver_error_get_type G_GNUC_CONST g_zlib_compressor_format_get_type g_settings_bind_flags_get_type +g_dbus_error_get_type G_GNUC_CONST +g_bus_type_get_type G_GNUC_CONST +g_bus_name_owner_flags_get_type G_GNUC_CONST +g_bus_name_watcher_flags_get_type G_GNUC_CONST +g_dbus_proxy_flags_get_type G_GNUC_CONST +g_dbus_connection_flags_get_type G_GNUC_CONST +g_dbus_capability_flags_get_type G_GNUC_CONST +g_dbus_invoke_method_flags_get_type G_GNUC_CONST +g_dbus_message_type_get_type G_GNUC_CONST +g_dbus_message_flags_get_type G_GNUC_CONST +g_dbus_message_header_field_get_type G_GNUC_CONST +g_dbus_property_info_flags_get_type G_GNUC_CONST +g_dbus_subtree_flags_get_type G_GNUC_CONST +g_dbus_server_flags_get_type G_GNUC_CONST #endif #endif @@ -1326,6 +1340,8 @@ g_tcp_connection_get_graceful_disconnect g_unix_connection_get_type G_GNUC_CONST g_unix_connection_receive_fd g_unix_connection_send_fd +g_unix_connection_receive_credentials +g_unix_connection_send_credentials #endif #endif #endif @@ -1430,3 +1446,286 @@ g_settings_get_boolean g_settings_set_boolean #endif #endif + +#if IN_HEADER(__G_CREDENTIALS_H__) +#if IN_FILE(__G_CREDENTIALS_C__) +g_credentials_get_type G_GNUC_CONST +g_credentials_new +g_credentials_new_for_process +g_credentials_new_for_string +g_credentials_to_string +g_credentials_get_unix_group +g_credentials_get_unix_process +g_credentials_get_unix_user +g_credentials_get_windows_user +g_credentials_has_unix_group +g_credentials_has_unix_process +g_credentials_has_unix_user +g_credentials_has_windows_user +g_credentials_set_unix_group +g_credentials_set_unix_process +g_credentials_set_unix_user +g_credentials_set_windows_user +#endif +#endif + +#if IN_HEADER(__G_DBUS_ADDRESS_H__) +#if IN_FILE(__G_DBUS_ADDRESS_C__) +g_dbus_is_address +g_dbus_is_supported_address +g_dbus_address_get_for_bus_sync +g_dbus_address_get_stream +g_dbus_address_get_stream_finish +g_dbus_address_get_stream_sync +#endif +#endif + +#if IN_HEADER(__G_DBUS_AUTH_OBSERVER_H__) +#if IN_FILE(__G_DBUS_AUTH_OBSERVER_C__) +g_dbus_auth_observer_get_type G_GNUC_CONST +g_dbus_auth_observer_new +g_dbus_auth_observer_deny_authenticated_peer +#endif +#endif + +#if IN_HEADER(__G_DBUS_CONNECTION_H__) +#if IN_FILE(__G_DBUS_CONNECTION_C__) +g_dbus_connection_get_type G_GNUC_CONST +g_bus_get +g_bus_get_finish +g_bus_get_sync +g_dbus_connection_new +g_dbus_connection_new_finish +g_dbus_connection_new_for_address +g_dbus_connection_new_for_address_finish +g_dbus_connection_new_for_address_sync +g_dbus_connection_new_sync +g_dbus_connection_get_capabilities +g_dbus_connection_get_exit_on_close +g_dbus_connection_get_guid +g_dbus_connection_get_peer_credentials +g_dbus_connection_get_stream +g_dbus_connection_get_unique_name +g_dbus_connection_is_closed +g_dbus_connection_set_exit_on_close +g_dbus_connection_close +g_dbus_connection_emit_signal +g_dbus_connection_invoke_method +g_dbus_connection_invoke_method_finish +g_dbus_connection_invoke_method_sync +g_dbus_connection_signal_subscribe +g_dbus_connection_signal_unsubscribe +g_dbus_connection_add_filter +g_dbus_connection_remove_filter +g_dbus_connection_send_message +g_dbus_connection_send_message_with_reply +g_dbus_connection_send_message_with_reply_finish +g_dbus_connection_send_message_with_reply_sync +g_dbus_connection_register_object +g_dbus_connection_unregister_object +g_dbus_connection_register_subtree +g_dbus_connection_unregister_subtree +#endif +#endif + +#if IN_HEADER(__G_DBUS_ERROR_H__) +#if IN_FILE(__G_DBUS_ERROR_C__) +g_dbus_error_quark +g_dbus_error_new_for_dbus_error +g_dbus_error_is_remote_error +g_dbus_error_get_remote_error +g_dbus_error_strip_remote_error +g_dbus_error_encode_gerror +g_dbus_error_register_error +g_dbus_error_register_error_domain +g_dbus_error_set_dbus_error +g_dbus_error_set_dbus_error_valist +g_dbus_error_unregister_error +#endif +#endif + +#if IN_HEADER(__G_DBUS_INTROSPECTION_H__) +#if IN_FILE(__G_DBUS_INTROSPECTION_C__) +g_dbus_annotation_info_get_type G_GNUC_CONST +g_dbus_arg_info_get_type G_GNUC_CONST +g_dbus_property_info_get_type G_GNUC_CONST +g_dbus_interface_info_get_type G_GNUC_CONST +g_dbus_method_info_get_type G_GNUC_CONST +g_dbus_signal_info_get_type G_GNUC_CONST +g_dbus_node_info_get_type G_GNUC_CONST +g_dbus_annotation_info_lookup +g_dbus_annotation_info_ref +g_dbus_annotation_info_unref +g_dbus_interface_info_generate_xml +g_dbus_interface_info_lookup_method +g_dbus_interface_info_lookup_property +g_dbus_interface_info_lookup_signal +g_dbus_node_info_new_for_xml +g_dbus_node_info_generate_xml +g_dbus_node_info_lookup_interface +g_dbus_arg_info_ref +g_dbus_arg_info_unref +g_dbus_property_info_ref +g_dbus_property_info_unref +g_dbus_signal_info_ref +g_dbus_signal_info_unref +g_dbus_method_info_ref +g_dbus_method_info_unref +g_dbus_interface_info_ref +g_dbus_interface_info_unref +g_dbus_node_info_ref +g_dbus_node_info_unref +#endif +#endif + +#if IN_HEADER(__G_DBUS_MESSAGE_H__) +#if IN_FILE(__G_DBUS_MESSAGE_C__) +g_dbus_message_get_type G_GNUC_CONST +g_dbus_message_new +g_dbus_message_new_from_blob +g_dbus_message_new_method_call +g_dbus_message_new_method_error +g_dbus_message_new_method_error_literal +g_dbus_message_new_method_error_valist +g_dbus_message_new_method_reply +g_dbus_message_new_signal +g_dbus_message_bytes_needed +g_dbus_message_get_arg0 +g_dbus_message_get_body +g_dbus_message_get_destination +g_dbus_message_get_error_name +g_dbus_message_get_flags +g_dbus_message_get_header +g_dbus_message_get_header_fields +g_dbus_message_get_interface +g_dbus_message_get_member +g_dbus_message_get_num_unix_fds +g_dbus_message_get_path +g_dbus_message_get_reply_serial +g_dbus_message_get_sender +g_dbus_message_get_serial +g_dbus_message_get_signature +g_dbus_message_get_message_type +g_dbus_message_get_unix_fd_list +g_dbus_message_print +g_dbus_message_set_body +g_dbus_message_set_destination +g_dbus_message_set_error_name +g_dbus_message_set_flags +g_dbus_message_set_header +g_dbus_message_set_interface +g_dbus_message_set_member +g_dbus_message_set_num_unix_fds +g_dbus_message_set_path +g_dbus_message_set_reply_serial +g_dbus_message_set_sender +g_dbus_message_set_serial +g_dbus_message_set_signature +g_dbus_message_set_message_type +g_dbus_message_set_unix_fd_list +g_dbus_message_to_blob +g_dbus_message_to_gerror +#endif +#endif + +#if IN_HEADER(__G_DBUS_METHOD_INVOCATION_H__) +#if IN_FILE(__G_DBUS_METHOD_INVOCATION_C__) +g_dbus_method_invocation_get_type G_GNUC_CONST +g_dbus_method_invocation_new +g_dbus_method_invocation_get_connection +g_dbus_method_invocation_get_interface_name +g_dbus_method_invocation_get_message +g_dbus_method_invocation_get_method_info +g_dbus_method_invocation_get_method_name +g_dbus_method_invocation_get_object_path +g_dbus_method_invocation_get_parameters +g_dbus_method_invocation_get_sender +g_dbus_method_invocation_get_user_data +g_dbus_method_invocation_return_dbus_error +g_dbus_method_invocation_return_error +g_dbus_method_invocation_return_error_literal +g_dbus_method_invocation_return_error_valist +g_dbus_method_invocation_return_gerror +g_dbus_method_invocation_return_value +#endif +#endif + +#if IN_HEADER(__G_DBUS_NAME_OWNING_H__) +#if IN_FILE(__G_DBUS_NAME_OWNING_C__) +g_bus_own_name +g_bus_own_name_on_connection +g_bus_unown_name +#endif +#endif + +#if IN_HEADER(__G_DBUS_NAME_WATCHING_H__) +#if IN_FILE(__G_DBUS_NAME_WATCHING_C__) +g_bus_watch_name +g_bus_unwatch_name +#endif +#endif + +#if IN_HEADER(__G_DBUS_PROXY_H__) +#if IN_FILE(__G_DBUS_PROXY_C__) +g_dbus_proxy_get_type G_GNUC_CONST +g_dbus_proxy_new +g_dbus_proxy_new_finish +g_dbus_proxy_new_sync +g_dbus_proxy_get_cached_property +g_dbus_proxy_get_cached_property_names +g_dbus_proxy_get_connection +g_dbus_proxy_get_default_timeout +g_dbus_proxy_get_flags +g_dbus_proxy_get_interface_info +g_dbus_proxy_get_interface_name +g_dbus_proxy_get_object_path +g_dbus_proxy_get_unique_bus_name +g_dbus_proxy_set_default_timeout +g_dbus_proxy_set_interface_info +g_dbus_proxy_invoke_method +g_dbus_proxy_invoke_method_finish +g_dbus_proxy_invoke_method_sync +#endif +#endif + +#if IN_HEADER(__G_DBUS_PROXY_WATCHING_H__) +#if IN_FILE(__G_DBUS_PROXY_WATCHING_C__) +g_bus_watch_proxy +g_bus_unwatch_proxy +#endif +#endif + +#if IN_HEADER(__G_DBUS_SERVER_H__) +#if IN_FILE(__G_DBUS_SERVER_C__) +g_dbus_server_get_type G_GNUC_CONST +g_dbus_server_new_sync +g_dbus_server_is_active +g_dbus_server_start +g_dbus_server_stop +g_dbus_server_get_client_address +g_dbus_server_get_flags +g_dbus_server_get_guid +#endif +#endif + +#if IN_HEADER(__G_DBUS_UTILS_H__) +#if IN_FILE(__G_DBUS_UTILS_C__) +g_dbus_generate_guid +g_dbus_is_activated +g_dbus_is_guid +g_dbus_is_interface_name +g_dbus_is_member_name +g_dbus_is_name +g_dbus_is_unique_name +#endif +#endif + +#if IN_HEADER(__G_UNIX_CREDENTIALS_MESSAGE_H__) +#if IN_FILE(__G_UNIX_CREDENTIALS_MESSAGE_C__) +g_unix_credentials_message_get_type G_GNUC_CONST +g_unix_credentials_message_new +g_unix_credentials_message_new_with_credentials +g_unix_credentials_message_get_credentials +g_unix_credentials_message_is_supported +#endif +#endif diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index 2be5c5d8d..87c3da57e 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -57,6 +57,7 @@ #include "gcredentials.h" #include "glibintl.h" +#include "gioalias.h" struct _GUnixCredentialsMessagePrivate { @@ -343,3 +344,6 @@ g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message) return message->priv->credentials; } + +#define __G_UNIX_CREDENTIALS_MESSAGE_C__ +#include "gioaliasdef.c" diff --git a/gio/tests/gdbus-example-unix-fd-client.c b/gio/tests/gdbus-example-unix-fd-client.c index 6a66431c3..b89da8cb3 100644 --- a/gio/tests/gdbus-example-unix-fd-client.c +++ b/gio/tests/gdbus-example-unix-fd-client.c @@ -49,7 +49,7 @@ get_server_stdout (GDBusConnection *connection, if (method_reply_message == NULL) goto out; - if (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_ERROR) + if (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_ERROR) { g_dbus_message_to_gerror (method_reply_message, error); goto out; diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index ae6d68dd4..50b9a492b 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -593,7 +593,7 @@ test_peer (void) NULL, /* cancellable */ &error); g_assert_no_error (error); - g_assert (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); + g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); g_assert (fd_list != NULL); g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 91b79f81b..782ddacdb 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -509,7 +509,7 @@ check_serialization (GVariant *value, message = g_dbus_message_new (); g_dbus_message_set_body (message, value); - g_dbus_message_set_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL); + g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL); g_dbus_message_set_serial (message, 0x41); s = get_body_signature (value); g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar")); From 44fd23b649ebe2f8f6d31e78400d3230b2c38366 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 17:41:31 -0400 Subject: [PATCH 06/76] GDBus: Add more symbols to pltcheck.sh's SKIP variable In particular, add these symbols g_memdup g_print g_random_int g_propagate_prefixed_e g_thread_create_full g_int_hash g_file_open_tmp g_thread_self g_usleep --- gio/pltcheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gio/pltcheck.sh b/gio/pltcheck.sh index f0500bf88..a6cbe4825 100755 --- a/gio/pltcheck.sh +++ b/gio/pltcheck.sh @@ -9,7 +9,7 @@ if ! which readelf 2>/dev/null >/dev/null; then exit 0 fi -SKIP='\\|\ Date: Thu, 6 May 2010 17:52:54 -0400 Subject: [PATCH 07/76] Add a migration chapter for dbus bits Also split migration.xml into separate files per chapter, it was getting unwieldy. --- docs/reference/gio/Makefile.am | 10 +- docs/reference/gio/gio-docs.xml | 8 +- docs/reference/gio/migrating-dbus-glib.xml | 7 + docs/reference/gio/migrating-gconf.xml | 418 ++++++++++++++++++ docs/reference/gio/migrating-gnome-vfs.xml | 133 ++++++ .../{migrating.xml => migrating-posix.xml} | 0 6 files changed, 573 insertions(+), 3 deletions(-) create mode 100644 docs/reference/gio/migrating-dbus-glib.xml create mode 100644 docs/reference/gio/migrating-gconf.xml create mode 100644 docs/reference/gio/migrating-gnome-vfs.xml rename docs/reference/gio/{migrating.xml => migrating-posix.xml} (100%) diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am index 3d667e122..4e77a2578 100644 --- a/docs/reference/gio/Makefile.am +++ b/docs/reference/gio/Makefile.am @@ -114,7 +114,10 @@ HTML_IMAGES = \ content_files = \ version.xml \ overview.xml \ - migrating.xml \ + migrating-posix.xml \ + migrating-gnome-vfs.xml \ + migrating-gconf.xml \ + migrating-dbus-glib.xml \ gio-querymodules.xml \ glib-compile-schemas.xml\ gsettings.xml \ @@ -122,7 +125,10 @@ content_files = \ expand_content_files = \ overview.xml \ - migrating.xml + migrating-posix.xml \ + migrating-gnome-vfs.xml \ + migrating-gconf.xml \ + migrating-dbus-glib.xml extra_files = \ version.xml.in \ diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 748955b0e..7a0e4ae2c 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -169,7 +169,13 @@ - + + Migrating to GIO + + + + + Object Hierarchy diff --git a/docs/reference/gio/migrating-dbus-glib.xml b/docs/reference/gio/migrating-dbus-glib.xml new file mode 100644 index 000000000..2dda37ec9 --- /dev/null +++ b/docs/reference/gio/migrating-dbus-glib.xml @@ -0,0 +1,7 @@ + + Migrating from dbus-glib to GDBus + + + Hints for migrating from dbus-glib to GDBus will appear here shortly... + + diff --git a/docs/reference/gio/migrating-gconf.xml b/docs/reference/gio/migrating-gconf.xml new file mode 100644 index 000000000..23c2ddb55 --- /dev/null +++ b/docs/reference/gio/migrating-gconf.xml @@ -0,0 +1,418 @@ + + Migrating from GConf to GSettings + +
+ Before you start + + + Converting individual applications and their settings from GConf to + GSettings can be done at will. But desktop-wide settings like font or + theme settings often have consumers in multiple modules. Therefore, + some consideration has to go into making sure that all users of a setting + are converted to GSettings at the same time or that the program + responsible for configuring that setting continues to update the value in + both places. + + + It is always a good idea to have a look at how others have handled + similar problems before. An examplaric conversion can be found e.g. + in the gsettings-tutorial branch of gnome-utils. + +
+ +
+ Conceptual differences + + + Conceptually, GConf and GSettings are fairly similar. Both + have a concept of pluggable backends. Both keep information + about keys and their types in schemas. Both have a concept of + mandatory values, which lets you implement lock-down. + + + There are some differences in the approach to schemas. GConf + installs the schemas into the database and has API to handle + schema information (gconf_client_get_default_from_schema(), + gconf_value_get_schema(), etc). GSettings on the other hand + assumes that an application knows its own schemas, and does + not provide API to handle schema information at runtime. + GSettings is also more strict about requiring a schema whenever + you want to read or write a key. To deal with more free-form + information that would appear in schema-less entries in GConf, + GSettings allows for schemas to be 'relocatable'. + + + One difference in the way applications interact with their + settings is that with GConf you interact with a tree of + settings (ie the keys you pass to functions when reading + or writing values are actually paths with the actual name + of the key as the last element. With GSettings, you create + a GSettings object which has an implicit prefix that determines + where the settings get stored in the global tree of settings, + but the keys you pass when reading or writing values are just + the key names, not the full path. + +
+ +
+ GConfClient (and GConfBridge) API conversion + + + Most people use GConf via the high-level #GConfClient API. + The corresponding API is the #GSettings object. While not + every GConfClient function has a direct GSettings equivalent, + many do: + + + + GConfClientGSettings + + + gconf_client_get_default()no direct equivalent, + instead you call g_settings_new() for the schemas you use + gconf_client_set()g_settings_set() + gconf_client_get()g_settings_get() + gconf_client_get_bool()g_settings_get_boolean() + gconf_client_set_bool()g_settings_set_boolean() + gconf_client_get_int()g_settings_get_int() + gconf_client_set_int()g_settings_set_int() + gconf_client_get_float()g_settings_get_double() + gconf_client_set_float()g_settings_set_double() + gconf_client_get_string()g_settings_get_string() + gconf_client_set_string()g_settings_set_string() + gconf_client_get_list()for string lists, see g_settings_get_strv(), else see g_settings_get_value() and #GVariant API + gconf_client_set_list()for string lists, see g_settings_set_strv(), else see g_settings_set_value() and #GVariant API + gconf_entry_get_is_writable()g_settings_is_writable() + gconf_client_notify_add()not required, the #GSettings::changed signal is emitted automatically + gconf_client_add_dir()not required, each GSettings instance automatically watches all keys in its path + #GConfChangeSetg_settings_delay(), g_settings_apply() + gconf_client_get_default_from_schema()no equivalent, applications are expected to know their schema + gconf_client_all_entries()no equivalent, applications are expected to know their schema, and GSettings does not allow schema-less entries + gconf_client_get_without_default()no equivalent + gconf_bridge_bind_property()g_settings_bind() + gconf_bridge_bind_property_full()g_settings_bind_with_mapping() + + +
+
+ + GConfBridge was a third-party library that used GConf to bind an object property + to a particular configuration key. GSettings offers this service itself. + + + There is a pattern that is sometimes used for GConf, where a setting can have + explicit 'value A', explicit 'value B' or 'use the system default'. With GConf, + 'use the system default' is sometimes implemented by unsetting the user value. + + + This is not possible in GSettings, since it does not have API to determine if a value + is the default and does not let you unset values. The recommended way (and much + clearer) way in which this can be implemented in GSettings is to have a separate + 'use-system-default' boolean setting. + +
+ +
+ Change notification + + + GConf requires you to call gconf_client_add_dir() and + gconf_client_notify_add() to get change notification. With + GSettings, this is not necessary; signals get emitted automatically + for every change. + + + The #GSettings::changed signal is emitted for each changed key. + There is also a #GSettings::change-event signal that you can handle + if you need to see groups of keys that get changed at the same time. + + + GSettings also notifies you about changes in writability of keys, + with the #GSettings::writable-changed signal (and the + #GSettings::writable-change-event signal). + +
+ +
Change sets + + GConf has a a concept of a set of changes which can be applied or reverted + at once: #GConfChangeSet (GConf doesn't actually apply changes atomically, + which is one of its shortcomings). + + + Instead of a separate object to represent a change set, GSettings has a + 'delayed-apply' mode, which can be turned on for a GSettings object by + calling g_settings_delay(). In this mode, changes done to the GSettings + object are not applied - they are still visible when calling g_settings_get() + on the same object, but not to other GSettings instances + or even other processes. + + + To apply the pending changes all at once (GSettings does + atomicity here), call g_settings_apply(). To revert the pending changes, + call g_settings_revert() or just drop the reference to the #GSettings object. + +
+ +
+ Schema conversion + + + If you are porting your application from GConf, most likely you already + have a GConf schema. GIO comes with a commandline tool + gsettings-schema-convert + that can help with the task of converting a GConf schema into + an equivalent GSettings schema. The tool is not perfect and + may need assistence in some cases. + + An example for using gsettings-schema-convert + Running gsettings-schema-convert --gconf --xml --schema-id "org.gnome.font-rendering" --output org.gnome.font-rendering.gschema.xml destop_gnome_font_rendering.schemas on the following desktop_gnome_font_rendering.schemas file: + + + + + + /schemas/desktop/gnome/font_rendering/dpi + /desktop/gnome/font_rendering/dpi + gnome + int + 96 + + DPI + The resolution used for converting font sizes to pixel sizes, in dots per inch. + + + + +]]> + +produces a org.gnome.font-rendering.gschema.xml file with the following content: + + + + + 96 + DPI + The resolution used for converting font sizes to pixel sizes, in dots per inch. + + + +]]> + + + + + + GSettings schemas are identified at runtime by their id (as specified + in the XML source file). It is recommended to use a dotted name as schema + id, similar in style to a DBus bus name, e.g. "org.gnome.font-rendering". + The filename used for the XML schema source is immaterial, but + schema compiler expects the files to have the extension + .gschema.xml. It is recommended to simply + use the schema id as the filename, followed by this extension, + e.g. org.gnome.font-rendering.gschema.xml. + + + + The XML source file for your GSettings schema needs to get installed + into $datadir/glib-2.0/schemas, and needs to be + compiled into a binary form. At runtime, GSettings looks for compiled + schemas in the glib-2.0/schemas subdirectories + of all XDG_DATA_DIRS directories, so if you install + your schema in a different location, you need to set the + XDG_DATA_DIRS environment variable appropriately. + + + Schemas are compiled into binary form by the + glib-compile-schemas utility. + GIO provides a gschema_compile + variable for the schema compiler, which can be used in + configure.in as follows: + +GLIB_GSETTINGS + + The corresponding Makefile.am fragment looks like + this: + +# gsettingsschemadir and gschema_compile are defined by the GLIB_GSETTINGS +# macro in configure.ac +gsettingsschema_DATA = my.app.gschema.xml +# This rule will check your schemas for validity before installation +@GSETTINGS_CHECK_RULE@ +if GSETTINGS_SCHEMAS_INSTALL +install-data-hook: + $(GLIB_COMPILE_SCHEMAS) $(DESTDIR)$(gsettingsschemadir) +endif + + + + + One possible pitfall in doing schema conversion is that the default + values in GSettings schemas are parsed by the #GVariant parser. + This means that strings need to include quotes in the XML. Also note + that the types are now specified as #GVariant type strings. + +string +rgb +]]> + + becomes + + + 'rgb' + +]]> + + + + Another possible complication is that GConf specifies full paths + for each key, while a GSettings schema has a 'path' attribute that + contains the prefix for all the keys in the schema, and individual + keys just have a simple name. So + +/schemas/desktop/gnome/font_rendering/antialiasing +]]> + + becomes + + + +]]> + + + + Default values can be localized in both GConf and GSettings schemas, + but GSettings uses gettext for the localization. You can specify + the gettext domain to use in the gettext-domain + attribute. Therefore, when converting localized defaults in GConf, + +/schemas/apps/my_app/font_size + + 18 + + + 24 + + +]]> + + becomes + + + ... + + 18 + +]]> + + Note how we used the context attribute to add msgctxt - "18" is not a + good string to look up in gettext by itself. Also note that the value + 24 is not present in the schema anymore. It has to be added to the + gettext catalog for "be" instead. + + + GSettings schemas have optional summary and + description elements for each key which + correspond to the short and + long elements in the GConf schema and + will be used in similar ways by a future gsettings-editor, so you + should use the same conventions for them: The summary is just a short + label with no punctuation, the description can be one or more complete + sentences. Translations for these strings will also be handled + via gettext, so you should arrange for these strings to be + extracted into your gettext catalog. + + + GSettings is a bit more restrictive about key names than GConf. Key + names in GSettings can be at most 32 characters long, and must only + consist of lowercase characters, numbers and dashes, with no + consecutive dashes. The first character must not be a number or dash, + and the last character cannot be '-'. + + + If you are using the GConf backend for GSettings during the + transition, you may want to keep your key names the same they + were in GConf, so that existing settings in the users GConf + database are preserved. You can achieve this by using the + with the + glib-compile-schemas schema + compiler. Note that this option is only meant + to ease the process of porting your application, allowing parts + of your application to continue to access GConf and parts to use + GSettings. By the time you have finished porting your application + you must ensure that all key names are valid. + +
+ +
Data conversion + + GConf comes with a GSettings backend that can be used to + facility the transition to the GSettings API until you are + ready to make the jump to a different backend (most likely + dconf). To use it, you need to set the GSETTINGS_BACKEND + to 'gconf', e.g. by using + + g_setenv ("GSETTINGS_BACKEND", "gconf", TRUE); + + early on in your program. Note that this backend is meant purely + as a transition tool, and should not be used in production. + + + GConf also comes with a utility called + gsettings-data-convert, which is designed to help + with the task of migrating user settings from GConf into another + GSettings backend. It can be run manually, but it is designed to be + executed automatically, every time a user logs in. It keeps track of + the data migrations that it has already done, and it is harmless to + run it more than once. + + + To make use of this utility, you must install a keyfile in the + directory /usr/share/GConf/gsettings which + lists the GSettings keys and GConf paths to map to each other, for + each schema that you want to migrate user data for. + + + Here is an example: + + + + The last key demonstrates that it may be necessary to modify the key + name to comply with stricter GSettings key name rules. Of course, + that means your application must use the new key names when looking + up settings in GSettings. + + + The last group in the example also shows how to handle the case + of 'relocatable' schemas, which don't have a fixed path. You can + specify the path to use in the group name, separated by a colon. + + + There are some limitations: gsettings-data-convert + does not do any transformation of the values. And it does not handle + complex GConf types other than lists of strings or integers. + + + Don't forget to require GConf 2.31.1 or newer in your configure + script if you are making use of the GConf backend or the conversion + utility. + +
+
diff --git a/docs/reference/gio/migrating-gnome-vfs.xml b/docs/reference/gio/migrating-gnome-vfs.xml new file mode 100644 index 000000000..ba3987cad --- /dev/null +++ b/docs/reference/gio/migrating-gnome-vfs.xml @@ -0,0 +1,133 @@ + + Migrating from GnomeVFS to GIO + + + Comparison of GnomeVFS and GIO concepts + + + GnomeVFSGIO + + + GnomeVFSURIGFile + GnomeVFSFileInfoGFileInfo + GnomeVFSResultGError, with G_IO_ERROR values + GnomeVFSHandle & GnomeVFSAsyncHandleGInputStream or GOutputStream + GnomeVFSDirectoryHandleGFileEnumerator + mime typecontent type + GnomeVFSMonitorGFileMonitor + GnomeVFSVolumeMonitorGVolumeMonitor + GnomeVFSVolumeGMount + GnomeVFSDriveGVolume + -GDrive + GnomeVFSContextGCancellable + gnome_vfs_async_cancelg_cancellable_cancel + + +
+ +
+ Trash handling + + + The handling of trashed files has been changed in GIO, compared + to gnome-vfs. gnome-vfs has a home-grown trash implementation that + predates the freedesktop.org Desktop Trash Can specification + that is implemented in GIO. The location for storing trashed files + has changed from $HOME/.Trash to + $HOME/.local/share/Trash (or more correctly + $XDG_DATA_HOME/Trash), which means that + there is a need for migrating files that have been trashed by + gnome-vfs to the new location. + + + In gnome-vfs, the trash:// scheme offering a + merged view of all trash directories was implemented in nautilus, + and trash-handling applications had to find and monitor all trash + directories themselves. With GIO, the trash:// + implementation has been moved to gvfs and applications can simply + monitor that location: + + +static void +file_changed (GFileMonitor *file_monitor, + GFile *child, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + switch (event_type) + { + case G_FILE_MONITOR_EVENT_DELETED: + g_print ("'%s' removed from trash\n", g_file_get_basename (child)); + break; + case G_FILE_MONITOR_EVENT_CREATED: + g_print ("'%s' added to trash\n", g_file_get_basename (child)); + break; + default: ; + } +} + +static void +start_monitoring_trash (void) +{ + GFile *file; + GFileMonitor *monitor; + + file = g_file_new_for_uri ("trash://"); + monitor = g_file_monitor_directory (file, 0, NULL, NULL); + g_object_unref (file); + + g_signal_connect (monitor, "changed", G_CALLBACK (file_changed), NULL); + + /* ... */ + +} + + + GIO exposes some useful metadata about trashed files. There are + trash::orig-path and trash::deletion-date attributes. The + standard::icon attribute of the trash:// + itself provides a suitable icon for displaying the trash can on + the desktop. If you are using this icon, make sure to monitor + this attribute for changes, since the icon may be updated to + reflect that state of the trash can. + + + Moving a file to the trash is much simpler with GIO. Instead of + using gnome_vfs_find_directory() with %GNOME_VFS_DIRECTORY_KIND_TRASH + to find out where to move the trashed file, just use the g_file_trash() + function. + +
+ +
+ Operations on multiple files + + + gnome-vfs has the dreaded gnome_vfs_xfer_uri_list() function which + has tons of options and offers the equivalent of cp, mv, ln, mkdir + and rm at the same time. + + + GIO offers a much simpler I/O scheduler functionality instead, that + lets you schedule a function to be called in a separate thread, or + if threads are not available, as an idle in the mainloop. + See g_io_scheduler_push_job(). + + +
+ +
+ Mime monitoring + + + gnome-vfs offered a way to monitor the association between mime types + and default handlers for changes, with the #GnomeVFSMIMEMonitor object. + GIO does not offer a replacement for this functionality at this time, + since we have not found a compelling use case where + #GnomeVFSMIMEMonitor was used. If you think you have such a use + case, please report it at + bugzilla.gnome.org. + +
+
diff --git a/docs/reference/gio/migrating.xml b/docs/reference/gio/migrating-posix.xml similarity index 100% rename from docs/reference/gio/migrating.xml rename to docs/reference/gio/migrating-posix.xml From f14e30818c7d355f0c3d61bab2f2a702dc747952 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 6 May 2010 18:15:00 -0400 Subject: [PATCH 08/76] Mention D-Bus functionality in the overview --- docs/reference/gio/overview.xml | 39 +++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/docs/reference/gio/overview.xml b/docs/reference/gio/overview.xml index 613538ee0..1c14666b4 100644 --- a/docs/reference/gio/overview.xml +++ b/docs/reference/gio/overview.xml @@ -105,6 +105,31 @@ network connection stream + There is support for connecting to D-Bus, sending and receiving + messages, owning and watching bus names, and making objects + available on the bus: + + + GDBusConnection + a D-Bus connection + + + + GDBusMethodInvocation + for handling remove calls + + + + GDBusServer + helper for accepting connections + + + + GDBusProxy + proxy to access D-Bus interfaces on a remote object + + + Beyond these, GIO provides facilities for file monitoring, asynchronous I/O and filename completion. In addition to the interfaces, GIO provides implementations for the local case. @@ -254,8 +279,8 @@ This variable can be set to the name of a #GSettingsBackend implementation to override the default for debugging purposes. - The keyfile-based implementation that is included in GIO has - the name "keyfile", the one in dconf has the name "dconf-settings". + The memory-based implementation that is included in GIO has + the name "memory", the one in dconf has the name "dconf-settings". @@ -269,16 +294,6 @@ for compiled schemas for #GSettings. - - - <envar>GSETTINGS_KEYFILE_BACKEND_STORE</envar> - - - This variable can be set to the path where the keyfile #GSettings - backend stores its data. By default, the keyfile is stored in - $HOME/.config/gsettings/store. - -
From f309334bc629b9b148d9ce2887489930d4eedd3f Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 19:39:16 -0400 Subject: [PATCH 09/76] GDBus: add a man page for gdbus(1) --- docs/reference/gio/Makefile.am | 7 +- docs/reference/gio/gdbus.xml | 221 ++++++++++++++++++++++++++++++++ docs/reference/gio/gio-docs.xml | 1 + 3 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 docs/reference/gio/gdbus.xml diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am index 4e77a2578..23bcd90e0 100644 --- a/docs/reference/gio/Makefile.am +++ b/docs/reference/gio/Makefile.am @@ -121,7 +121,9 @@ content_files = \ gio-querymodules.xml \ glib-compile-schemas.xml\ gsettings.xml \ - gsettings-schema-convert.xml + gsettings-schema-convert.xml \ + gdbus.xml \ + $(NULL) expand_content_files = \ overview.xml \ @@ -143,7 +145,8 @@ man_MANS = \ gio-querymodules.1 \ glib-compile-schemas.1 \ gsettings.1 \ - gsettings-schema-convert.1 + gsettings-schema-convert.1 \ + gdbus.1 if ENABLE_MAN diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml new file mode 100644 index 000000000..4bd0eb746 --- /dev/null +++ b/docs/reference/gio/gdbus.xml @@ -0,0 +1,221 @@ + + + + gdbus + 1 + User Commands + + + + gdbus + Introspect and call remote objects + + + + + gdbus + introspect + + --system + --session + --address address + + --dest bus_name + --object-path /path/to/object + + + gdbus + call + + --system + --session + --address address + + --dest bus_name + --object-path /path/to/object + --method org.project.InterfaceName.MethodName + ARG1 + ARG2 + + + gdbus + help + + + + + Description + + gdbus offers a simple commandline utility for + introspecting and calling methods on remote objects. + + + Commands + + + + + Prints out interfaces and property values for a remote object. + For this to work, the owner of the object needs to implement the + org.freedesktop.DBus.Introspectable interface. + + + + + + Invokes a method on a remote object. Each argument to pass to the + method must be specified as a serialized + GVariant except that strings do + not need explicit quotes. The return values are printed out as + serialized GVariant + values. + + + + + + Prints help and exit. + + + + + + + + Bash Completion + + gdbus ships with a bash completion script to + complete commands, destinations, bus names, object paths and + interface/method names. + + + + + Examples + This shows how to introspect an object - note that the value of each + property is displayed: + +$ gdbus introspect --system \ + --dest org.freedesktop.NetworkManager \ + --object-path /org/freedesktop/NetworkManager/Devices/0 +node /org/freedesktop/NetworkManager/Devices/0 { + interface org.freedesktop.DBus.Introspectable { + methods: + Introspect(out s data); + }; + interface org.freedesktop.DBus.Properties { + methods: + Get(in s interface, + in s propname, + out v value); + Set(in s interface, + in s propname, + in v value); + GetAll(in s interface, + out a{sv} props); + }; + interface org.freedesktop.NetworkManager.Device.Wired { + signals: + PropertiesChanged(a{sv} arg_0); + properties: + readonly b Carrier = false; + readonly u Speed = 0; + readonly s HwAddress = '00:1D:72:88:BE:97'; + }; + interface org.freedesktop.NetworkManager.Device { + methods: + Disconnect(); + signals: + StateChanged(u arg_0, + u arg_1, + u arg_2); + properties: + readonly u DeviceType = 1; + readonly b Managed = true; + readwrite o Ip6Config = '/'; + readwrite o Dhcp4Config = '/'; + readwrite o Ip4Config = '/'; + readonly u State = 2; + readwrite u Ip4Address = 0; + readonly u Capabilities = 3; + readonly s Driver = 'e1000e'; + readwrite s Interface = 'eth0'; + readonly s Udi = '/sys/devices/pci0000:00/0000:00:19.0/net/eth0'; + }; +}; + + + In a similar fashion, the command can be + used to learn details about the Notify method: + + +[...] + interface org.freedesktop.Notifications { + methods: + GetServerInformation(out s return_name, + out s return_vendor, + out s return_version, + out s return_spec_version); + GetCapabilities(out as return_caps); + CloseNotification(in u id); + Notify(in s app_name, + in u id, + in s icon, + in s summary, + in s body, + in as actions, + in a{sv} hints, + in i timeout, + out u return_id); + }; +[...] + + + With this information, it's easy to use the + command to display a notification + + +$ gdbus call --session \ + --dest org.freedesktop.Notifications \ + --object-path /org/freedesktop/Notifications \ + --method org.freedesktop.Notifications.Notify \ + my_app_name \ + 42 \ + gtk-dialog-info \ + "The Summary" \ + "Here's the body of the notification" \ + [] \ + {} \ + 5000 +(uint32 12,) + + + + + AUTHOR + + Written by David Zeuthen zeuthen@gmail.com with + a lot of help from many others. + + + + + BUGS + + Please send bug reports to either the distribution bug tracker + or the upstream bug tracker at + . + + + + + SEE ALSO + + + dbus-send1 + + + + + + diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 7a0e4ae2c..2b9a7f232 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -166,6 +166,7 @@ + From 1ddda12d646f67fcb558399dce20ecff2405bf20 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 6 May 2010 20:54:04 -0400 Subject: [PATCH 10/76] A quick cheat sheet --- docs/reference/gio/migrating-dbus-glib.xml | 30 +++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/reference/gio/migrating-dbus-glib.xml b/docs/reference/gio/migrating-dbus-glib.xml index 2dda37ec9..a8863d18d 100644 --- a/docs/reference/gio/migrating-dbus-glib.xml +++ b/docs/reference/gio/migrating-dbus-glib.xml @@ -1,7 +1,35 @@ Migrating from dbus-glib to GDBus + + dbus-glib functions and their GDBus counterparts + + + dbus-glibGDBus + + + dbus_g_bus_get()g_bus_get_sync(), also see + g_bus_get() + dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see + g_dbus_proxy_new() + dbus_g_proxy_add_signal()not needed, use the generic #GDBusProxy::g-signal + dbus_g_proxy_connect_signal()use g_signal_connect() with #GDBusProxy::g-signal + dbus_g_connection_register_g_object()g_dbus_connection_register_object() + dbus_g_connection_unregister_g_object()g_dbus_connection_unregister_object() + dbus_g_object_type_install_info()introspection data is installed while registering + an object, see g_dbus_connection_register_object() + dbus_g_proxy_begin_call()g_dbus_proxy_invoke_method() + dbus_g_proxy_end_call()g_dbus_proxy_invoke_method_finish() + dbus_g_proxy_call()g_dbus_proxy_invoke_method_sync() + dbus_g_error_domain_register()g_dbus_error_register_error_domain() + dbus_g_error_has_name()no direct equivalent, see g_dbus_error_get_remote_error() + dbus_g_method_return()g_dbus_method_invocation_return_value() + dbus_g_method_return_error()g_dbus_method_invocation_return_error() and variants + dbus_g_method_get_sender()g_dbus_method_invocation_get_sender() + + +
- Hints for migrating from dbus-glib to GDBus will appear here shortly... + More hints for migrating from dbus-glib to GDBus will appear here shortly...
From 5134a1d151f14cfadfa72c23d0660f3928bda821 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 7 May 2010 14:36:07 -0400 Subject: [PATCH 11/76] GDBus: Document environment variables in "Running GIO applications" --- docs/reference/gio/overview.xml | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docs/reference/gio/overview.xml b/docs/reference/gio/overview.xml index 1c14666b4..5289bf3b3 100644 --- a/docs/reference/gio/overview.xml +++ b/docs/reference/gio/overview.xml @@ -294,6 +294,51 @@ for compiled schemas for #GSettings. + + + <envar>G_DBUS_DEBUG</envar> + + + This variable can be set to a list of debug options, which + cause GLib to print out different types of debugging + information when using the D-Bus routines. + + + message + Show all sent and received D-Bus messages + + + authentication + Information about authentication + + + The special value all can be used to turn on + all debug options. + + + + + <envar>G_DBUS_COOKIE_SHA1_KEYRING_DIR</envar> + + + Can be used to override the directory used to store the + keyring used in the DBUS_COOKIE_SHA1 + authentication mechanism. Normally the directory used is + .dbus-keyrings in the user's home + directory. + + + + + <envar>G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION</envar> + + + If set, the permissions of the directory used to store the + keyring used in the DBUS_COOKIE_SHA1 + authentication mechanism won't be checked. Normally the + directory must be readable only by the user. + + From 5bd876bef0235ec5c745ac948e906bf51adf2fef Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 7 May 2010 14:56:01 -0400 Subject: [PATCH 12/76] Add TODO item about wanting G_DBUS_NONCE_TCP_TMPDIR --- gio/gdbusconnection.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 24dbeac90..1612404bd 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -25,6 +25,11 @@ * * - would be nice to expose GDBusAuthMechanism and an extension point * + * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable + * to specify where the nonce is stored. This will allow people to use + * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir + * to easily acheive secure RPC via nonce-tcp. + * * - need to expose an extension point for resolving D-Bus address and * turning them into GIOStream objects. This will allow us to implement * e.g. X11 D-Bus transports without dlopen()'ing or linking against From 85c85ae63a95f4004020db56da6242b26ce073c0 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 7 May 2010 14:57:20 -0400 Subject: [PATCH 13/76] GDBus: add TODO item about the need to rewrite private GDBusAuth* classes --- gio/gdbusconnection.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 1612404bd..f4b18e3c7 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -25,6 +25,9 @@ * * - would be nice to expose GDBusAuthMechanism and an extension point * + * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular + * the mechanism VFuncs need to be able to set an error. + * * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable * to specify where the nonce is stored. This will allow people to use * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir From f1855c2f77fb76347c332b21865e5513f89b15b9 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 7 May 2010 15:02:37 -0400 Subject: [PATCH 14/76] GDBus: add TODO item about maybe having to rework ::g-properties-changed --- gio/gdbusconnection.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index f4b18e3c7..f09328d3a 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -28,6 +28,19 @@ * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular * the mechanism VFuncs need to be able to set an error. * + * - The GDBusProxy::g-properties-changed signal currently looks like this + * + * void user_function (GDBusProxy *proxy, + * GHashTable *changed_properties, + * gpointer user_data); + * + * which is problematic because some people frown upon GHashTable + * usage in public API (in particular some of the JS people). Maybe we + * need to rework it, maybe it doesn't matter since GDBusProxy is + * a low-level API and, for C code, we expect code generators to + * spit out subclasses that automatically hook up to this signal + * and does g_object_notify() anyway? Hmm... + * * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable * to specify where the nonce is stored. This will allow people to use * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir From 9164fd02c9043baee99199a3ba33c98e2309dc00 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 8 May 2010 20:10:57 -0400 Subject: [PATCH 15/76] Document length parameter of g_settings_get/set_strv This parameter was not mentioned in the doc comment, as pointed out in bug 617767. --- gio/gsettings.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gio/gsettings.c b/gio/gsettings.c index 801f57060..4878645da 100644 --- a/gio/gsettings.c +++ b/gio/gsettings.c @@ -1788,6 +1788,7 @@ g_settings_set_boolean (GSettings *settings, * g_settings_get_strv: * @settings: a #GSettings object * @key: the key to get the value for + * @length: return location for the length of the result, or %NULL * @returns: a newly-allocated, %NULL-terminated array of strings * * Gets the value that is stored at @key in @settings. @@ -1819,6 +1820,7 @@ g_settings_get_strv (GSettings *settings, * @settings: a #GSettings object * @key: the name of the key to set * @value: the value to set it to + * @length: the length of the @value array, or -1 * @returns: %TRUE if setting the key succeeded, * %FALSE if the key was not writable * From 8315eb77d57de73b950d152edbc7a104d378642c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 01:44:11 -0400 Subject: [PATCH 16/76] Some documentation tweaks Add links to the D-Bus docs in some places, and various other additions. --- docs/reference/gio/overview.xml | 6 +++--- gio/gdbusaddress.c | 4 +++- gio/gdbusconnection.c | 14 +++++++------- gio/gdbusintrospection.c | 6 +++++- gio/gdbusmethodinvocation.c | 4 ++++ gio/gdbusnameowning.c | 18 ++++++++++-------- gio/gdbusproxywatching.c | 2 +- 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/reference/gio/overview.xml b/docs/reference/gio/overview.xml index 5289bf3b3..6b3339d28 100644 --- a/docs/reference/gio/overview.xml +++ b/docs/reference/gio/overview.xml @@ -105,9 +105,9 @@ network connection stream - There is support for connecting to D-Bus, sending and receiving - messages, owning and watching bus names, and making objects - available on the bus: + There is support for connecting to D-Bus, + sending and receiving messages, owning and watching bus names, + and making objects available on the bus: GDBusConnection diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 692c551ea..923c97350 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -44,7 +44,9 @@ * @short_description: D-Bus connection endpoints * @include: gio/gio.h * - * Routines for working with D-Bus addresses. + * Routines for working with D-Bus addresses. A D-Bus address is a string + * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses + * is explained in detail in the D-Bus specification. */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index f09328d3a..4f72594bc 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -105,14 +105,14 @@ * @short_description: D-Bus Connections * @include: gio/gio.h * - * - * This class is rarely used directly in D-Bus clients. If you are - * writing an D-Bus client, it is often easier to use the - * g_bus_own_name(), g_bus_watch_name() or g_bus_watch_proxy() APIs. - * - * * The #GDBusConnection type is used for D-Bus connections to remote - * peers such as a message buses. + * peers such as a message buses. It is a low-level API that offers a + * lot of flexibility. For instance, it lets you establish a connection + * over any transport that can by represented as an #GIOStream. + * + * This class is rarely used directly in D-Bus clients. If you are writing + * an D-Bus client, it is often easier to use the g_bus_own_name(), + * g_bus_watch_name() or g_bus_watch_proxy() APIs. * * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT * diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index 1f13f861b..e1ae213b8 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -37,7 +37,11 @@ * @include: gio/gio.h * * Various data structures and convenience routines to parse and - * generate D-Bus introspection XML. + * generate D-Bus introspection XML. Introspection information is + * used when registering objects with g_dbus_connection_register_object(). + * + * The format of D-BUs introspection XML is specified in the + * D-Bus specification. */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index 4ff9b1807..4ef03b229 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -43,6 +43,10 @@ * Instances of the #GDBusMethodInvocation class are used when * handling D-Bus method calls. It provides a way to asynchronously * return results and errors. + * + * The normal way to obtain a #GDBusMethodInvocation object is to receive + * it as an argument to the handle_method_call() function in a + * #GDBusInterfaceVTable that was passed to g_dbus_connection_register_object(). */ struct _GDBusMethodInvocationPrivate diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index 3199d79db..064f8b61f 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -553,8 +553,9 @@ g_bus_own_name_on_connection (GDBusConnection *connection, * closes, then @name_lost_handler is invoked since it is no * longer possible for other processes to access the process. * - * You cannot use g_bus_own_name() several times (unless interleaved - * with calls to g_bus_unown_name()) - only the first call will work. + * You cannot use g_bus_own_name() several times for the same name (unless + * interleaved with calls to g_bus_unown_name()) - only the first call + * will work. * * Another guarantee is that invocations of @name_acquired_handler * and @name_lost_handler are guaranteed to alternate; that @@ -562,14 +563,15 @@ g_bus_own_name_on_connection (GDBusConnection *connection, * guaranteed that the next time one of the handlers is invoked, it * will be @name_lost_handler. The reverse is also true. * - * If you plan on exporting objects (using e.g. g_dbus_connection_register_object()), note - * that it is generally too late to export the objects in @name_acquired_handler. Instead, - * you can do this in @bus_acquired_handler since you are guaranteed that this will - * run before @name is requested from the bus. + * If you plan on exporting objects (using e.g. + * g_dbus_connection_register_object()), note that it is generally too late + * to export the objects in @name_acquired_handler. Instead, you can do this + * in @bus_acquired_handler since you are guaranteed that this will run + * before @name is requested from the bus. * * This behavior makes it very simple to write applications that wants - * to own names and export objects, see . Simply - * register objects to be exported in @bus_acquired_handler and + * to own names and export objects, see . + * Simply register objects to be exported in @bus_acquired_handler and * unregister the objects (if any) in @name_lost_handler. * * Returns: An identifier (never 0) that an be used with diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 28384042b..7f832c49d 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -291,7 +291,7 @@ on_name_vanished (GDBusConnection *connection, * additional proxies in response to the result of enumeration methods * on the manager object. * - * Many of the comment that applies to g_bus_watch_name() also applies + * Many of the comments that apply to g_bus_watch_name() also apply * here. For example, you are guaranteed that one of the handlers will * be invoked (on the main thread) after calling this function and * also that the two handlers alternate. When you are done watching the From bb7106c5dfce5597bcc4a0682200fb50f5201c29 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 02:27:09 -0400 Subject: [PATCH 17/76] Add some conceptual changes --- docs/reference/gio/migrating-dbus-glib.xml | 95 +++++++++++++++------- gio/gdbusaddress.c | 2 +- gio/gdbusintrospection.c | 2 +- 3 files changed, 66 insertions(+), 33 deletions(-) diff --git a/docs/reference/gio/migrating-dbus-glib.xml b/docs/reference/gio/migrating-dbus-glib.xml index a8863d18d..68202d53f 100644 --- a/docs/reference/gio/migrating-dbus-glib.xml +++ b/docs/reference/gio/migrating-dbus-glib.xml @@ -1,35 +1,68 @@ Migrating from dbus-glib to GDBus - - dbus-glib functions and their GDBus counterparts - - - dbus-glibGDBus - - - dbus_g_bus_get()g_bus_get_sync(), also see - g_bus_get() - dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see - g_dbus_proxy_new() - dbus_g_proxy_add_signal()not needed, use the generic #GDBusProxy::g-signal - dbus_g_proxy_connect_signal()use g_signal_connect() with #GDBusProxy::g-signal - dbus_g_connection_register_g_object()g_dbus_connection_register_object() - dbus_g_connection_unregister_g_object()g_dbus_connection_unregister_object() - dbus_g_object_type_install_info()introspection data is installed while registering - an object, see g_dbus_connection_register_object() - dbus_g_proxy_begin_call()g_dbus_proxy_invoke_method() - dbus_g_proxy_end_call()g_dbus_proxy_invoke_method_finish() - dbus_g_proxy_call()g_dbus_proxy_invoke_method_sync() - dbus_g_error_domain_register()g_dbus_error_register_error_domain() - dbus_g_error_has_name()no direct equivalent, see g_dbus_error_get_remote_error() - dbus_g_method_return()g_dbus_method_invocation_return_value() - dbus_g_method_return_error()g_dbus_method_invocation_return_error() and variants - dbus_g_method_get_sender()g_dbus_method_invocation_get_sender() - - -
- - More hints for migrating from dbus-glib to GDBus will appear here shortly... - +
+ Conceptual differences + + + The central concepts of D-Bus are modelled in a very similar way + in dbus-glib and GDBus. Both have a objects representing connections, + proxies and method invocations. But there are some important + differences: + + + dbus-glib uses libdbus, GDBus doesn't. Instead, it relies on GIO + streams as transport layer, and has its own implementation for the + the D-Bus connection setup and authentication. + + + dbus-glib uses the GObject type system for method arguments and + return values, including a homegrown container specialization + mechanism. GDBus relies uses the #GVariant type system which is + explicitly designed to match D-Bus types. + + + The typical way to export an object in dbus-glib involves generating + glue code from XML introspection data using dbus-binding-tool. GDBus does not (yet?) use code generation; you are expected to + embed the introspection data in your application code. + + + +
+ +
+ Dbus-glib API conversion + + + dbus-glib APIs and their GDBus counterparts + + + dbus-glibGDBus + + + #DBusGConnection#GDBusConnection + #DBusGProxy#GDBusProxy + #DBusGMethodInvocation#GDBusMethodInvocatoin + dbus_g_bus_get()g_bus_get_sync(), also see + g_bus_get() + dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see + g_dbus_proxy_new() + dbus_g_proxy_add_signal()not needed, use the generic #GDBusProxy::g-signal + dbus_g_proxy_connect_signal()use g_signal_connect() with #GDBusProxy::g-signal + dbus_g_connection_register_g_object()g_dbus_connection_register_object() + dbus_g_connection_unregister_g_object()g_dbus_connection_unregister_object() + dbus_g_object_type_install_info()introspection data is installed while registering + an object, see g_dbus_connection_register_object() + dbus_g_proxy_begin_call()g_dbus_proxy_invoke_method() + dbus_g_proxy_end_call()g_dbus_proxy_invoke_method_finish() + dbus_g_proxy_call()g_dbus_proxy_invoke_method_sync() + dbus_g_error_domain_register()g_dbus_error_register_error_domain() + dbus_g_error_has_name()no direct equivalent, see g_dbus_error_get_remote_error() + dbus_g_method_return()g_dbus_method_invocation_return_value() + dbus_g_method_return_error()g_dbus_method_invocation_return_error() and variants + dbus_g_method_get_sender()g_dbus_method_invocation_get_sender() + + +
+
diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 923c97350..8dcb0c9b9 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -46,7 +46,7 @@ * * Routines for working with D-Bus addresses. A D-Bus address is a string * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses - * is explained in detail in the D-Bus specification. + * is explained in detail in the D-Bus specification. */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index e1ae213b8..017209dae 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -41,7 +41,7 @@ * used when registering objects with g_dbus_connection_register_object(). * * The format of D-BUs introspection XML is specified in the - * D-Bus specification. + * D-Bus specification. */ /* ---------------------------------------------------------------------------------------------------- */ From c148cafdd16f8bfed46cf5b0af05af3cbdc550c7 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Sun, 9 May 2010 10:02:56 -0400 Subject: [PATCH 18/76] GDBus: Rework GCredentials type These changes are is related to https://bugzilla.gnome.org/show_bug.cgi?id=617483 and IRC discussions with danw. --- docs/reference/gio/gio-sections.txt | 15 +- gio/gcredentials.c | 487 +++++++++++----------------- gio/gcredentials.h | 40 +-- gio/gdbusauth.c | 2 +- gio/gdbusauthmechanismexternal.c | 19 +- gio/gio.symbols | 15 +- gio/gunixcredentialsmessage.c | 13 +- 7 files changed, 217 insertions(+), 374 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 5fbeaeb33..fe4d7bd55 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2198,21 +2198,12 @@ GCredentials GCredentialsClass GCredentialType g_credentials_new -g_credentials_new_for_process -g_credentials_new_for_string g_credentials_to_string -g_credentials_has_unix_user +g_credentials_get_native +g_credentials_set_native +g_credentials_is_same_user g_credentials_get_unix_user g_credentials_set_unix_user -g_credentials_has_unix_group -g_credentials_get_unix_group -g_credentials_set_unix_group -g_credentials_has_unix_process -g_credentials_get_unix_process -g_credentials_set_unix_process -g_credentials_has_windows_user -g_credentials_get_windows_user -g_credentials_set_windows_user G_CREDENTIALS G_IS_CREDENTIALS diff --git a/gio/gcredentials.c b/gio/gcredentials.c index 6adef3919..1aad3489f 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -28,9 +28,12 @@ #include "gcredentials.h" #include "gioerror.h" -#ifdef G_OS_UNIX +#ifdef __linux__ +#define __USE_GNU #include +#include #include +#include #endif #include "glibintl.h" @@ -41,21 +44,31 @@ * @short_description: An object containing credentials * @include: gio/gio.h * - * The #GCredentials type is used for storing information that can be - * used for identifying, authenticating and authorizing processes. + * The #GCredentials type is a reference-counted wrapper for the + * native credentials type. This information is typically used for + * identifying, authenticating and authorizing other processes. * - * Most UNIX and UNIX-like operating systems support a secure exchange - * of credentials over a Unix Domain Socket, see + * Some operating systems supports looking up the credentials of the + * remote peer of a communication endpoint - see e.g. + * g_socket_get_credentials(). + * + * Some operating systems supports securely sending and receiving + * credentials over a Unix Domain Socket, see * #GUnixCredentialsMessage, g_unix_connection_send_credentials() and * g_unix_connection_receive_credentials() for details. + * + * On Linux, the native credential type is a struct ucred - see + * the unix(7) man page for details. */ struct _GCredentialsPrivate { - gint64 unix_user; - gint64 unix_group; - gint64 unix_process; - gchar *windows_user; +#ifdef __linux__ + struct ucred native; +#else +#warning Please add GCredentials support for your OS + guint foo; +#endif }; G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT); @@ -63,9 +76,7 @@ G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT); static void g_credentials_finalize (GObject *object) { - GCredentials *credentials = G_CREDENTIALS (object); - - g_free (credentials->priv->windows_user); + G_GNUC_UNUSED GCredentials *credentials = G_CREDENTIALS (object); if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL) G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object); @@ -87,11 +98,11 @@ static void g_credentials_init (GCredentials *credentials) { credentials->priv = G_TYPE_INSTANCE_GET_PRIVATE (credentials, G_TYPE_CREDENTIALS, GCredentialsPrivate); - - credentials->priv->unix_user = -1; - credentials->priv->unix_group = -1; - credentials->priv->unix_process = -1; - credentials->priv->windows_user = NULL; +#ifdef __linux__ + credentials->priv->native.pid = getpid (); + credentials->priv->native.uid = getuid (); + credentials->priv->native.gid = getgid (); +#endif } /* ---------------------------------------------------------------------------------------------------- */ @@ -99,7 +110,8 @@ g_credentials_init (GCredentials *credentials) /** * g_credentials_new: * - * Creates a new empty credentials object. + * Creates a new #GCredentials object with credentials matching the + * the current process. * * Returns: A #GCredentials. Free with g_object_unref(). * @@ -113,111 +125,13 @@ g_credentials_new (void) /* ---------------------------------------------------------------------------------------------------- */ -#ifdef G_OS_UNIX -static GCredentials * -g_credentials_new_for_unix_process (void) -{ - GCredentials *credentials; - credentials = g_credentials_new (); - credentials->priv->unix_user = getuid (); - credentials->priv->unix_group = getgid (); - credentials->priv->unix_process = getpid (); - return credentials; -} -#endif - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_credentials_new_for_process: - * - * Gets the credentials for the current process. Note that the exact - * set of credentials in the returned object vary according to - * platform. - * - * Returns: A #GCredentials. Free with g_object_unref(). - * - * Since: 2.26 - */ -GCredentials * -g_credentials_new_for_process (void) -{ -#ifdef G_OS_UNIX - return g_credentials_new_for_unix_process (); -#elif G_OS_WIN32 - return g_credentials_new_for_win32_process (); -#else -#warning Please implement g_credentials_new_for_process() for your OS. For now g_credentials_new_for_process() will return empty credentials. - return g_credentials_new (); -#endif -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_credentials_new_for_string: - * @str: A string returned from g_credentials_to_string(). - * @error: Return location for error. - * - * Constructs a #GCredentials instance from @str. - * - * Returns: A #GCredentials or %NULL if @error is set. The return - * object must be freed with g_object_unref(). - * - * Since: 2.26 - */ -GCredentials * -g_credentials_new_for_string (const gchar *str, - GError **error) -{ - GCredentials *credentials; - gchar **tokens; - guint n; - - g_return_val_if_fail (str != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - tokens = NULL; - credentials = g_credentials_new (); - - if (!g_str_has_prefix (str, "GCredentials:")) - goto fail; - - tokens = g_strsplit (str + sizeof "GCredentials:" - 1, ",", 0); - for (n = 0; tokens[n] != NULL; n++) - { - const gchar *token = tokens[n]; - if (g_str_has_prefix (token, "unix-user:")) - g_credentials_set_unix_user (credentials, atoi (token + sizeof ("unix-user:") - 1)); - else if (g_str_has_prefix (token, "unix-group:")) - g_credentials_set_unix_group (credentials, atoi (token + sizeof ("unix-group:") - 1)); - else if (g_str_has_prefix (token, "unix-process:")) - g_credentials_set_unix_process (credentials, atoi (token + sizeof ("unix-process:") - 1)); - else if (g_str_has_prefix (token, "windows-user:")) - g_credentials_set_windows_user (credentials, token + sizeof ("windows-user:")); - else - goto fail; - } - g_strfreev (tokens); - return credentials; - - fail: - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("The string `%s' is not a valid credentials string"), - str); - g_object_unref (credentials); - g_strfreev (tokens); - return NULL; -} - /** * g_credentials_to_string: * @credentials: A #GCredentials object. * - * Serializes @credentials to a string that can be used with - * g_credentials_new_for_string(). + * Creates a human-readable textual representation of @credentials + * that can be used in logging and debug messages. The format of the + * returned string may change in future GLib release. * * Returns: A string that should be freed with g_free(). * @@ -231,16 +145,19 @@ g_credentials_to_string (GCredentials *credentials) g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL); ret = g_string_new ("GCredentials:"); - if (credentials->priv->unix_user != -1) - g_string_append_printf (ret, "unix-user=%" G_GINT64_FORMAT ",", credentials->priv->unix_user); - if (credentials->priv->unix_group != -1) - g_string_append_printf (ret, "unix-group=%" G_GINT64_FORMAT ",", credentials->priv->unix_group); - if (credentials->priv->unix_process != -1) - g_string_append_printf (ret, "unix-process=%" G_GINT64_FORMAT ",", credentials->priv->unix_process); - if (credentials->priv->windows_user != NULL) - g_string_append_printf (ret, "windows-user=%s,", credentials->priv->windows_user); +#ifdef __linux__ + g_string_append (ret, "linux:"); + if (credentials->priv->native.pid != -1) + g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->priv->native.pid); + if (credentials->priv->native.uid != -1) + g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->priv->native.uid); + if (credentials->priv->native.gid != -1) + g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->priv->native.gid); if (ret->str[ret->len - 1] == ',') ret->str[ret->len - 1] = '\0'; +#else + g_string_append (ret, "unknown"); +#endif return g_string_free (ret, FALSE); } @@ -248,217 +165,181 @@ g_credentials_to_string (GCredentials *credentials) /* ---------------------------------------------------------------------------------------------------- */ /** - * g_credentials_has_unix_user: + * g_credentials_is_same_user: * @credentials: A #GCredentials. + * @other_credentials: A #GCredentials. + * @error: Return location for error or %NULL. * - * Checks if @credentials has a UNIX user credential. + * Checks if @credentials and @other_credentials is the same user. * - * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + * This operation can fail if #GCredentials is not supported on the + * the OS. + * + * Returns: %TRUE if @credentials and @other_credentials has the same + * user, %FALSE otherwise or if @error is set. * * Since: 2.26 */ gboolean -g_credentials_has_unix_user (GCredentials *credentials) +g_credentials_is_same_user (GCredentials *credentials, + GCredentials *other_credentials, + GError **error) { + gboolean ret; + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); - return credentials->priv->unix_user != -1; + g_return_val_if_fail (G_IS_CREDENTIALS (other_credentials), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; +#ifdef __linux__ + if (credentials->priv->native.uid == other_credentials->priv->native.uid) + ret = TRUE; +#else + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("GCredentials is not implemented on this OS")); +#endif + + return ret; } /** - * g_credentials_get_unix_user: + * g_credentials_get_native: * @credentials: A #GCredentials. * - * Gets the UNIX user identifier from @credentials. + * Gets a pointer to the native credentials structure. * - * Returns: The identifier or -1 if unset. + * Returns: The pointer or %NULL if there is no #GCredentials support + * for the OS. Do not free the returned data, it is owned by + * @credentials. * * Since: 2.26 */ -gint64 -g_credentials_get_unix_user (GCredentials *credentials) +gpointer +g_credentials_get_native (GCredentials *credentials) { + gpointer ret; + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL); + +#ifdef __linux__ + ret = &credentials->priv->native; +#else + ret = NULL; +#endif + + return ret; +} + +/** + * g_credentials_set_native: + * @credentials: A #GCredentials. + * @native: A pointer to native credentials. + * + * Copies the native credentials from @native into @credentials. + * + * It is a programming error (which will cause an warning to be + * logged) to use this method if there is no #GCredentials support for + * the OS. + * + * Since: 2.26 + */ +void +g_credentials_set_native (GCredentials *credentials, + gpointer native) +{ +#ifdef __linux__ + memcpy (&credentials->priv->native, native, sizeof (struct ucred)); +#else + g_warning ("g_credentials_set_native: Trying to set credentials but GLib has no support " + "for the native credentials type. Please add support."); +#endif +} + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_UNIX +/** + * g_credentials_get_unix_user: + * @credentials: A #GCredentials + * @error: Return location for error or %NULL. + * + * Tries to get the UNIX user identifier from @credentials. This + * method is only available on UNIX platforms. + * + * This operation can fail if #GCredentials is not supported on the + * OS or if the native credentials type does not contain information + * about the UNIX user. + * + * Returns: The UNIX user identifier or -1 if @error is set. + * + * Since: 2.26 + */ +uid_t +g_credentials_get_unix_user (GCredentials *credentials, + GError **error) +{ + uid_t ret; + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1); - return credentials->priv->unix_user; + g_return_val_if_fail (error == NULL || *error == NULL, -1); + +#ifdef __linux__ + ret = credentials->priv->native.uid; +#else + ret = -1; + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("There no GCredentials support for your your platform")); +#endif + + return ret; } /** * g_credentials_set_unix_user: * @credentials: A #GCredentials. - * @user_id: A UNIX user identifier (typically type #uid_t) or -1 to unset it. + * @uid: The UNIX user identifier to set. + * @error: Return location for error or %NULL. * - * Sets the UNIX user identifier. + * Tries to set the UNIX user identifier on @credentials. This method + * is only available on UNIX platforms. * - * Since: 2.26 - */ -void -g_credentials_set_unix_user (GCredentials *credentials, - gint64 user_id) -{ - g_return_if_fail (G_IS_CREDENTIALS (credentials)); - credentials->priv->unix_user = user_id; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_credentials_has_unix_group: - * @credentials: A #GCredentials. + * This operation can fail if #GCredentials is not supported on the + * OS or if the native credentials type does not contain information + * about the UNIX user. * - * Checks if @credentials has a UNIX group credential. - * - * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. + * Returns: %TRUE if @uid was set, %FALSE if error is set. * * Since: 2.26 */ gboolean -g_credentials_has_unix_group (GCredentials *credentials) +g_credentials_set_unix_user (GCredentials *credentials, + uid_t uid, + GError **error) { + gboolean ret; + g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); - return credentials->priv->unix_group != -1; + g_return_val_if_fail (uid != -1, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; +#ifdef __linux__ + credentials->priv->native.uid = uid; + ret = TRUE; +#else + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("GCredentials is not implemented on this OS")); +#endif + + return ret; } - -/** - * g_credentials_get_unix_group: - * @credentials: A #GCredentials. - * - * Gets the UNIX group identifier from @credentials. - * - * Returns: The identifier or -1 if unset. - * - * Since: 2.26 - */ -gint64 -g_credentials_get_unix_group (GCredentials *credentials) -{ - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1); - return credentials->priv->unix_group; -} - -/** - * g_credentials_set_unix_group: - * @credentials: A #GCredentials. - * @group_id: A UNIX group identifier (typically type #gid_t) or -1 to unset. - * - * Sets the UNIX group identifier. - * - * Since: 2.26 - */ -void -g_credentials_set_unix_group (GCredentials *credentials, - gint64 group_id) -{ - g_return_if_fail (G_IS_CREDENTIALS (credentials)); - credentials->priv->unix_group = group_id; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_credentials_has_unix_process: - * @credentials: A #GCredentials. - * - * Checks if @credentials has a UNIX process credential. - * - * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. - * - * Since: 2.26 - */ -gboolean -g_credentials_has_unix_process (GCredentials *credentials) -{ - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); - return credentials->priv->unix_process != -1; -} - -/** - * g_credentials_get_unix_process: - * @credentials: A #GCredentials. - * - * Gets the UNIX process identifier from @credentials. - * - * Returns: The identifier or -1 if unset. - * - * Since: 2.26 - */ -gint64 -g_credentials_get_unix_process (GCredentials *credentials) -{ - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1); - return credentials->priv->unix_process; -} - -/** - * g_credentials_set_unix_process: - * @credentials: A #GCredentials. - * @process_id: A UNIX process identifier (typically type #pid_t/#GPid) or -1 to unset. - * - * Sets the UNIX process identifier. - * - * Since: 2.26 - */ -void -g_credentials_set_unix_process (GCredentials *credentials, - gint64 process_id) -{ - g_return_if_fail (G_IS_CREDENTIALS (credentials)); - credentials->priv->unix_process = process_id; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_credentials_has_windows_user: - * @credentials: A #GCredentials. - * - * Checks if @credentials has a Windows user SID (Security Identifier). - * - * Returns: %TRUE if @credentials has this type of credential, %FALSE otherwise. - * - * Since: 2.26 - */ -gboolean -g_credentials_has_windows_user (GCredentials *credentials) -{ - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); - return credentials->priv->windows_user != NULL; -} - -/** - * g_credentials_get_windows_user: - * @credentials: A #GCredentials. - * - * Gets the Windows User SID from @credentials. - * - * Returns: A string or %NULL if unset. Do not free, the string is owned by @credentials. - * - * Since: 2.26 - */ -const gchar * -g_credentials_get_windows_user (GCredentials *credentials) -{ - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL); - return credentials->priv->windows_user; -} - -/** - * g_credentials_set_windows_user: - * @credentials: A #GCredentials. - * @user_sid: The Windows User SID or %NULL to unset. - * - * Sets the Windows User SID. - * - * Since: 2.26 - */ -void -g_credentials_set_windows_user (GCredentials *credentials, - const gchar *user_sid) -{ - g_return_if_fail (G_IS_CREDENTIALS (credentials)); - g_free (credentials->priv->windows_user); - credentials->priv->windows_user = g_strdup (user_sid); -} - -/* ---------------------------------------------------------------------------------------------------- */ +#endif /* G_OS_UNIX */ #define __G_CREDENTIALS_C__ #include "gioaliasdef.c" diff --git a/gio/gcredentials.h b/gio/gcredentials.h index e26b0180c..1a33e0441 100644 --- a/gio/gcredentials.h +++ b/gio/gcredentials.h @@ -25,6 +25,12 @@ #include +#ifdef G_OS_UNIX +/* To get the uid_t type */ +#include +#include +#endif + G_BEGIN_DECLS #define G_TYPE_CREDENTIALS (g_credentials_get_type ()) @@ -78,30 +84,24 @@ struct _GCredentialsClass GType g_credentials_get_type (void) G_GNUC_CONST; GCredentials *g_credentials_new (void); -GCredentials *g_credentials_new_for_process (void); -GCredentials *g_credentials_new_for_string (const gchar *str, - GError **error); + gchar *g_credentials_to_string (GCredentials *credentials); -gboolean g_credentials_has_unix_user (GCredentials *credentials); -gint64 g_credentials_get_unix_user (GCredentials *credentials); -void g_credentials_set_unix_user (GCredentials *credentials, - gint64 user_id); +gpointer g_credentials_get_native (GCredentials *credentials); +void g_credentials_set_native (GCredentials *credentials, + gpointer native); -gboolean g_credentials_has_unix_group (GCredentials *credentials); -gint64 g_credentials_get_unix_group (GCredentials *credentials); -void g_credentials_set_unix_group (GCredentials *credentials, - gint64 group_id); +gboolean g_credentials_is_same_user (GCredentials *credentials, + GCredentials *other_credentials, + GError **error); -gboolean g_credentials_has_unix_process (GCredentials *credentials); -gint64 g_credentials_get_unix_process (GCredentials *credentials); -void g_credentials_set_unix_process (GCredentials *credentials, - gint64 process_id); - -gboolean g_credentials_has_windows_user (GCredentials *credentials); -const gchar *g_credentials_get_windows_user (GCredentials *credentials); -void g_credentials_set_windows_user (GCredentials *credentials, - const gchar *user_sid); +#ifdef G_OS_UNIX +uid_t g_credentials_get_unix_user (GCredentials *credentials, + GError **error); +gboolean g_credentials_set_unix_user (GCredentials *credentials, + uid_t uid, + GError **error); +#endif G_END_DECLS diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index e983f8df5..47905cd73 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -610,7 +610,7 @@ _g_dbus_auth_run_client (GDBusAuth *auth, #ifdef G_OS_UNIX if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ()) { - credentials = g_credentials_new_for_process (); + credentials = g_credentials_new (); if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream), credentials, cancellable, diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c index 0bc94c138..526023f07 100644 --- a/gio/gdbusauthmechanismexternal.c +++ b/gio/gdbusauthmechanismexternal.c @@ -211,27 +211,14 @@ data_matches_credentials (const gchar *data, alleged_uid = g_ascii_strtoll (data, &endp, 10); if (*endp == '\0') { - if (g_credentials_has_unix_user (credentials) && - g_credentials_get_unix_user (credentials) == alleged_uid) + if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid) { match = TRUE; } } } -#elif defined(G_OS_WIN32) - { - const gchar *alleged_sid; - - /* on Win32, this is the User SID */ - alleged_sid = data; - if (g_credentials_has_windows_user (credentials) && - g_strcmp0 (g_credentials_get_windows_user (credentials), alleged_sid) == 0) - { - match = TRUE; - } - } #else -#error Dont know how to read credentials on this OS. Please implement. + /* TODO: Dont know how to compare credentials on this OS. Please implement. */ #endif out: @@ -364,7 +351,7 @@ mechanism_client_initiate (GDBusAuthMechanism *mechanism, /* return the uid */ #if defined(G_OS_UNIX) - initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, g_credentials_get_unix_user (credentials)); + initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, g_credentials_get_unix_user (credentials, NULL)); #elif defined(G_OS_WIN32) initial_response = g_strdup_printf ("%s", g_credentials_get_windows_user ()); #else diff --git a/gio/gio.symbols b/gio/gio.symbols index daf71a0eb..14e4f745d 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1451,21 +1451,12 @@ g_settings_set_boolean #if IN_FILE(__G_CREDENTIALS_C__) g_credentials_get_type G_GNUC_CONST g_credentials_new -g_credentials_new_for_process -g_credentials_new_for_string g_credentials_to_string -g_credentials_get_unix_group -g_credentials_get_unix_process +g_credentials_get_native +g_credentials_set_native +g_credentials_is_same_user g_credentials_get_unix_user -g_credentials_get_windows_user -g_credentials_has_unix_group -g_credentials_has_unix_process -g_credentials_has_unix_user -g_credentials_has_windows_user -g_credentials_set_unix_group -g_credentials_set_unix_process g_credentials_set_unix_user -g_credentials_set_windows_user #endif #endif diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index 87c3da57e..13556e1bd 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -128,9 +128,7 @@ g_unix_credentials_message_deserialize (gint level, ucred = data; credentials = g_credentials_new (); - g_credentials_set_unix_user (credentials, ucred->uid); - g_credentials_set_unix_group (credentials, ucred->gid); - g_credentials_set_unix_process (credentials, ucred->pid); + g_credentials_set_native (credentials, ucred); message = g_unix_credentials_message_new_with_credentials (credentials); g_object_unref (credentials); out: @@ -147,12 +145,7 @@ g_unix_credentials_message_serialize (GSocketControlMessage *_message, { GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (_message); #ifdef __linux__ - { - struct ucred *ucred = data; - ucred->uid = g_credentials_get_unix_user (message->priv->credentials); - ucred->gid = g_credentials_get_unix_group (message->priv->credentials); - ucred->pid = g_credentials_get_unix_process (message->priv->credentials); - } + memcpy (data, g_credentials_get_native (message->priv->credentials), sizeof (struct ucred)); #endif } @@ -222,7 +215,7 @@ g_unix_credentials_message_constructed (GObject *object) GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object); if (message->priv->credentials == NULL) - message->priv->credentials = g_credentials_new_for_process (); + message->priv->credentials = g_credentials_new (); if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed != NULL) G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed (object); From b96c3b6d60a87f31a46ff5499c133571f0470940 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 12:24:56 -0400 Subject: [PATCH 19/76] Mention multithreading as a reason for not using libdbus --- docs/reference/gio/migrating-dbus-glib.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/reference/gio/migrating-dbus-glib.xml b/docs/reference/gio/migrating-dbus-glib.xml index 68202d53f..dc7aeaff1 100644 --- a/docs/reference/gio/migrating-dbus-glib.xml +++ b/docs/reference/gio/migrating-dbus-glib.xml @@ -13,7 +13,9 @@ dbus-glib uses libdbus, GDBus doesn't. Instead, it relies on GIO streams as transport layer, and has its own implementation for the - the D-Bus connection setup and authentication. + the D-Bus connection setup and authentication. Apart from using + streams as transport, avoiding libdbus also lets GDBus avoid some + thorny multithreading issues. dbus-glib uses the GObject type system for method arguments and From b87dd96a8a418f4f5915e4a1cb59737a009efdd8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 12:41:02 -0400 Subject: [PATCH 20/76] Move some platform sources around gunixcredentialsmessage.h ought to live with other UNIX headers, and the credentials are moved from dbus-specific to just GIO sources. Also move gfiledescriptorbased.c to the UNIX sources. --- gio/Makefile.am | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gio/Makefile.am b/gio/Makefile.am index 147974923..d051bf244 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -82,7 +82,6 @@ gio-marshal.c: gio-marshal.h gio-marshal.list gdbus_headers = \ gdbusauthobserver.h \ gcredentials.h \ - gunixcredentialsmessage.h \ gdbusutils.h \ gdbuserror.h \ gdbusaddress.h \ @@ -99,8 +98,6 @@ gdbus_headers = \ gdbus_sources = \ gdbusutils.h gdbusutils.c \ - gcredentials.h gcredentials.c \ - gunixcredentialsmessage.h gunixcredentialsmessage.c \ gdbusaddress.h gdbusaddress.c \ gdbusauthobserver.h gdbusauthobserver.c \ gdbusauth.h gdbusauth.c \ @@ -197,17 +194,18 @@ SUBDIRS += fam endif if OS_UNIX -appinfo_sources += gdesktopappinfo.c gdesktopappinfo.h +appinfo_sources += gdesktopappinfo.c platform_libadd += libasyncns/libasyncns.la xdgmime/libxdgmime.la platform_deps += libasyncns/libasyncns.la xdgmime/libxdgmime.la unix_sources = \ + gfiledescriptorbased.c \ gunixconnection.c \ + gunixcredentialsmessage.c \ gunixfdlist.c \ gunixfdmessage.c \ gunixmount.c \ gunixmount.h \ gunixmounts.c \ - gunixmounts.h \ gunixresolver.c \ gunixresolver.h \ gunixsocketaddress.c \ @@ -225,6 +223,7 @@ giounixinclude_HEADERS = \ gdesktopappinfo.h \ gfiledescriptorbased.h \ gunixconnection.h \ + gunixcredentialsmessage.h \ gunixmounts.h \ gunixfdlist.h \ gunixfdmessage.h \ @@ -282,6 +281,7 @@ libgio_2_0_la_SOURCES = \ gconverter.c \ gconverterinputstream.c \ gconverteroutputstream.c \ + gcredentials.c \ gdatainputstream.c \ gdataoutputstream.c \ gdrive.c \ @@ -294,8 +294,6 @@ libgio_2_0_la_SOURCES = \ gfile.c \ gfileattribute.c \ gfileattribute-priv.h \ - gfiledescriptorbased.h \ - gfiledescriptorbased.c \ gfileenumerator.c \ gfileicon.c \ gfileinfo.c \ From e82eea6fdae1a24b58bbd9a440c6c37bd2980afd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 13:09:54 -0400 Subject: [PATCH 21/76] Microoptimize string reallocations --- gio/gdbusconnection.c | 86 ++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 4f72594bc..98046ab9c 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3477,50 +3477,58 @@ handle_get_all_properties (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ +static const gchar introspect_header[] = + "\n" + "\n" + "\n"; + +static const gchar introspect_tail[] = + "\n"; + +static const gchar introspect_standard_interfaces[] = + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"; + static void introspect_append_header (GString *s) { - g_string_append (s, - "\n" - "\n" - "\n"); + g_string_append (s, introspect_header); } static void introspect_append_standard_interfaces (GString *s) { - g_string_append (s, - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n"); + g_string_append (s, introspect_standard_interfaces); } static void @@ -3624,7 +3632,9 @@ handle_introspect (GDBusConnection *connection, gchar **registered; /* first the header with the standard interfaces */ - s = g_string_new (NULL); + s = g_string_sized_new (sizeof (introspect_header) + + sizeof (introspect_standard_interfaces) + + sizeof (introspect_tail)); introspect_append_header (s); introspect_append_standard_interfaces (s); @@ -3642,7 +3652,7 @@ handle_introspect (GDBusConnection *connection, g_string_append_printf (s, " \n", registered[n]); } g_strfreev (registered); - g_string_append (s, "\n"); + g_string_append (s, introspect_tail); reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); From 0cf467c2ca92ece9625dbc54ad3065ad5298f735 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 13:14:55 -0400 Subject: [PATCH 22/76] Update copyright years to include 2010 --- gio/gcredentials.c | 2 +- gio/gcredentials.h | 2 +- gio/gdbus-tool.c | 2 +- gio/gdbusaddress.c | 2 +- gio/gdbusaddress.h | 2 +- gio/gdbusauth.c | 2 +- gio/gdbusauth.h | 2 +- gio/gdbusauthmechanism.c | 2 +- gio/gdbusauthmechanism.h | 2 +- gio/gdbusauthmechanismanon.c | 2 +- gio/gdbusauthmechanismanon.h | 2 +- gio/gdbusauthmechanismexternal.c | 2 +- gio/gdbusauthmechanismexternal.h | 2 +- gio/gdbusauthmechanismsha1.c | 2 +- gio/gdbusauthmechanismsha1.h | 2 +- gio/gdbusauthobserver.c | 2 +- gio/gdbusauthobserver.h | 2 +- gio/gdbusconnection.c | 2 +- gio/gdbusconnection.h | 2 +- gio/gdbuserror.c | 2 +- gio/gdbuserror.h | 2 +- gio/gdbusintrospection.c | 2 +- gio/gdbusintrospection.h | 2 +- gio/gdbusmessage.c | 2 +- gio/gdbusmessage.h | 2 +- gio/gdbusmethodinvocation.c | 2 +- gio/gdbusmethodinvocation.h | 2 +- gio/gdbusnameowning.c | 2 +- gio/gdbusnameowning.h | 2 +- gio/gdbusnamewatching.c | 2 +- gio/gdbusnamewatching.h | 2 +- gio/gdbusprivate.c | 2 +- gio/gdbusprivate.h | 2 +- gio/gdbusproxy.c | 2 +- gio/gdbusproxy.h | 2 +- gio/gdbusproxywatching.c | 2 +- gio/gdbusproxywatching.h | 2 +- gio/gdbusserver.c | 2 +- gio/gdbusserver.h | 2 +- gio/gdbusutils.c | 2 +- gio/gdbusutils.h | 2 +- 41 files changed, 41 insertions(+), 41 deletions(-) diff --git a/gio/gcredentials.c b/gio/gcredentials.c index 1aad3489f..feedbf781 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gcredentials.h b/gio/gcredentials.h index 1a33e0441..be1965f63 100644 --- a/gio/gcredentials.h +++ b/gio/gcredentials.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index 573b53d8a..1cf230657 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 8dcb0c9b9..96697126a 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusaddress.h b/gio/gdbusaddress.h index feac1a56e..389bd83dc 100644 --- a/gio/gdbusaddress.h +++ b/gio/gdbusaddress.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index 47905cd73..d1307bab6 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauth.h b/gio/gdbusauth.h index 2fc750017..039565932 100644 --- a/gio/gdbusauth.h +++ b/gio/gdbusauth.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c index 23d315959..6f2a56d1a 100644 --- a/gio/gdbusauthmechanism.c +++ b/gio/gdbusauthmechanism.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanism.h b/gio/gdbusauthmechanism.h index e00cb1753..fd46d71ca 100644 --- a/gio/gdbusauthmechanism.h +++ b/gio/gdbusauthmechanism.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanismanon.c b/gio/gdbusauthmechanismanon.c index fd6597d63..2ab3f3ba1 100644 --- a/gio/gdbusauthmechanismanon.c +++ b/gio/gdbusauthmechanismanon.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanismanon.h b/gio/gdbusauthmechanismanon.h index d3c2c2472..b770e20b3 100644 --- a/gio/gdbusauthmechanismanon.h +++ b/gio/gdbusauthmechanismanon.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c index 526023f07..cebbc2426 100644 --- a/gio/gdbusauthmechanismexternal.c +++ b/gio/gdbusauthmechanismexternal.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanismexternal.h b/gio/gdbusauthmechanismexternal.h index 39e7fa217..552dd2e71 100644 --- a/gio/gdbusauthmechanismexternal.h +++ b/gio/gdbusauthmechanismexternal.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c index e36c9f782..405698e00 100644 --- a/gio/gdbusauthmechanismsha1.c +++ b/gio/gdbusauthmechanismsha1.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthmechanismsha1.h b/gio/gdbusauthmechanismsha1.h index 67839fd11..762fc5abf 100644 --- a/gio/gdbusauthmechanismsha1.h +++ b/gio/gdbusauthmechanismsha1.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index f32605844..6883058b5 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusauthobserver.h b/gio/gdbusauthobserver.h index f3c88d020..ac3e234f4 100644 --- a/gio/gdbusauthobserver.h +++ b/gio/gdbusauthobserver.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 98046ab9c..06cc6e487 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index 5a2b278bd..f7bbdb7a4 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c index 675fbcb00..12cd0086b 100644 --- a/gio/gdbuserror.c +++ b/gio/gdbuserror.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbuserror.h b/gio/gdbuserror.h index 73c97aa83..145137917 100644 --- a/gio/gdbuserror.h +++ b/gio/gdbuserror.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index 017209dae..9ad46974c 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusintrospection.h b/gio/gdbusintrospection.h index 897041f3d..3560ff39f 100644 --- a/gio/gdbusintrospection.h +++ b/gio/gdbusintrospection.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 557e2b674..d2f02453d 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h index 16b2a6c94..c30abcfe3 100644 --- a/gio/gdbusmessage.h +++ b/gio/gdbusmessage.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index 4ef03b229..d264d31ec 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h index 2c6563c02..cb1d751fd 100644 --- a/gio/gdbusmethodinvocation.h +++ b/gio/gdbusmethodinvocation.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index 064f8b61f..2a4002788 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusnameowning.h b/gio/gdbusnameowning.h index 3a7518cf0..a1adc3f53 100644 --- a/gio/gdbusnameowning.h +++ b/gio/gdbusnameowning.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 875e7295e..4e33e8356 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusnamewatching.h b/gio/gdbusnamewatching.h index 28805ceac..1bbf8e0cd 100644 --- a/gio/gdbusnamewatching.h +++ b/gio/gdbusnamewatching.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 20c6129f9..4cdc480e2 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h index ef7fa0ac5..37cc03d56 100644 --- a/gio/gdbusprivate.h +++ b/gio/gdbusprivate.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index fcb817300..8d3926b70 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index 560d106f8..1b7a3a457 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 7f832c49d..7e900393c 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusproxywatching.h b/gio/gdbusproxywatching.h index 119311baf..2dddaffbf 100644 --- a/gio/gdbusproxywatching.h +++ b/gio/gdbusproxywatching.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index 9d276596a..0f3fa3632 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusserver.h b/gio/gdbusserver.h index dd1333f45..5822b9cf3 100644 --- a/gio/gdbusserver.h +++ b/gio/gdbusserver.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c index d75081420..fcaf3c3d5 100644 --- a/gio/gdbusutils.c +++ b/gio/gdbusutils.c @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/gdbusutils.h b/gio/gdbusutils.h index e3e606bb0..d6f345073 100644 --- a/gio/gdbusutils.h +++ b/gio/gdbusutils.h @@ -1,6 +1,6 @@ /* GDBus - GLib D-Bus Library * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 From 25a8aa5d88d3d4b8ebcf8be42a2adc233dbb104c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 9 May 2010 22:13:18 -0400 Subject: [PATCH 23/76] Cosmetic fixes Use P_() for properties, fix up indentation, etc. --- gio/gdbusauth.c | 4 +- gio/gdbusauthmechanism.c | 71 +++-- gio/gdbusconnection.c | 510 +++++++++++++++------------------- gio/gdbusmethodinvocation.c | 69 +++-- gio/gdbusproxy.c | 145 +++++----- gio/gdbusserver.c | 81 +++--- gio/gunixcredentialsmessage.c | 7 +- 7 files changed, 405 insertions(+), 482 deletions(-) diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index d1307bab6..3f7ccce4e 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -175,8 +175,8 @@ _g_dbus_auth_class_init (GDBusAuthClass *klass) g_object_class_install_property (gobject_class, PROP_STREAM, g_param_spec_object ("stream", - _("IO Stream"), - _("The underlying GIOStream used for I/O"), + P_("IO Stream"), + P_("The underlying GIOStream used for I/O"), G_TYPE_IO_STREAM, G_PARAM_READABLE | G_PARAM_WRITABLE | diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c index 6f2a56d1a..ede990372 100644 --- a/gio/gdbusauthmechanism.c +++ b/gio/gdbusauthmechanism.c @@ -60,8 +60,7 @@ _g_dbus_auth_mechanism_finalize (GObject *object) if (mechanism->priv->credentials != NULL) g_object_unref (mechanism->priv->credentials); - if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize != NULL) - G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize (object); + G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize (object); } static void @@ -127,8 +126,8 @@ _g_dbus_auth_mechanism_class_init (GDBusAuthMechanismClass *klass) g_object_class_install_property (gobject_class, PROP_STREAM, g_param_spec_object ("stream", - _("IO Stream"), - _("The underlying GIOStream used for I/O"), + P_("IO Stream"), + P_("The underlying GIOStream used for I/O"), G_TYPE_IO_STREAM, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -149,8 +148,8 @@ _g_dbus_auth_mechanism_class_init (GDBusAuthMechanismClass *klass) g_object_class_install_property (gobject_class, PROP_CREDENTIALS, g_param_spec_object ("credentials", - _("Credentials"), - _("The credentials of the remote peer"), + P_("Credentials"), + P_("The credentials of the remote peer"), G_TYPE_CREDENTIALS, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -193,7 +192,7 @@ _g_dbus_auth_mechanism_get_name (GType mechanism_type) const gchar *name; GDBusAuthMechanismClass *klass; - g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), NULL); + g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), NULL); klass = g_type_class_ref (mechanism_type); g_assert (klass != NULL); @@ -209,7 +208,7 @@ _g_dbus_auth_mechanism_get_priority (GType mechanism_type) gint priority; GDBusAuthMechanismClass *klass; - g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), 0); + g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), 0); klass = g_type_class_ref (mechanism_type); g_assert (klass != NULL); @@ -229,10 +228,10 @@ _g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism) } gchar * -_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism, - const gchar *data, - gsize data_len, - gsize *out_data_len) +_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->encode_data (mechanism, data, data_len, out_data_len); @@ -240,10 +239,10 @@ _g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism, gchar * -_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism, - const gchar *data, - gsize data_len, - gsize *out_data_len) +_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len, + gsize *out_data_len) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->decode_data (mechanism, data, data_len, out_data_len); @@ -252,47 +251,47 @@ _g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism, /* ---------------------------------------------------------------------------------------------------- */ GDBusAuthMechanismState -_g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism) +_g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_state (mechanism); } void -_g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism, - const gchar *initial_response, - gsize initial_response_len) +_g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism, + const gchar *initial_response, + gsize initial_response_len) { g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_initiate (mechanism, initial_response, initial_response_len); } void -_g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism, - const gchar *data, - gsize data_len) +_g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) { g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_receive (mechanism, data, data_len); } gchar * -_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism, - gsize *out_data_len) +_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_send (mechanism, out_data_len); } gchar * -_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) +_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_reject_reason (mechanism); } void -_g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism) +_g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism) { g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_shutdown (mechanism); @@ -301,15 +300,15 @@ _g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism) /* ---------------------------------------------------------------------------------------------------- */ GDBusAuthMechanismState -_g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism) +_g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism); } gchar * -_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism, - gsize *out_initial_response_len) +_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism, + gsize *out_initial_response_len) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism, @@ -317,24 +316,24 @@ _g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism, } void -_g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism, - const gchar *data, - gsize data_len) +_g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, + gsize data_len) { g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len); } gchar * -_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism, - gsize *out_data_len) +_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len) { g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len); } void -_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism) +_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism) { g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism)); G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism); diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 06cc6e487..8e7570e81 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -131,8 +131,8 @@ static GDBusConnection *the_system_bus = NULL; /* ---------------------------------------------------------------------------------------------------- */ static gboolean -_g_strv_has_string (const gchar* const * haystack, - const gchar *needle) +_g_strv_has_string (const gchar* const *haystack, + const gchar *needle) { guint n; @@ -303,7 +303,7 @@ static void purge_all_filters (GDBusConnection *connection); static guint signals[LAST_SIGNAL] = { 0 }; -static void initable_iface_init (GInitableIface *initable_iface); +static void initable_iface_init (GInitableIface *initable_iface); static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT, @@ -392,8 +392,7 @@ g_dbus_connection_finalize (GObject *object) g_mutex_free (connection->priv->init_lock); g_mutex_free (connection->priv->lock); - if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object); + G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object); } static void @@ -514,8 +513,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_STREAM, g_param_spec_object ("stream", - _("IO Stream"), - _("The underlying streams used for I/O"), + P_("IO Stream"), + P_("The underlying streams used for I/O"), G_TYPE_IO_STREAM, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -535,8 +534,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_ADDRESS, g_param_spec_string ("address", - _("Address"), - _("D-Bus address specifying potential socket endpoints"), + P_("Address"), + P_("D-Bus address specifying potential socket endpoints"), NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | @@ -554,8 +553,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_FLAGS, g_param_spec_flags ("flags", - _("Flags"), - _("Flags"), + P_("Flags"), + P_("Flags"), G_TYPE_DBUS_CONNECTION_FLAGS, G_DBUS_CONNECTION_FLAGS_NONE, G_PARAM_WRITABLE | @@ -586,8 +585,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_GUID, g_param_spec_string ("guid", - _("GUID"), - _("GUID of the server peer"), + P_("GUID"), + P_("GUID of the server peer"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -607,8 +606,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_UNIQUE_NAME, g_param_spec_string ("unique-name", - _("unique-name"), - _("Unique name of bus connection"), + P_("unique-name"), + P_("Unique name of bus connection"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME | @@ -625,8 +624,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_CLOSED, g_param_spec_boolean ("closed", - _("Closed"), - _("Whether the connection is closed"), + P_("Closed"), + P_("Whether the connection is closed"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NAME | @@ -645,8 +644,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_EXIT_ON_CLOSE, g_param_spec_boolean ("exit-on-close", - _("Exit on close"), - _("Whether the process is terminated when the connection is closed"), + P_("Exit on close"), + P_("Whether the process is terminated when the connection is closed"), FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -665,8 +664,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_CAPABILITY_FLAGS, g_param_spec_flags ("capabilities", - _("Capabilities"), - _("Capabilities"), + P_("Capabilities"), + P_("Capabilities"), G_TYPE_DBUS_CAPABILITY_FLAGS, G_DBUS_CAPABILITY_FLAGS_NONE, G_PARAM_READABLE | @@ -684,8 +683,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) g_object_class_install_property (gobject_class, PROP_AUTHENTICATION_OBSERVER, g_param_spec_object ("authentication-observer", - _("Authentication Observer"), - _("Object used to assist in the authentication process"), + P_("Authentication Observer"), + P_("Object used to assist in the authentication process"), G_TYPE_DBUS_AUTH_OBSERVER, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | @@ -860,8 +859,8 @@ emit_closed_in_idle (gpointer user_data) /* Can be called from any thread, must hold lock */ static void set_closed_unlocked (GDBusConnection *connection, - gboolean remote_peer_vanished, - GError *error) + gboolean remote_peer_vanished, + GError *error) { GSource *idle_source; EmitClosedData *data; @@ -908,29 +907,29 @@ g_dbus_connection_close (GDBusConnection *connection) CONNECTION_LOCK (connection); if (!connection->priv->closed) - { - GError *error = NULL; + { + GError *error = NULL; - /* TODO: do this async */ - //g_debug ("closing connection %p's stream %p", connection, connection->priv->stream); - if (!g_io_stream_close (connection->priv->stream, NULL, &error)) - { - g_warning ("Error closing stream: %s", error->message); - g_error_free (error); - } + /* TODO: do this async */ + //g_debug ("closing connection %p's stream %p", connection, connection->priv->stream); + if (!g_io_stream_close (connection->priv->stream, NULL, &error)) + { + g_warning ("Error closing stream: %s", error->message); + g_error_free (error); + } - set_closed_unlocked (connection, FALSE, NULL); - } + set_closed_unlocked (connection, FALSE, NULL); + } CONNECTION_UNLOCK (connection); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean -g_dbus_connection_send_message_unlocked (GDBusConnection *connection, - GDBusMessage *message, - volatile guint32 *out_serial, - GError **error) +g_dbus_connection_send_message_unlocked (GDBusConnection *connection, + GDBusMessage *message, + volatile guint32 *out_serial, + GError **error) { guchar *blob; gsize blob_size; @@ -991,9 +990,8 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, /* TODO: use connection->priv->auth to encode the blob */ if (out_serial != NULL) - { - *out_serial = serial_to_use; - } + *out_serial = serial_to_use; + g_dbus_message_set_serial (message, serial_to_use); _g_dbus_worker_send_message (connection->priv->worker, @@ -1038,10 +1036,10 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, * Since: 2.26 */ gboolean -g_dbus_connection_send_message (GDBusConnection *connection, - GDBusMessage *message, - volatile guint32 *out_serial, - GError **error) +g_dbus_connection_send_message (GDBusConnection *connection, + GDBusMessage *message, + volatile guint32 *out_serial, + GError **error) { gboolean ret; @@ -1398,9 +1396,9 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, * Since: 2.26 */ GDBusMessage * -g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, - GAsyncResult *res, - GError **error) +g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); GDBusMessage *reply; @@ -1487,12 +1485,12 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * Since: 2.26 */ GDBusMessage * -g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, - GDBusMessage *message, - gint timeout_msec, - volatile guint32 *out_serial, - GCancellable *cancellable, - GError **error) +g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, + GDBusMessage *message, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GError **error) { SendMessageSyncData *data; GDBusMessage *reply; @@ -1663,11 +1661,10 @@ get_offered_capabilities_max (GDBusConnection *connection) return ret; } - static gboolean -initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) { GDBusConnection *connection = G_DBUS_CONNECTION (initable); gboolean ret; @@ -1841,8 +1838,8 @@ initable_iface_init (GInitableIface *initable_iface) static void async_init_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) + GObject *object, + GCancellable *cancellable) { GError *error = NULL; @@ -1865,9 +1862,9 @@ async_initable_init_async (GAsyncInitable *initable, g_return_if_fail (G_IS_INITABLE (initable)); res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, - async_initable_init_async); + async_initable_init_async); g_simple_async_result_run_in_thread (res, async_init_thread, - io_priority, cancellable); + io_priority, cancellable); g_object_unref (res); } @@ -1919,13 +1916,13 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * Since: 2.26 */ void -g_dbus_connection_new (GIOStream *stream, - const gchar *guid, - GDBusConnectionFlags flags, - GDBusAuthObserver *authentication_observer, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_connection_new (GIOStream *stream, + const gchar *guid, + GDBusConnectionFlags flags, + GDBusAuthObserver *authentication_observer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_return_if_fail (G_IS_IO_STREAM (stream)); g_async_initable_new_async (G_TYPE_DBUS_CONNECTION, @@ -1952,8 +1949,8 @@ g_dbus_connection_new (GIOStream *stream, * Since: 2.26 */ GDBusConnection * -g_dbus_connection_new_finish (GAsyncResult *res, - GError **error) +g_dbus_connection_new_finish (GAsyncResult *res, + GError **error) { GObject *object; GObject *source_object; @@ -1997,12 +1994,12 @@ g_dbus_connection_new_finish (GAsyncResult *res, * Since: 2.26 */ GDBusConnection * -g_dbus_connection_new_sync (GIOStream *stream, - const gchar *guid, - GDBusConnectionFlags flags, - GDBusAuthObserver *authentication_observer, - GCancellable *cancellable, - GError **error) +g_dbus_connection_new_sync (GIOStream *stream, + const gchar *guid, + GDBusConnectionFlags flags, + GDBusAuthObserver *authentication_observer, + GCancellable *cancellable, + GError **error) { g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); @@ -2047,11 +2044,11 @@ g_dbus_connection_new_sync (GIOStream *stream, * Since: 2.26 */ void -g_dbus_connection_new_for_address (const gchar *address, - GDBusConnectionFlags flags, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_connection_new_for_address (const gchar *address, + GDBusConnectionFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_return_if_fail (address != NULL); g_async_initable_new_async (G_TYPE_DBUS_CONNECTION, @@ -2076,8 +2073,8 @@ g_dbus_connection_new_for_address (const gchar *address, * Since: 2.26 */ GDBusConnection * -g_dbus_connection_new_for_address_finish (GAsyncResult *res, - GError **error) +g_dbus_connection_new_for_address_finish (GAsyncResult *res, + GError **error) { GObject *object; GObject *source_object; @@ -2122,10 +2119,10 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, * Since: 2.26 */ GDBusConnection * -g_dbus_connection_new_for_address_sync (const gchar *address, - GDBusConnectionFlags flags, - GCancellable *cancellable, - GError **error) +g_dbus_connection_new_for_address_sync (const gchar *address, + GDBusConnectionFlags flags, + GCancellable *cancellable, + GError **error) { g_return_val_if_fail (address != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); @@ -2314,8 +2311,8 @@ purge_all_filters (GDBusConnection *connection) } void -g_dbus_connection_remove_filter (GDBusConnection *connection, - guint filter_id) +g_dbus_connection_remove_filter (GDBusConnection *connection, + guint filter_id) { guint n; FilterData *to_destroy; @@ -2385,11 +2382,11 @@ signal_data_free (SignalData *data) } static gchar * -args_to_rule (const gchar *sender, - const gchar *interface_name, - const gchar *member, - const gchar *object_path, - const gchar *arg0) +args_to_rule (const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0) { GString *rule; @@ -2474,10 +2471,10 @@ static gboolean is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) { return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 && - g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && - g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && - (g_strcmp0 (signal_data->member, "NameLost") == 0 || - g_strcmp0 (signal_data->member, "NameAcquired") == 0); + g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && + g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && + (g_strcmp0 (signal_data->member, "NameLost") == 0 || + g_strcmp0 (signal_data->member, "NameAcquired") == 0); } /* ---------------------------------------------------------------------------------------------------- */ @@ -2594,9 +2591,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { if (!is_signal_data_for_name_lost_or_acquired (signal_data)) - { - add_match_rule (connection, signal_data->rule); - } + add_match_rule (connection, signal_data->rule); } out: @@ -2624,9 +2619,9 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, /* must hold lock when calling this */ static void -unsubscribe_id_internal (GDBusConnection *connection, - guint subscription_id, - GArray *out_removed_subscribers) +unsubscribe_id_internal (GDBusConnection *connection, + guint subscription_id, + GArray *out_removed_subscribers) { SignalData *signal_data; GPtrArray *signal_data_array; @@ -2669,9 +2664,7 @@ unsubscribe_id_internal (GDBusConnection *connection, if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { if (!is_signal_data_for_name_lost_or_acquired (signal_data)) - { - remove_match_rule (connection, signal_data->rule); - } + remove_match_rule (connection, signal_data->rule); } signal_data_free (signal_data); @@ -2696,8 +2689,8 @@ unsubscribe_id_internal (GDBusConnection *connection, * Since: 2.26 */ void -g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, - guint subscription_id) +g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, + guint subscription_id) { GArray *subscribers; guint n; @@ -2783,16 +2776,15 @@ emit_signal_instance_in_idle_cb (gpointer data) CONNECTION_UNLOCK (signal_instance->connection); if (has_subscription) - { - signal_instance->callback (signal_instance->connection, - signal_instance->sender, - signal_instance->path, - signal_instance->interface, - signal_instance->member, - parameters, - signal_instance->user_data); - } - if (parameters != NULL) + signal_instance->callback (signal_instance->connection, + signal_instance->sender, + signal_instance->path, + signal_instance->interface, + signal_instance->member, + parameters, + signal_instance->user_data); + + if (parameters != NULL) g_variant_unref (parameters); return FALSE; @@ -2889,8 +2881,8 @@ schedule_callbacks (GDBusConnection *connection, /* called in message handler thread with lock held */ static void -distribute_signals (GDBusConnection *connection, - GDBusMessage *message) +distribute_signals (GDBusConnection *connection, + GDBusMessage *message) { GPtrArray *signal_data_array; const gchar *sender; @@ -2901,17 +2893,14 @@ distribute_signals (GDBusConnection *connection, if (sender != NULL) { signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, sender); - if (signal_data_array != NULL) { + if (signal_data_array != NULL) schedule_callbacks (connection, signal_data_array, message, sender); - } } /* collect subcsribers not matching on sender */ signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, ""); if (signal_data_array != NULL) - { - schedule_callbacks (connection, signal_data_array, message, sender); - } + schedule_callbacks (connection, signal_data_array, message, sender); } /* ---------------------------------------------------------------------------------------------------- */ @@ -2997,14 +2986,12 @@ static void exported_interface_free (ExportedInterface *ei) { if (ei->user_data_free_func != NULL) - { - /* TODO: push to thread-default mainloop */ - ei->user_data_free_func (ei->user_data); - } + /* TODO: push to thread-default mainloop */ + ei->user_data_free_func (ei->user_data); + if (ei->context != NULL) - { - g_main_context_unref (ei->context); - } + g_main_context_unref (ei->context); + g_free (ei->interface_name); g_free (ei); } @@ -3544,22 +3531,14 @@ maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHa end = strchr (begin, '/'); if (end != NULL) - { - s = g_strndup (begin, end - begin); - } + s = g_strndup (begin, end - begin); else - { - s = g_strdup (begin); - } + s = g_strdup (begin); if (g_hash_table_lookup (set, s) == NULL) - { - g_hash_table_insert (set, s, GUINT_TO_POINTER (1)); - } + g_hash_table_insert (set, s, GUINT_TO_POINTER (1)); else - { - g_free (s); - } + g_free (s); } } @@ -3596,9 +3575,7 @@ g_dbus_connection_list_registered_unlocked (GDBusConnection *connection, p = g_ptr_array_new (); keys = g_hash_table_get_keys (set); for (l = keys; l != NULL; l = l->next) - { - g_ptr_array_add (p, l->data); - } + g_ptr_array_add (p, l->data); g_hash_table_unref (set); g_list_free (keys); @@ -3641,16 +3618,12 @@ handle_introspect (GDBusConnection *connection, /* then include the registered interfaces */ g_hash_table_iter_init (&hash_iter, eo->map_if_name_to_ei); while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &ei)) - { - g_dbus_interface_info_generate_xml (ei->introspection_data, 2, s); - } + g_dbus_interface_info_generate_xml (ei->introspection_data, 2, s); /* finally include nodes registered below us */ registered = g_dbus_connection_list_registered_unlocked (connection, eo->object_path); for (n = 0; registered != NULL && registered[n] != NULL; n++) - { - g_string_append_printf (s, " \n", registered[n]); - } + g_string_append_printf (s, " \n", registered[n]); g_strfreev (registered); g_string_append (s, introspect_tail); @@ -3781,7 +3754,6 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, handled = TRUE; out: - return handled; } @@ -4008,9 +3980,7 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, ei = g_hash_table_lookup (connection->priv->map_id_to_ei, GUINT_TO_POINTER (registration_id)); if (ei == NULL) - { - goto out; - } + goto out; eo = ei->eo; @@ -4018,10 +3988,8 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, g_warn_if_fail (g_hash_table_remove (eo->map_if_name_to_ei, ei->interface_name)); /* unregister object path if we have no more exported interfaces */ if (g_hash_table_size (eo->map_if_name_to_ei) == 0) - { - g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_eo, - eo->object_path)); - } + g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_eo, + eo->object_path)); ret = TRUE; @@ -4052,13 +4020,13 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, * Since: 2.26 */ gboolean -g_dbus_connection_emit_signal (GDBusConnection *connection, - const gchar *destination_bus_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - GError **error) +g_dbus_connection_emit_signal (GDBusConnection *connection, + const gchar *destination_bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + GError **error) { GDBusMessage *message; gboolean ret; @@ -4078,11 +4046,9 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, signal_name); if (destination_bus_name != NULL) - { - g_dbus_message_set_header (message, - G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, - g_variant_new_string (destination_bus_name)); - } + g_dbus_message_set_header (message, + G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, + g_variant_new_string (destination_bus_name)); if (parameters != NULL) g_dbus_message_set_body (message, parameters); @@ -4094,8 +4060,8 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, } static void -add_invoke_method_flags (GDBusMessage *message, - GDBusInvokeMethodFlags flags) +add_invoke_method_flags (GDBusMessage *message, + GDBusInvokeMethodFlags flags) { if (flags & G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START) g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_AUTO_START); @@ -4136,17 +4102,17 @@ add_invoke_method_flags (GDBusMessage *message, * Since: 2.26 */ void -g_dbus_connection_invoke_method (GDBusConnection *connection, - const gchar *bus_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusInvokeMethodFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_connection_invoke_method (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GDBusMessage *message; @@ -4174,12 +4140,13 @@ g_dbus_connection_invoke_method (GDBusConnection *connection, callback, user_data); - if (message != NULL) + if (message != NULL) g_object_unref (message); } static GVariant * -decode_method_reply (GDBusMessage *reply, GError **error) +decode_method_reply (GDBusMessage *reply, + GError **error) { GVariant *result; @@ -4198,6 +4165,7 @@ decode_method_reply (GDBusMessage *reply, GError **error) g_variant_ref (result); } break; + case G_DBUS_MESSAGE_TYPE_ERROR: g_dbus_message_to_gerror (reply, error); break; @@ -4224,9 +4192,9 @@ decode_method_reply (GDBusMessage *reply, GError **error) * Since: 2.26 */ GVariant * -g_dbus_connection_invoke_method_finish (GDBusConnection *connection, - GAsyncResult *res, - GError **error) +g_dbus_connection_invoke_method_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) { GDBusMessage *reply; GVariant *result; @@ -4284,16 +4252,16 @@ g_dbus_connection_invoke_method_finish (GDBusConnection *connection, * Since: 2.26 */ GVariant * -g_dbus_connection_invoke_method_sync (GDBusConnection *connection, - const gchar *bus_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusInvokeMethodFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GError **error) +g_dbus_connection_invoke_method_sync (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) { GDBusMessage *message; GDBusMessage *reply; @@ -4359,23 +4327,21 @@ static void exported_subtree_free (ExportedSubtree *es) { if (es->user_data_free_func != NULL) - { - /* TODO: push to thread-default mainloop */ - es->user_data_free_func (es->user_data); - } + /* TODO: push to thread-default mainloop */ + es->user_data_free_func (es->user_data); + if (es->context != NULL) - { - g_main_context_unref (es->context); - } + g_main_context_unref (es->context); + g_free (es->object_path); g_free (es); } /* called without lock held */ static gboolean -handle_subtree_introspect (GDBusConnection *connection, - ExportedSubtree *es, - GDBusMessage *message) +handle_subtree_introspect (GDBusConnection *connection, + ExportedSubtree *es, + GDBusMessage *message) { GString *s; gboolean handled; @@ -4445,17 +4411,13 @@ handle_subtree_introspect (GDBusConnection *connection, if (is_root) { for (n = 0; children != NULL && children[n] != NULL; n++) - { - g_string_append_printf (s, " \n", children[n]); - } + g_string_append_printf (s, " \n", children[n]); } /* finally include nodes registered below us */ subnode_paths = g_dbus_connection_list_registered (es->connection, requested_object_path); for (n = 0; subnode_paths != NULL && subnode_paths[n] != NULL; n++) - { - g_string_append_printf (s, " \n", subnode_paths[n]); - } + g_string_append_printf (s, " \n", subnode_paths[n]); g_strfreev (subnode_paths); g_string_append (s, "\n"); @@ -4475,9 +4437,9 @@ handle_subtree_introspect (GDBusConnection *connection, /* called without lock held */ static gboolean -handle_subtree_method_invocation (GDBusConnection *connection, - ExportedSubtree *es, - GDBusMessage *message) +handle_subtree_method_invocation (GDBusConnection *connection, + ExportedSubtree *es, + GDBusMessage *message) { gboolean handled;; const gchar *sender; @@ -4513,17 +4475,11 @@ handle_subtree_method_invocation (GDBusConnection *connection, if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0) { if (g_strcmp0 (member, "Get") == 0 && g_strcmp0 (signature, "ss") == 0) - { - is_property_get = TRUE; - } + is_property_get = TRUE; else if (g_strcmp0 (member, "Set") == 0 && g_strcmp0 (signature, "ssv") == 0) - { - is_property_set = TRUE; - } + is_property_set = TRUE; else if (g_strcmp0 (member, "GetAll") == 0 && g_strcmp0 (signature, "s") == 0) - { - is_property_get_all = TRUE; - } + is_property_get_all = TRUE; } children = es->vtable->enumerate (es->connection, @@ -4557,9 +4513,7 @@ handle_subtree_method_invocation (GDBusConnection *connection, { const GDBusInterfaceInfo *id_n = (const GDBusInterfaceInfo *) interfaces->pdata[n]; if (g_strcmp0 (id_n->name, interface_name) == 0) - { - introspection_data = id_n; - } + introspection_data = id_n; } /* dispatch the call if the user wants to handle it */ @@ -4603,9 +4557,7 @@ handle_subtree_method_invocation (GDBusConnection *connection, { const GDBusInterfaceInfo *id_n = (const GDBusInterfaceInfo *) interfaces->pdata[n]; if (g_strcmp0 (id_n->name, interface_name) == 0) - { - introspection_data = id_n; - } + introspection_data = id_n; } /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the user-code @@ -4693,18 +4645,13 @@ process_subtree_vtable_message_in_idle_cb (gpointer _data) if (g_strcmp0 (g_dbus_message_get_interface (data->message), "org.freedesktop.DBus.Introspectable") == 0 && g_strcmp0 (g_dbus_message_get_member (data->message), "Introspect") == 0 && g_strcmp0 (g_dbus_message_get_signature (data->message), "") == 0) - { - handled = handle_subtree_introspect (data->es->connection, - data->es, - data->message); - } + handled = handle_subtree_introspect (data->es->connection, + data->es, + data->message); else - { - handled = handle_subtree_method_invocation (data->es->connection, - data->es, - data->message); - } - + handled = handle_subtree_method_invocation (data->es->connection, + data->es, + data->message); if (!handled) { @@ -4808,13 +4755,13 @@ subtree_message_func (GDBusConnection *connection, * Since: 2.26 */ guint -g_dbus_connection_register_subtree (GDBusConnection *connection, - const gchar *object_path, - const GDBusSubtreeVTable *vtable, - GDBusSubtreeFlags flags, - gpointer user_data, - GDestroyNotify user_data_free_func, - GError **error) +g_dbus_connection_register_subtree (GDBusConnection *connection, + const gchar *object_path, + const GDBusSubtreeVTable *vtable, + GDBusSubtreeFlags flags, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error) { guint ret; ExportedSubtree *es; @@ -4894,9 +4841,7 @@ g_dbus_connection_unregister_subtree (GDBusConnection *connection, es = g_hash_table_lookup (connection->priv->map_id_to_es, GUINT_TO_POINTER (registration_id)); if (es == NULL) - { - goto out; - } + goto out; g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_es, GUINT_TO_POINTER (es->id))); g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_es, es->object_path)); @@ -4913,9 +4858,9 @@ g_dbus_connection_unregister_subtree (GDBusConnection *connection, /* must be called with lock held */ static void -handle_generic_ping_unlocked (GDBusConnection *connection, - const gchar *object_path, - GDBusMessage *message) +handle_generic_ping_unlocked (GDBusConnection *connection, + const gchar *object_path, + GDBusMessage *message) { GDBusMessage *reply; reply = g_dbus_message_new_method_reply (message); @@ -4925,9 +4870,9 @@ handle_generic_ping_unlocked (GDBusConnection *connection, /* must be called with lock held */ static void -handle_generic_get_machine_id_unlocked (GDBusConnection *connection, - const gchar *object_path, - GDBusMessage *message) +handle_generic_get_machine_id_unlocked (GDBusConnection *connection, + const gchar *object_path, + GDBusMessage *message) { GDBusMessage *reply; @@ -4966,9 +4911,9 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection, /* must be called with lock held */ static void -handle_generic_introspect_unlocked (GDBusConnection *connection, - const gchar *object_path, - GDBusMessage *message) +handle_generic_introspect_unlocked (GDBusConnection *connection, + const gchar *object_path, + GDBusMessage *message) { guint n; GString *s; @@ -4981,9 +4926,7 @@ handle_generic_introspect_unlocked (GDBusConnection *connection, registered = g_dbus_connection_list_registered_unlocked (connection, object_path); for (n = 0; registered != NULL && registered[n] != NULL; n++) - { g_string_append_printf (s, " \n", registered[n]); - } g_strfreev (registered); g_string_append (s, "\n"); @@ -5043,8 +4986,8 @@ handle_generic_unlocked (GDBusConnection *connection, /* called in message handler thread with lock held */ static void -distribute_method_call (GDBusConnection *connection, - GDBusMessage *message) +distribute_method_call (GDBusConnection *connection, + GDBusMessage *message) { ExportedObject *eo; ExportedSubtree *es; @@ -5131,10 +5074,6 @@ message_bus_get_singleton (GBusType bus_type, switch (bus_type) { - default: - g_assert_not_reached (); - break; - case G_BUS_TYPE_SESSION: ret = &the_session_bus; break; @@ -5176,15 +5115,20 @@ message_bus_get_singleton (GBusType bus_type, } } break; + + default: + g_assert_not_reached (); + break; } + out: return ret; } static GDBusConnection * -get_uninitialized_connection (GBusType bus_type, - GCancellable *cancellable, - GError **error) +get_uninitialized_connection (GBusType bus_type, + GCancellable *cancellable, + GError **error) { GDBusConnection **singleton; GDBusConnection *ret; @@ -5249,9 +5193,9 @@ get_uninitialized_connection (GBusType bus_type, * Since: 2.26 */ GDBusConnection * -g_bus_get_sync (GBusType bus_type, - GCancellable *cancellable, - GError **error) +g_bus_get_sync (GBusType bus_type, + GCancellable *cancellable, + GError **error) { GDBusConnection *connection; diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index d264d31ec..767097d8a 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -92,8 +92,7 @@ g_dbus_method_invocation_finalize (GObject *object) g_object_unref (invocation->priv->message); g_variant_unref (invocation->priv->parameters); - if (G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize (object); + G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize (object); } static void @@ -220,8 +219,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_SENDER, g_param_spec_string ("sender", - _("Sender"), - _("The bus name that invoked the method."), + P_("Sender"), + P_("The bus name that invoked the method."), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -240,8 +239,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_OBJECT_PATH, g_param_spec_string ("object-path", - _("Object Path"), - _("The object path the method was invoked on."), + P_("Object Path"), + P_("The object path the method was invoked on."), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -260,8 +259,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_INTERFACE_NAME, g_param_spec_string ("interface-name", - _("Interface Name"), - _("The name of the D-Bus interface the method was invoked on."), + P_("Interface Name"), + P_("The name of the D-Bus interface the method was invoked on."), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -280,8 +279,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_METHOD_NAME, g_param_spec_string ("method-name", - _("Method Name"), - _("The name of the method that was invoked."), + P_("Method Name"), + P_("The name of the method that was invoked."), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -300,8 +299,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_METHOD_INFO, g_param_spec_boxed ("method-info", - _("Method Info"), - _("Information about the method that was invoked, if any."), + P_("Method Info"), + P_("Information about the method that was invoked, if any."), G_TYPE_DBUS_METHOD_INFO, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -320,8 +319,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_CONNECTION, g_param_spec_object ("connection", - _("Connection"), - _("The #GDBusConnection the method was invoked on."), + P_("Connection"), + P_("The #GDBusConnection the method was invoked on."), G_TYPE_DBUS_CONNECTION, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -340,8 +339,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_MESSAGE, g_param_spec_object ("message", - _("Message"), - _("The D-Bus Message."), + P_("Message"), + P_("The D-Bus Message."), G_TYPE_DBUS_MESSAGE, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -360,8 +359,8 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_PARAMETERS, g_param_spec_boxed ("parameters", - _("Parameters"), - _("The parameters as a #GVariant tuple."), + P_("Parameters"), + P_("The parameters as a #GVariant tuple."), G_TYPE_VARIANT, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -380,14 +379,14 @@ g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) g_object_class_install_property (gobject_class, PROP_USER_DATA, g_param_spec_pointer ("user-data", - _("User Data"), - _("The gpointer passed to g_dbus_connection_register_object()."), - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); + P_("User Data"), + P_("The gpointer passed to g_dbus_connection_register_object()."), + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); g_type_class_add_private (klass, sizeof (GDBusMethodInvocationPrivate)); } @@ -579,15 +578,15 @@ g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation) * Since: 2.26 */ GDBusMethodInvocation * -g_dbus_method_invocation_new (const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - const GDBusMethodInfo *method_info, - GDBusConnection *connection, - GDBusMessage *message, - GVariant *parameters, - gpointer user_data) +g_dbus_method_invocation_new (const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + const GDBusMethodInfo *method_info, + GDBusConnection *connection, + GDBusMessage *message, + GVariant *parameters, + gpointer user_data) { g_return_val_if_fail (sender == NULL || g_dbus_is_name (sender), NULL); g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 8d3926b70..1ed7ef400 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -110,16 +110,12 @@ g_dbus_proxy_finalize (GObject *object) GDBusProxy *proxy = G_DBUS_PROXY (object); if (proxy->priv->properties_changed_subscriber_id > 0) - { - g_dbus_connection_signal_unsubscribe (proxy->priv->connection, - proxy->priv->properties_changed_subscriber_id); - } + g_dbus_connection_signal_unsubscribe (proxy->priv->connection, + proxy->priv->properties_changed_subscriber_id); if (proxy->priv->signals_subscriber_id > 0) - { - g_dbus_connection_signal_unsubscribe (proxy->priv->connection, - proxy->priv->signals_subscriber_id); - } + g_dbus_connection_signal_unsubscribe (proxy->priv->connection, + proxy->priv->signals_subscriber_id); g_object_unref (proxy->priv->connection); g_free (proxy->priv->unique_bus_name); @@ -131,8 +127,7 @@ g_dbus_proxy_finalize (GObject *object) if (proxy->priv->expected_interface != NULL) g_dbus_interface_info_unref (proxy->priv->expected_interface); - if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object); + G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object); } static void @@ -250,8 +245,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_INTERFACE_INFO, g_param_spec_boxed ("g-interface-info", - _("Interface Information"), - _("Interface Information"), + P_("Interface Information"), + P_("Interface Information"), G_TYPE_DBUS_INTERFACE_INFO, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -269,8 +264,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_CONNECTION, g_param_spec_object ("g-connection", - _("g-connection"), - _("The connection the proxy is for"), + P_("g-connection"), + P_("The connection the proxy is for"), G_TYPE_DBUS_CONNECTION, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -289,8 +284,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_FLAGS, g_param_spec_flags ("g-flags", - _("g-flags"), - _("Flags for the proxy"), + P_("g-flags"), + P_("Flags for the proxy"), G_TYPE_DBUS_PROXY_FLAGS, G_DBUS_PROXY_FLAGS_NONE, G_PARAM_READABLE | @@ -310,8 +305,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_UNIQUE_BUS_NAME, g_param_spec_string ("g-unique-bus-name", - _("g-unique-bus-name"), - _("The unique bus name the proxy is for"), + P_("g-unique-bus-name"), + P_("The unique bus name the proxy is for"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -330,8 +325,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_OBJECT_PATH, g_param_spec_string ("g-object-path", - _("g-object-path"), - _("The object path the proxy is for"), + P_("g-object-path"), + P_("The object path the proxy is for"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -350,8 +345,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_INTERFACE_NAME, g_param_spec_string ("g-interface-name", - _("g-interface-name"), - _("The D-Bus interface name the proxy is for"), + P_("g-interface-name"), + P_("The D-Bus interface name the proxy is for"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -377,8 +372,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_object_class_install_property (gobject_class, PROP_G_DEFAULT_TIMEOUT, g_param_spec_int ("g-default-timeout", - _("Default Timeout"), - _("Timeout for remote method invocation"), + P_("Default Timeout"), + P_("Timeout for remote method invocation"), -1, G_MAXINT, -1, @@ -459,8 +454,8 @@ g_dbus_proxy_init (GDBusProxy *proxy) * Since: 2.26 */ gchar ** -g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, - GError **error) +g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, + GError **error) { gchar **names; GPtrArray *p; @@ -485,9 +480,7 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, g_hash_table_iter_init (&iter, proxy->priv->properties); while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) - { - g_ptr_array_add (p, g_strdup (key)); - } + g_ptr_array_add (p, g_strdup (key)); g_ptr_array_sort (p, (GCompareFunc) g_strcmp0); g_ptr_array_add (p, NULL); @@ -519,9 +512,9 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, * Since: 2.26 */ GVariant * -g_dbus_proxy_get_cached_property (GDBusProxy *proxy, - const gchar *property_name, - GError **error) +g_dbus_proxy_get_cached_property (GDBusProxy *proxy, + const gchar *property_name, + GError **error) { GVariant *value; @@ -560,13 +553,13 @@ g_dbus_proxy_get_cached_property (GDBusProxy *proxy, /* ---------------------------------------------------------------------------------------------------- */ static void -on_signal_received (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) +on_signal_received (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (user_data); @@ -581,13 +574,13 @@ on_signal_received (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ static void -on_properties_changed (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) +on_properties_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (user_data); GError *error; @@ -746,9 +739,9 @@ process_get_all_reply (GDBusProxy *proxy, } static gboolean -initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) { GDBusProxy *proxy = G_DBUS_PROXY (initable); GVariant *result; @@ -823,11 +816,11 @@ get_all_cb (GDBusConnection *connection, } static void -async_initable_init_async (GAsyncInitable *initable, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +async_initable_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (initable); GSimpleAsyncResult *simple; @@ -1248,9 +1241,9 @@ g_dbus_proxy_set_interface_info (GDBusProxy *proxy, /* ---------------------------------------------------------------------------------------------------- */ static gboolean -maybe_split_method_name (const gchar *method_name, - gchar **out_interface_name, - const gchar **out_method_name) +maybe_split_method_name (const gchar *method_name, + gchar **out_interface_name, + const gchar **out_method_name) { gboolean was_split; @@ -1310,8 +1303,8 @@ reply_cb (GDBusConnection *connection, } static const GDBusMethodInfo * -lookup_method_info_or_warn (GDBusProxy *proxy, - const char *method_name) +lookup_method_info_or_warn (GDBusProxy *proxy, + const gchar *method_name) { const GDBusMethodInfo *info; @@ -1400,14 +1393,14 @@ validate_method_return (const char *method_name, * Since: 2.26 */ void -g_dbus_proxy_invoke_method (GDBusProxy *proxy, - const gchar *method_name, - GVariant *parameters, - GDBusInvokeMethodFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_proxy_invoke_method (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *simple; gboolean was_split; @@ -1536,21 +1529,21 @@ g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, * Since: 2.26 */ GVariant * -g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, - const gchar *method_name, - GVariant *parameters, - GDBusInvokeMethodFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GError **error) +g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusInvokeMethodFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) { GVariant *ret; gboolean was_split; gchar *split_interface_name; const gchar *split_method_name; const GDBusMethodInfo *expected_method_info; - const char *target_method_name; - const char *target_interface_name; + const gchar *target_method_name; + const gchar *target_interface_name; g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); g_return_val_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name), NULL); diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index 0f3fa3632..b6bd931cf 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -134,8 +134,7 @@ g_dbus_server_finalize (GObject *object) if (server->priv->main_context_at_construction != NULL) g_main_context_unref (server->priv->main_context_at_construction); - if (G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize (object); + G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize (object); } static void @@ -229,8 +228,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) g_object_class_install_property (gobject_class, PROP_FLAGS, g_param_spec_flags ("flags", - _("Flags"), - _("Flags for the server"), + P_("Flags"), + P_("Flags for the server"), G_TYPE_DBUS_SERVER_FLAGS, G_DBUS_SERVER_FLAGS_NONE, G_PARAM_READABLE | @@ -250,8 +249,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) g_object_class_install_property (gobject_class, PROP_GUID, g_param_spec_string ("guid", - _("GUID"), - _("The guid of the server"), + P_("GUID"), + P_("The guid of the server"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -270,8 +269,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) g_object_class_install_property (gobject_class, PROP_ADDRESS, g_param_spec_string ("address", - _("Address"), - _("The address to listen on"), + P_("Address"), + P_("The address to listen on"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -290,8 +289,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) g_object_class_install_property (gobject_class, PROP_CLIENT_ADDRESS, g_param_spec_string ("client-address", - _("Client Address"), - _("The address clients can use"), + P_("Client Address"), + P_("The address clients can use"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME | @@ -308,8 +307,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) g_object_class_install_property (gobject_class, PROP_ACTIVE, g_param_spec_string ("active", - _("Active"), - _("Whether the server is currently active"), + P_("Active"), + P_("Whether the server is currently active"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME | @@ -326,8 +325,8 @@ g_dbus_server_class_init (GDBusServerClass *klass) g_object_class_install_property (gobject_class, PROP_AUTHENTICATION_OBSERVER, g_param_spec_object ("authentication-observer", - _("Authentication Observer"), - _("Object used to assist in the authentication process"), + P_("Authentication Observer"), + P_("Object used to assist in the authentication process"), G_TYPE_DBUS_AUTH_OBSERVER, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -422,12 +421,12 @@ on_run (GSocketService *service, * Since: 2.26 */ GDBusServer * -g_dbus_server_new_sync (const gchar *address, - GDBusServerFlags flags, - const gchar *guid, - GDBusAuthObserver *observer, - GCancellable *cancellable, - GError **error) +g_dbus_server_new_sync (const gchar *address, + GDBusServerFlags flags, + const gchar *guid, + GDBusAuthObserver *observer, + GCancellable *cancellable, + GError **error) { GDBusServer *server; @@ -764,9 +763,8 @@ try_tcp (GDBusServer *server, NULL, error); if (resolved_addresses == NULL) - { - goto out; - } + goto out; + /* TODO: handle family */ for (l = resolved_addresses; l != NULL; l = l->next) { @@ -787,10 +785,9 @@ try_tcp (GDBusServer *server, goto out; } if (port_num == 0) - { - /* make sure we allocate the same port number for other listeners */ - port_num = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (effective_address)); - } + /* make sure we allocate the same port number for other listeners */ + port_num = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (effective_address)); + g_object_unref (effective_address); g_object_unref (socket_address); } @@ -961,9 +958,9 @@ on_run (GSocketService *service, } static gboolean -initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) { GDBusServer *server = G_DBUS_SERVER (initable); gboolean ret; @@ -1009,26 +1006,18 @@ initable_init (GInitable *initable, } #ifdef G_OS_UNIX else if (g_strcmp0 (transport_name, "unix") == 0) - { - ret = try_unix (server, address_entry, key_value_pairs, &this_error); - } + ret = try_unix (server, address_entry, key_value_pairs, &this_error); #endif else if (g_strcmp0 (transport_name, "tcp") == 0) - { - ret = try_tcp (server, address_entry, key_value_pairs, FALSE, &this_error); - } + ret = try_tcp (server, address_entry, key_value_pairs, FALSE, &this_error); else if (g_strcmp0 (transport_name, "nonce-tcp") == 0) - { - ret = try_tcp (server, address_entry, key_value_pairs, TRUE, &this_error); - } + ret = try_tcp (server, address_entry, key_value_pairs, TRUE, &this_error); else - { - g_set_error (&this_error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Cannot listen on unsupported transport `%s'"), - transport_name); - } + g_set_error (&this_error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Cannot listen on unsupported transport `%s'"), + transport_name); g_free (transport_name); if (key_value_pairs != NULL) diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index 13556e1bd..d1188ade2 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -157,8 +157,7 @@ g_unix_credentials_message_finalize (GObject *object) if (message->priv->credentials != NULL) g_object_unref (message->priv->credentials); - if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize (object); + G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize (object); } static void @@ -252,8 +251,8 @@ g_unix_credentials_message_class_init (GUnixCredentialsMessageClass *class) g_object_class_install_property (gobject_class, PROP_CREDENTIALS, g_param_spec_object ("credentials", - _("Credentials"), - _("The credentials stored in the message"), + P_("Credentials"), + P_("The credentials stored in the message"), G_TYPE_CREDENTIALS, G_PARAM_READABLE | G_PARAM_WRITABLE | From 5d1135618e6ec5c6a6c41bed602cec643f9d8b8f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 10 May 2010 08:07:07 -0400 Subject: [PATCH 24/76] Trivia --- gio/gdbusaddress.c | 34 +++++++++++++++++----------------- gio/gdbusconnection.c | 34 +++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 96697126a..5e3b87c44 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -396,10 +396,10 @@ g_dbus_is_supported_address (const gchar *string, } gboolean -_g_dbus_address_parse_entry (const gchar *address_entry, - gchar **out_transport_name, - GHashTable **out_key_value_pairs, - GError **error) +_g_dbus_address_parse_entry (const gchar *address_entry, + gchar **out_transport_name, + GHashTable **out_key_value_pairs, + GError **error) { gboolean ret; GHashTable *key_value_pairs; @@ -485,11 +485,11 @@ out: * making libgio link to libX11... */ static GIOStream * -g_dbus_address_connect (const gchar *address_entry, - const gchar *transport_name, - GHashTable *key_value_pairs, - GCancellable *cancellable, - GError **error) +g_dbus_address_connect (const gchar *address_entry, + const gchar *transport_name, + GHashTable *key_value_pairs, + GCancellable *cancellable, + GError **error) { GIOStream *ret; GSocketConnectable *connectable; @@ -669,10 +669,10 @@ g_dbus_address_connect (const gchar *address_entry, } static GIOStream * -g_dbus_address_try_connect_one (const gchar *address_entry, - gchar **out_guid, - GCancellable *cancellable, - GError **error) +g_dbus_address_try_connect_one (const gchar *address_entry, + gchar **out_guid, + GCancellable *cancellable, + GError **error) { GIOStream *ret; GHashTable *key_value_pairs; @@ -856,10 +856,10 @@ g_dbus_address_get_stream_finish (GAsyncResult *res, * Since: 2.26 */ GIOStream * -g_dbus_address_get_stream_sync (const gchar *address, - gchar **out_guid, - GCancellable *cancellable, - GError **error) +g_dbus_address_get_stream_sync (const gchar *address, + gchar **out_guid, + GCancellable *cancellable, + GError **error) { GIOStream *ret; gchar **addr_array; diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 8e7570e81..0a20e4695 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -577,7 +577,7 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * If you are constructing a #GDBusConnection and pass * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT in the * #GDBusConnection:flags property you will be able to read the GUID - * of the other peer here after the connection has been succesfully + * of the other peer here after the connection has been successfully * initialized. * * Since: 2.26 @@ -779,6 +779,16 @@ g_dbus_connection_init (GDBusConnection *connection) connection->priv->filters = g_ptr_array_new (); } +/** + * g_dbus_connection_get_stream: + * @connection: a #GDBusConnection + * + * Gets the underlying stream used for IO. + * + * Returns: the stream used for IO + * + * Since: 2.26 + */ GIOStream * g_dbus_connection_get_stream (GDBusConnection *connection) { @@ -810,7 +820,7 @@ g_dbus_connection_is_closed (GDBusConnection *connection) * * Gets the capabilities negotiated with the remote peer * - * Returns: One or more flags from the #GDBusCapabilityFlags enumeration. + * Returns: Zero or more flags from the #GDBusCapabilityFlags enumeration. * * Since: 2.26 */ @@ -1654,9 +1664,7 @@ get_offered_capabilities_max (GDBusConnection *connection) ret = G_DBUS_CAPABILITY_FLAGS_NONE; #ifdef G_OS_UNIX if (G_IS_UNIX_CONNECTION (connection->priv->stream)) - { - ret |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; - } + ret |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; #endif return ret; } @@ -1851,11 +1859,11 @@ async_init_thread (GSimpleAsyncResult *res, } static void -async_initable_init_async (GAsyncInitable *initable, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +async_initable_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *res; @@ -2586,7 +2594,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, /* Add the match rule to the bus... * * Avoid adding match rules for NameLost and NameAcquired messages - the bus will - * always send such messages to to us. + * always send such messages to us. */ if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { @@ -2889,7 +2897,7 @@ distribute_signals (GDBusConnection *connection, sender = g_dbus_message_get_sender (message); - /* collect subcsribers that match on sender */ + /* collect subscribers that match on sender */ if (sender != NULL) { signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, sender); @@ -2897,7 +2905,7 @@ distribute_signals (GDBusConnection *connection, schedule_callbacks (connection, signal_data_array, message, sender); } - /* collect subcsribers not matching on sender */ + /* collect subscribers not matching on sender */ signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, ""); if (signal_data_array != NULL) schedule_callbacks (connection, signal_data_array, message, sender); From 728c4e38e72055080c148f9cebca85a08d16a445 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 10 May 2010 08:07:28 -0400 Subject: [PATCH 25/76] More copyright year updates --- gio/tests/gdbus-addresses.c | 2 +- gio/tests/gdbus-connection.c | 2 +- gio/tests/gdbus-error.c | 2 +- gio/tests/gdbus-exit-on-close.c | 2 +- gio/tests/gdbus-export.c | 2 +- gio/tests/gdbus-introspection.c | 2 +- gio/tests/gdbus-names.c | 2 +- gio/tests/gdbus-peer.c | 2 +- gio/tests/gdbus-proxy.c | 2 +- gio/tests/gdbus-serialization.c | 2 +- gio/tests/gdbus-sessionbus.c | 2 +- gio/tests/gdbus-tests.c | 2 +- gio/tests/gdbus-threading.c | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gio/tests/gdbus-addresses.c b/gio/tests/gdbus-addresses.c index 97e5922ff..568c775a6 100644 --- a/gio/tests/gdbus-addresses.c +++ b/gio/tests/gdbus-addresses.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c index 1174d6f29..025de1d4c 100644 --- a/gio/tests/gdbus-connection.c +++ b/gio/tests/gdbus-connection.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-error.c b/gio/tests/gdbus-error.c index 342771002..2231ca863 100644 --- a/gio/tests/gdbus-error.c +++ b/gio/tests/gdbus-error.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-exit-on-close.c b/gio/tests/gdbus-exit-on-close.c index 7f75519ce..6120fa5d4 100644 --- a/gio/tests/gdbus-exit-on-close.c +++ b/gio/tests/gdbus-exit-on-close.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c index 6e33d2f4f..886e7147c 100644 --- a/gio/tests/gdbus-export.c +++ b/gio/tests/gdbus-export.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c index 1b3d7a51f..88e368d72 100644 --- a/gio/tests/gdbus-introspection.c +++ b/gio/tests/gdbus-introspection.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c index 9008fa018..7edd6498e 100644 --- a/gio/tests/gdbus-names.c +++ b/gio/tests/gdbus-names.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 50b9a492b..229f70e8f 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c index 6e45f4217..f9e17432a 100644 --- a/gio/tests/gdbus-proxy.c +++ b/gio/tests/gdbus-proxy.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 782ddacdb..9a96f50bc 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-sessionbus.c b/gio/tests/gdbus-sessionbus.c index 1e16eddc5..6e70e02f3 100644 --- a/gio/tests/gdbus-sessionbus.c +++ b/gio/tests/gdbus-sessionbus.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-tests.c b/gio/tests/gdbus-tests.c index b8ac8490d..d801e1d56 100644 --- a/gio/tests/gdbus-tests.c +++ b/gio/tests/gdbus-tests.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 diff --git a/gio/tests/gdbus-threading.c b/gio/tests/gdbus-threading.c index 0747907df..cfeafe199 100644 --- a/gio/tests/gdbus-threading.c +++ b/gio/tests/gdbus-threading.c @@ -1,6 +1,6 @@ /* GLib testing framework examples and tests * - * Copyright (C) 2008-2009 Red Hat, Inc. + * 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 From 869b4c68332f36377bbdfd186e37f6194ae5ed5a Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 10 May 2010 11:47:08 -0400 Subject: [PATCH 26/76] GDBus: Use call() instead of invoke_method() Lots of people been suggesting this. We still use MethodInvocation / method_invocation for handling incoming method calls so use call() instead of invoke_method() helps to separate the client and server facilities. Which is a good thing(tm). --- docs/reference/gio/gio-sections.txt | 14 +- docs/reference/gio/migrating-dbus-glib.xml | 6 +- gio/gdbus-tool.c | 160 ++++++------- gio/gdbusauthmechanismexternal.c | 2 +- gio/gdbusconnection.c | 104 ++++----- gio/gdbusconnection.h | 10 +- gio/gdbuserror.c | 13 +- gio/gdbusnameowning.c | 52 ++--- gio/gdbusnamewatching.c | 56 ++--- gio/gdbusproxy.c | 142 ++++++------ gio/gdbusproxy.h | 10 +- gio/gio.symbols | 14 +- gio/gioenums.h | 14 +- gio/tests/gdbus-connection.c | 160 ++++++------- gio/tests/gdbus-example-peer.c | 20 +- gio/tests/gdbus-export.c | 252 ++++++++++----------- gio/tests/gdbus-introspection.c | 14 +- gio/tests/gdbus-names.c | 40 ++-- gio/tests/gdbus-peer.c | 44 ++-- gio/tests/gdbus-proxy.c | 146 ++++++------ gio/tests/gdbus-threading.c | 114 +++++----- 21 files changed, 694 insertions(+), 693 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index fe4d7bd55..a5d61eccd 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2361,10 +2361,10 @@ g_dbus_connection_get_guid g_dbus_connection_get_unique_name g_dbus_connection_get_capabilities g_dbus_connection_get_peer_credentials -GDBusInvokeMethodFlags -g_dbus_connection_invoke_method -g_dbus_connection_invoke_method_finish -g_dbus_connection_invoke_method_sync +GDBusCallFlags +g_dbus_connection_call +g_dbus_connection_call_finish +g_dbus_connection_call_sync g_dbus_connection_emit_signal GDBusSignalCallback g_dbus_connection_signal_subscribe @@ -2495,9 +2495,9 @@ g_dbus_proxy_get_cached_property_names g_dbus_proxy_get_cached_property g_dbus_proxy_set_interface_info g_dbus_proxy_get_interface_info -g_dbus_proxy_invoke_method -g_dbus_proxy_invoke_method_finish -g_dbus_proxy_invoke_method_sync +g_dbus_proxy_call +g_dbus_proxy_call_finish +g_dbus_proxy_call_sync G_DBUS_PROXY G_IS_DBUS_PROXY diff --git a/docs/reference/gio/migrating-dbus-glib.xml b/docs/reference/gio/migrating-dbus-glib.xml index dc7aeaff1..70e6f2d5b 100644 --- a/docs/reference/gio/migrating-dbus-glib.xml +++ b/docs/reference/gio/migrating-dbus-glib.xml @@ -55,9 +55,9 @@ dbus_g_connection_unregister_g_object()g_dbus_connection_unregister_object() dbus_g_object_type_install_info()introspection data is installed while registering an object, see g_dbus_connection_register_object() - dbus_g_proxy_begin_call()g_dbus_proxy_invoke_method() - dbus_g_proxy_end_call()g_dbus_proxy_invoke_method_finish() - dbus_g_proxy_call()g_dbus_proxy_invoke_method_sync() + dbus_g_proxy_begin_call()g_dbus_proxy_call() + dbus_g_proxy_end_call()g_dbus_proxy_call_finish() + dbus_g_proxy_call()g_dbus_proxy_call_sync() dbus_g_error_domain_register()g_dbus_error_register_error_domain() dbus_g_error_has_name()no direct equivalent, see g_dbus_error_get_remote_error() dbus_g_method_return()g_dbus_method_invocation_return_value() diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index 1cf230657..ddec98517 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -143,16 +143,16 @@ print_methods (GDBusConnection *c, guint m; error = NULL; - result = g_dbus_connection_invoke_method_sync (c, - name, - path, - "org.freedesktop.DBus.Introspectable", - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, /* 3 secs */ - NULL, - &error); + result = g_dbus_connection_call_sync (c, + name, + path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); if (result == NULL) { g_printerr (_("Error: %s\n"), error->message); @@ -205,16 +205,16 @@ print_paths (GDBusConnection *c, guint n; error = NULL; - result = g_dbus_connection_invoke_method_sync (c, - name, - path, - "org.freedesktop.DBus.Introspectable", - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, /* 3 secs */ - NULL, - &error); + result = g_dbus_connection_call_sync (c, + name, + path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); if (result == NULL) { g_printerr (_("Error: %s\n"), error->message); @@ -283,16 +283,16 @@ print_names (GDBusConnection *c, name_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); error = NULL; - result = g_dbus_connection_invoke_method_sync (c, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ListNames", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, /* 3 secs */ - NULL, - &error); + result = g_dbus_connection_call_sync (c, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ListNames", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); if (result == NULL) { g_printerr (_("Error: %s\n"), error->message); @@ -312,16 +312,16 @@ print_names (GDBusConnection *c, g_variant_unref (result); error = NULL; - result = g_dbus_connection_invoke_method_sync (c, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ListActivatableNames", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, /* 3 secs */ - NULL, - &error); + result = g_dbus_connection_call_sync (c, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ListActivatableNames", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + &error); if (result == NULL) { g_printerr (_("Error: %s\n"), error->message); @@ -453,16 +453,16 @@ call_helper_get_method_in_signature (GDBusConnection *c, result = NULL; node_info = NULL; - result = g_dbus_connection_invoke_method_sync (c, - dest, - path, - "org.freedesktop.DBus.Introspectable", - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, /* 3 secs */ - NULL, - error); + result = g_dbus_connection_call_sync (c, + dest, + path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 3000, /* 3 secs */ + NULL, + error); if (result == NULL) goto out; @@ -825,16 +825,16 @@ handle_call (gint *argc, if (parameters != NULL) parameters = g_variant_ref_sink (parameters); - result = g_dbus_connection_invoke_method_sync (c, - opt_call_dest, - opt_call_object_path, - interface_name, - method_name, - parameters, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_connection_call_sync (c, + opt_call_dest, + opt_call_object_path, + interface_name, + method_name, + parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); if (result == NULL) { g_printerr (_("Error: %s\n"), error->message); @@ -1011,16 +1011,16 @@ dump_interface (GDBusConnection *c, if (c != NULL && name != NULL && object_path != NULL) { GVariant *result; - result = g_dbus_connection_invoke_method_sync (c, - name, - object_path, - "org.freedesktop.DBus.Properties", - "GetAll", - g_variant_new ("(s)", o->name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, - NULL, - NULL); + result = g_dbus_connection_call_sync (c, + name, + object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", o->name), + G_DBUS_CALL_FLAGS_NONE, + 3000, + NULL, + NULL); if (result != NULL) { if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})"))) @@ -1259,16 +1259,16 @@ handle_introspect (gint *argc, if (request_completion) goto out; - result = g_dbus_connection_invoke_method_sync (c, - opt_introspect_dest, - opt_introspect_object_path, - "org.freedesktop.DBus.Introspectable", - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 3000, /* 3 sec */ - NULL, - &error); + result = g_dbus_connection_call_sync (c, + opt_introspect_dest, + opt_introspect_object_path, + "org.freedesktop.DBus.Introspectable", + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 3000, /* 3 sec */ + NULL, + &error); if (result == NULL) { g_printerr (_("Error: %s\n"), error->message); diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c index cebbc2426..0ec56b934 100644 --- a/gio/gdbusauthmechanismexternal.c +++ b/gio/gdbusauthmechanismexternal.c @@ -351,7 +351,7 @@ mechanism_client_initiate (GDBusAuthMechanism *mechanism, /* return the uid */ #if defined(G_OS_UNIX) - initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, g_credentials_get_unix_user (credentials, NULL)); + initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL)); #elif defined(G_OS_WIN32) initial_response = g_strdup_printf ("%s", g_credentials_get_windows_user ()); #else diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 0a20e4695..2de63821a 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -1802,16 +1802,16 @@ initable_init (GInitable *initable, GVariant *hello_result; const gchar *s; - hello_result = g_dbus_connection_invoke_method_sync (connection, - "org.freedesktop.DBus", /* name */ - "/org/freedesktop/DBus", /* path */ - "org.freedesktop.DBus", /* interface */ - "Hello", - NULL, /* parameters */ - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* TODO: cancellable */ - &connection->priv->initialization_error); + hello_result = g_dbus_connection_call_sync (connection, + "org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "Hello", + NULL, /* parameters */ + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* TODO: cancellable */ + &connection->priv->initialization_error); if (hello_result == NULL) goto out; @@ -2274,7 +2274,7 @@ static guint _global_filter_id = 1; * worker thread. Also note that filters are rarely needed - use API * such as g_dbus_connection_send_message_with_reply(), * g_dbus_connection_signal_subscribe() or - * g_dbus_connection_invoke_method() instead. + * g_dbus_connection_call() instead. * * Returns: A filter identifier that can be used with * g_dbus_connection_remove_filter(). @@ -3646,7 +3646,7 @@ handle_introspect (GDBusConnection *connection, /* called in thread where object was registered - no locks held */ static gboolean -invoke_method_in_idle_cb (gpointer user_data) +call_in_idle_cb (gpointer user_data) { GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data); GDBusInterfaceVTable *vtable; @@ -3753,7 +3753,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, idle_source = g_idle_source_new (); g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); g_source_set_callback (idle_source, - invoke_method_in_idle_cb, + call_in_idle_cb, invocation, g_object_unref); g_source_attach (idle_source, main_context); @@ -4068,22 +4068,22 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, } static void -add_invoke_method_flags (GDBusMessage *message, - GDBusInvokeMethodFlags flags) +add_call_flags (GDBusMessage *message, + GDBusCallFlags flags) { - if (flags & G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START) + if (flags & G_DBUS_CALL_FLAGS_NO_AUTO_START) g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_AUTO_START); } /** - * g_dbus_connection_invoke_method: + * g_dbus_connection_call: * @connection: A #GDBusConnection. * @bus_name: A unique or well-known bus name or %NULL if @connection is not a message bus connection. * @object_path: Path of remote object. * @interface_name: D-Bus interface to invoke method on. * @method_name: The name of the method to invoke. * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. - * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @flags: Flags from the #GDBusCallFlags enumeration. * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. * @cancellable: A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't @@ -4103,24 +4103,24 @@ add_invoke_method_flags (GDBusMessage *message, * This is an asynchronous method. When the operation is finished, @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_connection_invoke_method_finish() to get the result of the operation. - * See g_dbus_connection_invoke_method_sync() for the synchronous version of this + * g_dbus_connection_call_finish() to get the result of the operation. + * See g_dbus_connection_call_sync() for the synchronous version of this * function. * * Since: 2.26 */ void -g_dbus_connection_invoke_method (GDBusConnection *connection, - const gchar *bus_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusInvokeMethodFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_connection_call (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GDBusMessage *message; @@ -4136,7 +4136,7 @@ g_dbus_connection_invoke_method (GDBusConnection *connection, object_path, interface_name, method_name); - add_invoke_method_flags (message, flags); + add_call_flags (message, flags); if (parameters != NULL) g_dbus_message_set_body (message, parameters); @@ -4187,12 +4187,12 @@ decode_method_reply (GDBusMessage *reply, } /** - * g_dbus_connection_invoke_method_finish: + * g_dbus_connection_call_finish: * @connection: A #GDBusConnection. - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_invoke_method(). + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_call(). * @error: Return location for error or %NULL. * - * Finishes an operation started with g_dbus_connection_invoke_method(). + * Finishes an operation started with g_dbus_connection_call(). * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with * return values. Free with g_variant_unref(). @@ -4200,9 +4200,9 @@ decode_method_reply (GDBusMessage *reply, * Since: 2.26 */ GVariant * -g_dbus_connection_invoke_method_finish (GDBusConnection *connection, - GAsyncResult *res, - GError **error) +g_dbus_connection_call_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) { GDBusMessage *reply; GVariant *result; @@ -4228,14 +4228,14 @@ g_dbus_connection_invoke_method_finish (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ /** - * g_dbus_connection_invoke_method_sync: + * g_dbus_connection_call_sync: * @connection: A #GDBusConnection. * @bus_name: A unique or well-known bus name. * @object_path: Path of remote object. * @interface_name: D-Bus interface to invoke method on. * @method_name: The name of the method to invoke. * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. - * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @flags: Flags from the #GDBusCallFlags enumeration. * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. @@ -4251,7 +4251,7 @@ g_dbus_connection_invoke_method_finish (GDBusConnection *connection, * fails with %G_IO_ERROR_INVALID_ARGUMENT. * * The calling thread is blocked until a reply is received. See - * g_dbus_connection_invoke_method() for the asynchronous version of + * g_dbus_connection_call() for the asynchronous version of * this method. * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with @@ -4260,16 +4260,16 @@ g_dbus_connection_invoke_method_finish (GDBusConnection *connection, * Since: 2.26 */ GVariant * -g_dbus_connection_invoke_method_sync (GDBusConnection *connection, - const gchar *bus_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusInvokeMethodFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GError **error) +g_dbus_connection_call_sync (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) { GDBusMessage *message; GDBusMessage *reply; @@ -4291,7 +4291,7 @@ g_dbus_connection_invoke_method_sync (GDBusConnection *connection, object_path, interface_name, method_name); - add_invoke_method_flags (message, flags); + add_call_flags (message, flags); if (parameters != NULL) g_dbus_message_set_body (message, parameters); diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index f7bbdb7a4..eef99d381 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -171,27 +171,27 @@ gboolean g_dbus_connection_emit_signal (GDBusConnection const gchar *signal_name, GVariant *parameters, GError **error); -void g_dbus_connection_invoke_method (GDBusConnection *connection, +void g_dbus_connection_call (GDBusConnection *connection, const gchar *bus_name, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, - GDBusInvokeMethodFlags flags, + GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); -GVariant *g_dbus_connection_invoke_method_finish (GDBusConnection *connection, +GVariant *g_dbus_connection_call_finish (GDBusConnection *connection, GAsyncResult *res, GError **error); -GVariant *g_dbus_connection_invoke_method_sync (GDBusConnection *connection, +GVariant *g_dbus_connection_call_sync (GDBusConnection *connection, const gchar *bus_name, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, - GDBusInvokeMethodFlags flags, + GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GError **error); diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c index 12cd0086b..a6e552b0f 100644 --- a/gio/gdbuserror.c +++ b/gio/gdbuserror.c @@ -41,9 +41,9 @@ * @include: gio/gio.h * * All facilities that return errors from remote methods (such as - * g_dbus_connection_invoke_method_sync()) use #GError to represent - * both D-Bus errors (e.g. errors returned from the other peer) and - * locally in-process generated errors. + * g_dbus_connection_call_sync()) use #GError to represent both D-Bus + * errors (e.g. errors returned from the other peer) and locally + * in-process generated errors. * * To check if a returned #GError is an error from a remote peer, use * g_dbus_error_is_remote_error(). To get the actual D-Bus error name, @@ -488,9 +488,10 @@ g_dbus_error_is_remote_error (const GError *error) * * Gets the D-Bus error name used for @error, if any. * - * This function is guaranteed to return a D-Bus error name for all #GErrors returned from - * functions handling remote method calls (e.g. g_dbus_connection_invoke_method_finish()) - * unless g_dbus_error_strip_remote_error() has been used on @error. + * This function is guaranteed to return a D-Bus error name for all + * #GErrors returned from functions handling remote method + * calls (e.g. g_dbus_connection_call_finish()) unless + * g_dbus_error_strip_remote_error() has been used on @error. * * Returns: An allocated string or %NULL if the D-Bus error name could not be found. Free with g_free(). * diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index 2a4002788..90c0e8479 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -294,9 +294,9 @@ request_name_cb (GObject *source_object, request_name_reply = 0; result = NULL; - result = g_dbus_connection_invoke_method_finish (client->connection, - res, - NULL); + result = g_dbus_connection_call_finish (client->connection, + res, + NULL); if (result != NULL) { g_variant_get (result, "(u)", &request_name_reply); @@ -395,19 +395,19 @@ has_connection (Client *client) client); /* attempt to acquire the name */ - g_dbus_connection_invoke_method (client->connection, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "RequestName", /* method name */ - g_variant_new ("(su)", - client->name, - client->flags), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) request_name_cb, - client_ref (client)); + g_dbus_connection_call (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "RequestName", /* method name */ + g_variant_new ("(su)", + client->name, + client->flags), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) request_name_cb, + client_ref (client)); } @@ -676,16 +676,16 @@ g_bus_unown_name (guint owner_id) * I believe this is a bug in the bus daemon. */ error = NULL; - result = g_dbus_connection_invoke_method_sync (client->connection, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "ReleaseName", /* method name */ - g_variant_new ("(s)", client->name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_connection_call_sync (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "ReleaseName", /* method name */ + g_variant_new ("(s)", client->name), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); if (result == NULL) { g_warning ("Error releasing name %s: %s", client->name, error->message); diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 4e33e8356..3d96ee0a9 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -331,9 +331,9 @@ get_name_owner_cb (GObject *source_object, name_owner = NULL; result = NULL; - result = g_dbus_connection_invoke_method_finish (client->connection, - res, - NULL); + result = g_dbus_connection_call_finish (client->connection, + res, + NULL); if (result != NULL) { g_variant_get (result, "(s)", &name_owner); @@ -362,17 +362,17 @@ get_name_owner_cb (GObject *source_object, static void invoke_get_name_owner (Client *client) { - g_dbus_connection_invoke_method (client->connection, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetNameOwner", /* method name */ - g_variant_new ("(s)", client->name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) get_name_owner_cb, - client_ref (client)); + g_dbus_connection_call (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetNameOwner", /* method name */ + g_variant_new ("(s)", client->name), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) get_name_owner_cb, + client_ref (client)); } /* ---------------------------------------------------------------------------------------------------- */ @@ -387,9 +387,9 @@ start_service_by_name_cb (GObject *source_object, result = NULL; - result = g_dbus_connection_invoke_method_finish (client->connection, - res, - NULL); + result = g_dbus_connection_call_finish (client->connection, + res, + NULL); if (result != NULL) { guint32 start_service_result; @@ -452,17 +452,17 @@ has_connection (Client *client) if (client->flags & G_BUS_NAME_WATCHER_FLAGS_AUTO_START) { - g_dbus_connection_invoke_method (client->connection, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "StartServiceByName", /* method name */ - g_variant_new ("(su)", client->name, 0), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) start_service_by_name_cb, - client_ref (client)); + g_dbus_connection_call (client->connection, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "StartServiceByName", /* method name */ + g_variant_new ("(su)", client->name, 0), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) start_service_by_name_cb, + client_ref (client)); } else { diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 1ed7ef400..c851cc168 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -359,8 +359,8 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) * GDBusProxy:g-default-timeout: * * The timeout to use if -1 (specifying default timeout) is passed - * as @timeout_msec in the g_dbus_proxy_invoke_method() and - * g_dbus_proxy_invoke_method_sync() functions. + * as @timeout_msec in the g_dbus_proxy_call() and + * g_dbus_proxy_call_sync() functions. * * This allows applications to set a proxy-wide timeout for all * remote method invocations on the proxy. If this property is -1, @@ -752,16 +752,16 @@ initable_init (GInitable *initable, if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) { /* load all properties synchronously */ - result = g_dbus_connection_invoke_method_sync (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->object_path, - "org.freedesktop.DBus.Properties", - "GetAll", - g_variant_new ("(s)", proxy->priv->interface_name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, /* timeout */ - cancellable, - error); + result = g_dbus_connection_call_sync (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", proxy->priv->interface_name), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + cancellable, + error); if (result == NULL) goto out; @@ -796,9 +796,9 @@ get_all_cb (GDBusConnection *connection, GError *error; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); if (result == NULL) { g_simple_async_result_set_from_error (simple, error); @@ -833,17 +833,17 @@ async_initable_init_async (GAsyncInitable *initable, if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) { /* load all properties asynchronously */ - g_dbus_connection_invoke_method (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->object_path, - "org.freedesktop.DBus.Properties", - "GetAll", - g_variant_new ("(s)", proxy->priv->interface_name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, /* timeout */ - cancellable, - (GAsyncReadyCallback) get_all_cb, - simple); + g_dbus_connection_call (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", proxy->priv->interface_name), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + cancellable, + (GAsyncReadyCallback) get_all_cb, + simple); } else { @@ -1148,8 +1148,8 @@ g_dbus_proxy_get_interface_name (GDBusProxy *proxy) * @proxy: A #GDBusProxy. * * Gets the timeout to use if -1 (specifying default timeout) is - * passed as @timeout_msec in the g_dbus_proxy_invoke_method() and - * g_dbus_proxy_invoke_method_sync() functions. + * passed as @timeout_msec in the g_dbus_proxy_call() and + * g_dbus_proxy_call_sync() functions. * * See the #GDBusProxy:g-default-timeout property for more details. * @@ -1170,8 +1170,8 @@ g_dbus_proxy_get_default_timeout (GDBusProxy *proxy) * @timeout_msec: Timeout in milliseconds. * * Sets the timeout to use if -1 (specifying default timeout) is - * passed as @timeout_msec in the g_dbus_proxy_invoke_method() and - * g_dbus_proxy_invoke_method_sync() functions. + * passed as @timeout_msec in the g_dbus_proxy_call() and + * g_dbus_proxy_call_sync() functions. * * See the #GDBusProxy:g-default-timeout property for more details. * @@ -1282,9 +1282,9 @@ reply_cb (GDBusConnection *connection, GError *error; error = NULL; - value = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + value = g_dbus_connection_call_finish (connection, + res, + &error); if (error != NULL) { g_simple_async_result_set_from_error (simple, @@ -1359,11 +1359,11 @@ validate_method_return (const char *method_name, } /** - * g_dbus_proxy_invoke_method: + * g_dbus_proxy_call: * @proxy: A #GDBusProxy. * @method_name: Name of method to invoke. * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters. - * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @flags: Flags from the #GDBusCallFlags enumeration. * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout. * @cancellable: A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't @@ -1386,17 +1386,17 @@ validate_method_return (const char *method_name, * This is an asynchronous method. When the operation is finished, @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_proxy_invoke_method_finish() to get the result of the operation. - * See g_dbus_proxy_invoke_method_sync() for the + * g_dbus_proxy_call_finish() to get the result of the operation. + * See g_dbus_proxy_call_sync() for the * synchronous version of this method. * * Since: 2.26 */ void -g_dbus_proxy_invoke_method (GDBusProxy *proxy, +g_dbus_proxy_call (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, - GDBusInvokeMethodFlags flags, + GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, @@ -1418,7 +1418,7 @@ g_dbus_proxy_invoke_method (GDBusProxy *proxy, simple = g_simple_async_result_new (G_OBJECT (proxy), callback, user_data, - g_dbus_proxy_invoke_method); + g_dbus_proxy_call); was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name); target_method_name = was_split ? split_method_name : method_name; @@ -1429,28 +1429,28 @@ g_dbus_proxy_invoke_method (GDBusProxy *proxy, /* Just warn here */ expected_method_info = lookup_method_info_or_warn (proxy, target_method_name); - g_dbus_connection_invoke_method (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->object_path, - target_interface_name, - target_method_name, - parameters, - flags, - timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec, - cancellable, - (GAsyncReadyCallback) reply_cb, - simple); + g_dbus_connection_call (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + target_interface_name, + target_method_name, + parameters, + flags, + timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec, + cancellable, + (GAsyncReadyCallback) reply_cb, + simple); g_free (split_interface_name); } /** - * g_dbus_proxy_invoke_method_finish: + * g_dbus_proxy_call_finish: * @proxy: A #GDBusProxy. - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_invoke_method(). + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_call(). * @error: Return location for error or %NULL. * - * Finishes an operation started with g_dbus_proxy_invoke_method(). + * Finishes an operation started with g_dbus_proxy_call(). * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with * return values. Free with g_variant_unref(). @@ -1458,7 +1458,7 @@ g_dbus_proxy_invoke_method (GDBusProxy *proxy, * Since: 2.26 */ GVariant * -g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, +g_dbus_proxy_call_finish (GDBusProxy *proxy, GAsyncResult *res, GError **error) { @@ -1471,7 +1471,7 @@ g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_invoke_method); + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_call); value = NULL; @@ -1497,11 +1497,11 @@ g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, } /** - * g_dbus_proxy_invoke_method_sync: + * g_dbus_proxy_call_sync: * @proxy: A #GDBusProxy. * @method_name: Name of method to invoke. * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters. - * @flags: Flags from the #GDBusInvokeMethodFlags enumeration. + * @flags: Flags from the #GDBusCallFlags enumeration. * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. @@ -1520,7 +1520,7 @@ g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, * %G_IO_ERROR_INVALID_ARGUMENT. * * The calling thread is blocked until a reply is received. See - * g_dbus_proxy_invoke_method() for the asynchronous version of this + * g_dbus_proxy_call() for the asynchronous version of this * method. * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with @@ -1529,10 +1529,10 @@ g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, * Since: 2.26 */ GVariant * -g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, +g_dbus_proxy_call_sync (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, - GDBusInvokeMethodFlags flags, + GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GError **error) @@ -1567,16 +1567,16 @@ g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, expected_method_info = NULL; } - ret = g_dbus_connection_invoke_method_sync (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->object_path, - target_interface_name, - target_method_name, - parameters, - flags, - timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec, - cancellable, - error); + ret = g_dbus_connection_call_sync (proxy->priv->connection, + proxy->priv->unique_bus_name, + proxy->priv->object_path, + target_interface_name, + target_method_name, + parameters, + flags, + timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec, + cancellable, + error); if (!validate_method_return (target_method_name, ret, expected_method_info, error)) { g_variant_unref (ret); diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index 1b7a3a457..9b65e27f0 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -126,21 +126,21 @@ GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *pr GError **error); gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, GError **error); -void g_dbus_proxy_invoke_method (GDBusProxy *proxy, +void g_dbus_proxy_call (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, - GDBusInvokeMethodFlags flags, + GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); -GVariant *g_dbus_proxy_invoke_method_finish (GDBusProxy *proxy, +GVariant *g_dbus_proxy_call_finish (GDBusProxy *proxy, GAsyncResult *res, GError **error); -GVariant *g_dbus_proxy_invoke_method_sync (GDBusProxy *proxy, +GVariant *g_dbus_proxy_call_sync (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, - GDBusInvokeMethodFlags flags, + GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GError **error); diff --git a/gio/gio.symbols b/gio/gio.symbols index 14e4f745d..e77274826 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -994,7 +994,7 @@ g_bus_name_watcher_flags_get_type G_GNUC_CONST g_dbus_proxy_flags_get_type G_GNUC_CONST g_dbus_connection_flags_get_type G_GNUC_CONST g_dbus_capability_flags_get_type G_GNUC_CONST -g_dbus_invoke_method_flags_get_type G_GNUC_CONST +g_dbus_call_flags_get_type G_GNUC_CONST g_dbus_message_type_get_type G_GNUC_CONST g_dbus_message_flags_get_type G_GNUC_CONST g_dbus_message_header_field_get_type G_GNUC_CONST @@ -1501,9 +1501,9 @@ g_dbus_connection_is_closed g_dbus_connection_set_exit_on_close g_dbus_connection_close g_dbus_connection_emit_signal -g_dbus_connection_invoke_method -g_dbus_connection_invoke_method_finish -g_dbus_connection_invoke_method_sync +g_dbus_connection_call +g_dbus_connection_call_finish +g_dbus_connection_call_sync g_dbus_connection_signal_subscribe g_dbus_connection_signal_unsubscribe g_dbus_connection_add_filter @@ -1673,9 +1673,9 @@ g_dbus_proxy_get_object_path g_dbus_proxy_get_unique_bus_name g_dbus_proxy_set_default_timeout g_dbus_proxy_set_interface_info -g_dbus_proxy_invoke_method -g_dbus_proxy_invoke_method_finish -g_dbus_proxy_invoke_method_sync +g_dbus_proxy_call +g_dbus_proxy_call_finish +g_dbus_proxy_call_sync #endif #endif diff --git a/gio/gioenums.h b/gio/gioenums.h index f7b222fba..71ed59a7c 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -994,20 +994,20 @@ typedef enum { } GDBusCapabilityFlags; /** - * GDBusInvokeMethodFlags: - * @G_DBUS_INVOKE_METHOD_FLAGS_NONE: No flags set. - * @G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START: The bus must not launch + * GDBusCallFlags: + * @G_DBUS_CALL_FLAGS_NONE: No flags set. + * @G_DBUS_CALL_FLAGS_NO_AUTO_START: The bus must not launch * an owner for the destination name in response to this method * invocation. * - * Flags used in g_dbus_connection_invoke_method() and similar APIs. + * Flags used in g_dbus_connection_call() and similar APIs. * * Since: 2.26 */ typedef enum { - G_DBUS_INVOKE_METHOD_FLAGS_NONE = 0, - G_DBUS_INVOKE_METHOD_FLAGS_NO_AUTO_START = (1<<0), -} GDBusInvokeMethodFlags; + G_DBUS_CALL_FLAGS_NONE = 0, + G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0), +} GDBusCallFlags; /** * GDBusMessageType: diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c index 025de1d4c..266512f3e 100644 --- a/gio/tests/gdbus-connection.c +++ b/gio/tests/gdbus-connection.c @@ -118,9 +118,9 @@ msg_cb_expect_error_disconnected (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); g_assert (!g_dbus_error_is_remote_error (error)); g_error_free (error); @@ -138,9 +138,9 @@ msg_cb_expect_error_unknown_method (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); g_assert (g_dbus_error_is_remote_error (error)); g_assert (result == NULL); @@ -157,9 +157,9 @@ msg_cb_expect_success (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_unref (result); @@ -176,9 +176,9 @@ msg_cb_expect_error_cancelled (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_assert (!g_dbus_error_is_remote_error (error)); g_error_free (error); @@ -196,9 +196,9 @@ msg_cb_expect_error_cancelled_2 (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_assert (!g_dbus_error_is_remote_error (error)); g_error_free (error); @@ -229,67 +229,67 @@ test_connection_send (void) */ ca = g_cancellable_new (); g_cancellable_cancel (ca); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled, - NULL); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + NULL); g_main_loop_run (loop); g_object_unref (ca); /* * Check that we get a reply to the GetId() method call. */ - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_success, - NULL); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_success, + NULL); g_main_loop_run (loop); /* * Check that we get an error reply to the NonExistantMethod() method call. */ - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "NonExistantMethod", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_error_unknown_method, - NULL); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NonExistantMethod", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_error_unknown_method, + NULL); g_main_loop_run (loop); /* * Check that cancellation works when the message is already in flight. */ ca = g_cancellable_new (); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2, - NULL); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2, + NULL); g_cancellable_cancel (ca); g_main_loop_run (loop); g_object_unref (ca); @@ -302,17 +302,17 @@ test_connection_send (void) _g_assert_signal_received (c, "closed"); g_assert (g_dbus_connection_is_closed (c)); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_error_disconnected, - NULL); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_error_disconnected, + NULL); g_main_loop_run (loop); _g_object_wait_for_single_ref (c); @@ -456,16 +456,16 @@ test_connection_signals (void) * * To ensure this is not the case, do a synchronous call on c1. */ - result = g_dbus_connection_invoke_method_sync (c1, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, /* parameters */ - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_connection_call_sync (c1, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, /* parameters */ + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_unref (result); diff --git a/gio/tests/gdbus-example-peer.c b/gio/tests/gdbus-example-peer.c index a1041d9c7..3a1385878 100644 --- a/gio/tests/gdbus-example-peer.c +++ b/gio/tests/gdbus-example-peer.c @@ -287,16 +287,16 @@ main (int argc, char *argv[]) g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); greeting = g_strdup_printf ("Hey, it's %" G_GUINT64_FORMAT " already!", (guint64) time (NULL)); - value = g_dbus_connection_invoke_method_sync (connection, - NULL, /* bus_name */ - "/org/gtk/GDBus/TestObject", - "org.gtk.GDBus.TestPeerInterface", - "HelloWorld", - g_variant_new ("(s)", greeting), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_connection_call_sync (connection, + NULL, /* bus_name */ + "/org/gtk/GDBus/TestObject", + "org.gtk.GDBus.TestPeerInterface", + "HelloWorld", + g_variant_new ("(s)", greeting), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); if (value == NULL) { g_printerr ("Error invoking HelloWorld(): %s\n", error->message); diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c index 886e7147c..77d7303c4 100644 --- a/gio/tests/gdbus-export.c +++ b/gio/tests/gdbus-export.c @@ -320,7 +320,7 @@ introspect_callback (GDBusProxy *proxy, GError *error; error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, + result = g_dbus_proxy_call_finish (proxy, res, &error); g_assert_no_error (error); @@ -359,14 +359,14 @@ get_nodes_at (GDBusConnection *c, /* do this async to avoid libdbus-1 deadlocks */ xml_data = NULL; - g_dbus_proxy_invoke_method (proxy, - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) introspect_callback, - &xml_data); + g_dbus_proxy_call (proxy, + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); g_main_loop_run (loop); g_assert (xml_data != NULL); @@ -416,14 +416,14 @@ has_interface (GDBusConnection *c, /* do this async to avoid libdbus-1 deadlocks */ xml_data = NULL; - g_dbus_proxy_invoke_method (proxy, - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) introspect_callback, - &xml_data); + g_dbus_proxy_call (proxy, + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); g_main_loop_run (loop); g_assert (xml_data != NULL); @@ -466,14 +466,14 @@ count_interfaces (GDBusConnection *c, /* do this async to avoid libdbus-1 deadlocks */ xml_data = NULL; - g_dbus_proxy_invoke_method (proxy, - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) introspect_callback, - &xml_data); + g_dbus_proxy_call (proxy, + "Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); g_main_loop_run (loop); g_assert (xml_data != NULL); @@ -501,9 +501,9 @@ dyna_create_callback (GDBusProxy *proxy, GError *error; error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); + result = g_dbus_proxy_call_finish (proxy, + res, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_unref (result); @@ -537,14 +537,14 @@ dyna_create (GDBusConnection *c, g_assert (proxy != NULL); /* do this async to avoid libdbus-1 deadlocks */ - g_dbus_proxy_invoke_method (proxy, - "DynaCyber", - g_variant_new ("()"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) dyna_create_callback, - NULL); + g_dbus_proxy_call (proxy, + "DynaCyber", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) dyna_create_callback, + NULL); g_main_loop_run (loop); g_assert_no_error (error); @@ -760,26 +760,26 @@ test_dispatch_thread_func (gpointer user_data) /* generic interfaces */ error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Peer.Ping", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Peer.Ping", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (value != NULL); g_variant_unref (value); /* user methods */ error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "Method1", - g_variant_new ("(s)", "winwinwin"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "Method1", + g_variant_new ("(s)", "winwinwin"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (value != NULL); g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)"))); @@ -788,39 +788,39 @@ test_dispatch_thread_func (gpointer user_data) g_variant_unref (value); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "Method2", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "Method2", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.example.SomeError: How do you like them apples, buddy!"); g_error_free (error); g_assert (value == NULL); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "Method2", - g_variant_new ("(s)", "failfailfail"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "Method2", + g_variant_new ("(s)", "failfailfail"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Signature of message, `s', does not match expected signature `'"); g_error_free (error); g_assert (value == NULL); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "NonExistantMethod", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "NonExistantMethod", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method `NonExistantMethod'"); g_error_free (error); @@ -828,15 +828,15 @@ test_dispatch_thread_func (gpointer user_data) /* user properties */ error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Get", - g_variant_new ("(ss)", - "org.example.Foo", - "PropertyUno"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "PropertyUno"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (value != NULL); g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(v)"))); @@ -846,76 +846,76 @@ test_dispatch_thread_func (gpointer user_data) g_variant_unref (value); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Get", - g_variant_new ("(ss)", - "org.example.Foo", - "ThisDoesntExist"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "ThisDoesntExist"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert (value == NULL); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property `ThisDoesntExist'"); g_error_free (error); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Get", - g_variant_new ("(ss)", - "org.example.Foo", - "NotReadable"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "NotReadable"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert (value == NULL); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotReadable' is not readable"); g_error_free (error); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Set", - g_variant_new ("(ssv)", - "org.example.Foo", - "NotReadable", - g_variant_new_string ("But Writable you are!")), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.example.Foo", + "NotReadable", + g_variant_new_string ("But Writable you are!")), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert (value == NULL); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value `NotReadable' to the property `'But Writable you are!''"); g_error_free (error); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Set", - g_variant_new ("(ssv)", - "org.example.Foo", - "NotWritable", - g_variant_new_uint32 (42)), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.example.Foo", + "NotWritable", + g_variant_new_uint32 (42)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert (value == NULL); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotWritable' is not writable"); g_error_free (error); error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.GetAll", - g_variant_new ("(s)", - "org.example.Foo"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + value = g_dbus_proxy_call_sync (foo_proxy, + "org.freedesktop.DBus.Properties.GetAll", + g_variant_new ("(s)", + "org.example.Foo"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (value != NULL); g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(a{sv})"))); diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c index 88e368d72..a93c776de 100644 --- a/gio/tests/gdbus-introspection.c +++ b/gio/tests/gdbus-introspection.c @@ -53,13 +53,13 @@ introspection_on_proxy_appeared (GDBusConnection *connection, /* * Invoke Introspect(), then parse the output. */ - result = g_dbus_proxy_invoke_method_sync (proxy, - "org.freedesktop.DBus.Introspectable.Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "org.freedesktop.DBus.Introspectable.Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_get (result, "(s)", &xml_data); diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c index 7edd6498e..b48d695ad 100644 --- a/gio/tests/gdbus-names.c +++ b/gio/tests/gdbus-names.c @@ -170,16 +170,16 @@ test_bus_own_name (void) c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); g_assert (c != NULL); g_assert (!g_dbus_connection_is_closed (c)); - result = g_dbus_connection_invoke_method_sync (c, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "NameHasOwner", /* method name */ - g_variant_new ("(s)", name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_connection_call_sync (c, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NameHasOwner", /* method name */ + g_variant_new ("(s)", name), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_get (result, "(b)", &name_has_owner_reply); @@ -195,16 +195,16 @@ test_bus_own_name (void) /* * Check that the name was actually released. */ - result = g_dbus_connection_invoke_method_sync (c, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "NameHasOwner", /* method name */ - g_variant_new ("(s)", name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_connection_call_sync (c, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NameHasOwner", /* method name */ + g_variant_new ("(s)", name), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_get (result, "(b)", &name_has_owner_reply); diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 229f70e8f..e96666a8c 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -537,13 +537,13 @@ test_peer (void) /* try invoking a method */ error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloPeer", - g_variant_new ("(s)", "Hey Peer!"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - &error); + result = g_dbus_proxy_call_sync (proxy, + "HelloPeer", + g_variant_new ("(s)", "Hey Peer!"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); g_assert_no_error (error); g_variant_get (result, "(s)", &s); g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'."); @@ -556,14 +556,14 @@ test_peer (void) G_CALLBACK (on_proxy_signal_received), &data); g_assert (!data.signal_received); - g_dbus_proxy_invoke_method (proxy, - "EmitSignal", - NULL, /* no arguments */ - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - NULL, /* GAsyncReadyCallback - we don't care about the result */ - NULL); /* user_data */ + g_dbus_proxy_call (proxy, + "EmitSignal", + NULL, /* no arguments */ + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + NULL, /* GAsyncReadyCallback - we don't care about the result */ + NULL); /* user_data */ g_main_loop_run (loop); g_assert (data.signal_received); g_assert_cmpint (data.num_method_calls, ==, 2); @@ -679,13 +679,13 @@ test_peer (void) server = NULL; error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloPeer", - g_variant_new ("(s)", "Hey Again Peer!"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - &error); + result = g_dbus_proxy_call_sync (proxy, + "HelloPeer", + g_variant_new ("(s)", "Hey Again Peer!"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); g_assert_no_error (error); g_variant_get (result, "(s)", &s); g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'."); diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c index f9e17432a..5df6ad532 100644 --- a/gio/tests/gdbus-proxy.c +++ b/gio/tests/gdbus-proxy.c @@ -46,13 +46,13 @@ test_methods (GDBusConnection *connection, /* check that we can invoke a method */ error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloWorld", - g_variant_new ("(s)", "Hey"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "HelloWorld", + g_variant_new ("(s)", "Hey"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)"); @@ -61,13 +61,13 @@ test_methods (GDBusConnection *connection, g_variant_unref (result); /* Check that we can completely recover the returned error */ - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloWorld", - g_variant_new ("(s)", "Yo"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "HelloWorld", + g_variant_new ("(s)", "Yo"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); g_assert (g_dbus_error_is_remote_error (error)); g_assert (g_dbus_error_is_remote_error (error)); @@ -81,13 +81,13 @@ test_methods (GDBusConnection *connection, /* Check that we get a timeout if the method handling is taking longer than timeout */ error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "Sleep", - g_variant_new ("(i)", 500 /* msec */), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 100 /* msec */, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_CALL_FLAGS_NONE, + 100 /* msec */, + NULL, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_assert (!g_dbus_error_is_remote_error (error)); g_assert (result == NULL); @@ -97,13 +97,13 @@ test_methods (GDBusConnection *connection, g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1); /* the default timeout is 25000 msec so this should work */ - result = g_dbus_proxy_invoke_method_sync (proxy, - "Sleep", - g_variant_new ("(i)", 500 /* msec */), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */ - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_CALL_FLAGS_NONE, + -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */ + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); @@ -112,13 +112,13 @@ test_methods (GDBusConnection *connection, /* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */ g_dbus_proxy_set_default_timeout (proxy, 250); g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250); - result = g_dbus_proxy_invoke_method_sync (proxy, - "Sleep", - g_variant_new ("(i)", 500 /* msec */), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, /* use proxy default (e.g. 250 msec) */ - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_CALL_FLAGS_NONE, + -1, /* use proxy default (e.g. 250 msec) */ + NULL, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_assert (!g_dbus_error_is_remote_error (error)); g_assert (result == NULL); @@ -166,15 +166,15 @@ test_properties (GDBusConnection *connection, * is received. Also check that the cache is updated. */ variant2 = g_variant_new_byte (42); - result = g_dbus_proxy_invoke_method_sync (proxy, - "FrobSetProperty", - g_variant_new ("(sv)", - "y", - variant2), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "FrobSetProperty", + g_variant_new ("(sv)", + "y", + variant2), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); @@ -222,9 +222,9 @@ test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy, GVariant *result; error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); + result = g_dbus_proxy_call_finish (proxy, + res, + &error); g_assert_no_error (error); g_assert (result != NULL); g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); @@ -263,15 +263,15 @@ test_signals (GDBusConnection *connection, G_CALLBACK (test_proxy_signals_on_signal), s); - result = g_dbus_proxy_invoke_method_sync (proxy, - "EmitSignal", - g_variant_new ("(so)", - "Accept the next proposition you hear", - "/some/path"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "EmitSignal", + g_variant_new ("(so)", + "Accept the next proposition you hear", + "/some/path"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_no_error (error); g_assert (result != NULL); g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); @@ -296,16 +296,16 @@ test_signals (GDBusConnection *connection, "g-signal", G_CALLBACK (test_proxy_signals_on_signal), s); - g_dbus_proxy_invoke_method (proxy, - "EmitSignal", - g_variant_new ("(so)", - "You will make a great programmer", - "/some/other/path"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb, - &data); + g_dbus_proxy_call (proxy, + "EmitSignal", + g_variant_new ("(so)", + "You will make a great programmer", + "/some/other/path"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb, + &data); g_main_loop_run (data.internal_loop); g_main_loop_unref (data.internal_loop); g_assert_cmpstr (s->str, @@ -324,13 +324,13 @@ test_bogus_method_return (GDBusConnection *connection, GError *error = NULL; GVariant *result; - result = g_dbus_proxy_invoke_method_sync (proxy, - "PairReturn", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (proxy, + "PairReturn", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert (result == NULL); } diff --git a/gio/tests/gdbus-threading.c b/gio/tests/gdbus-threading.c index cfeafe199..1de395c3b 100644 --- a/gio/tests/gdbus-threading.c +++ b/gio/tests/gdbus-threading.c @@ -52,9 +52,9 @@ msg_cb_expect_success (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_unref (result); @@ -74,9 +74,9 @@ msg_cb_expect_error_cancelled (GDBusConnection *connection, GVariant *result; error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); + result = g_dbus_connection_call_finish (connection, + res, + &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_assert (!g_dbus_error_is_remote_error (error)); g_error_free (error); @@ -131,17 +131,17 @@ test_delivery_in_thread_func (gpointer _data) /* * Check that we get a reply to the GetId() method call. */ - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_success, - &data); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_success, + &data); g_main_loop_run (thread_loop); /* @@ -151,17 +151,17 @@ test_delivery_in_thread_func (gpointer _data) */ ca = g_cancellable_new (); g_cancellable_cancel (ca); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled, - &data); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + &data); g_main_loop_run (thread_loop); g_object_unref (ca); @@ -169,17 +169,17 @@ test_delivery_in_thread_func (gpointer _data) * Check that cancellation works when the message is already in flight. */ ca = g_cancellable_new (); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled, - &data); + g_dbus_connection_call (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + &data); g_cancellable_cancel (ca); g_main_loop_run (thread_loop); g_object_unref (ca); @@ -269,9 +269,9 @@ sleep_cb (GDBusProxy *proxy, GVariant *result; error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); + result = g_dbus_proxy_call_finish (proxy, + res, + &error); g_assert_no_error (error); g_assert (result != NULL); g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); @@ -302,14 +302,14 @@ test_sleep_in_thread_func (gpointer _data) if (data->async) { //g_debug ("invoking async (%p)", g_thread_self ()); - g_dbus_proxy_invoke_method (data->proxy, - "Sleep", - g_variant_new ("(i)", data->msec), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) sleep_cb, - data); + g_dbus_proxy_call (data->proxy, + "Sleep", + g_variant_new ("(i)", data->msec), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) sleep_cb, + data); g_main_loop_run (data->thread_loop); g_print ("A"); //g_debug ("done invoking async (%p)", g_thread_self ()); @@ -321,13 +321,13 @@ test_sleep_in_thread_func (gpointer _data) error = NULL; //g_debug ("invoking sync (%p)", g_thread_self ()); - result = g_dbus_proxy_invoke_method_sync (data->proxy, - "Sleep", - g_variant_new ("(i)", data->msec), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); + result = g_dbus_proxy_call_sync (data->proxy, + "Sleep", + g_variant_new ("(i)", data->msec), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); g_print ("S"); //g_debug ("done invoking sync (%p)", g_thread_self ()); g_assert_no_error (error); From 7e8b07ae3be5ce63ba17183a410ac8512a29cb13 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 10 May 2010 13:31:54 -0400 Subject: [PATCH 27/76] GDBus: Use GVariant instead of GHashTable for GDBusProxy::g-properties-changed --- gio/gdbusconnection.c | 13 ------------- gio/gdbusproxy.c | 24 +++++++++++------------- gio/gdbusproxy.h | 2 +- gio/tests/gdbus-example-watch-proxy.c | 21 +++++++++++++++------ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 2de63821a..f26d48eee 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -28,19 +28,6 @@ * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular * the mechanism VFuncs need to be able to set an error. * - * - The GDBusProxy::g-properties-changed signal currently looks like this - * - * void user_function (GDBusProxy *proxy, - * GHashTable *changed_properties, - * gpointer user_data); - * - * which is problematic because some people frown upon GHashTable - * usage in public API (in particular some of the JS people). Maybe we - * need to rework it, maybe it doesn't matter since GDBusProxy is - * a low-level API and, for C code, we expect code generators to - * spit out subclasses that automatically hook up to this signal - * and does g_object_notify() anyway? Hmm... - * * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable * to specify where the nonce is stored. This will allow people to use * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index c851cc168..cb0a2f47b 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -387,7 +387,7 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) /** * GDBusProxy::g-properties-changed: * @proxy: The #GDBusProxy emitting the signal. - * @changed_properties: A #GHashTable containing the properties that changed. + * @changed_properties: A #GVariant containing the properties that changed. * * Emitted when one or more D-Bus properties on @proxy changes. The cached properties * are already replaced when this signal fires. @@ -403,7 +403,7 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, - G_TYPE_HASH_TABLE); + G_TYPE_VARIANT); /** * GDBusProxy::g-signal: @@ -587,7 +587,8 @@ on_properties_changed (GDBusConnection *connection, const gchar *interface_name_for_signal; GVariantIter *iter; GVariant *item; - GHashTable *changed_properties; + GVariant *changed_properties; + GVariantBuilder *builder; error = NULL; iter = NULL; @@ -617,11 +618,7 @@ on_properties_changed (GDBusConnection *connection, if (g_strcmp0 (interface_name_for_signal, proxy->priv->interface_name) != 0) goto out; - changed_properties = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) g_variant_unref); - + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); while ((item = g_variant_iter_next_value (iter))) { const gchar *key; @@ -636,16 +633,17 @@ on_properties_changed (GDBusConnection *connection, g_strdup (key), value); /* steals value */ - g_hash_table_insert (changed_properties, - g_strdup (key), - g_variant_ref (value)); + g_variant_builder_add (builder, + "{sv}", + g_strdup (key), + g_variant_ref (value)); } - + changed_properties = g_variant_builder_end (builder); /* emit signal */ g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL], 0, changed_properties); - g_hash_table_unref (changed_properties); + g_variant_unref (changed_properties); out: if (iter != NULL) diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index 9b65e27f0..ee9f62eda 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -70,7 +70,7 @@ struct _GDBusProxyClass /*< public >*/ /* Signals */ void (*g_properties_changed) (GDBusProxy *proxy, - GHashTable *changed_properties); + GVariant *changed_properties); void (*g_signal) (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index 9a6176ea5..7060b21ae 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -56,19 +56,28 @@ print_properties (GDBusProxy *proxy) static void on_properties_changed (GDBusProxy *proxy, - GHashTable *changed_properties, + GVariant *changed_properties, gpointer user_data) { - GHashTableIter iter; - const gchar *key; - GVariant *value; + GVariantIter *iter; + GVariant *item; g_print (" *** Properties Changed:\n"); - g_hash_table_iter_init (&iter, changed_properties); - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) + g_variant_get (changed_properties, + "a{sv}", + &iter); + while ((item = g_variant_iter_next_value (iter))) { + const gchar *key; + GVariant *value; gchar *value_str; + + g_variant_get (item, + "{sv}", + &key, + &value); + value_str = g_variant_print (value, TRUE); g_print (" %s -> %s\n", key, value_str); g_free (value_str); From adf50912ddb29c8c6b36702df1162e6211765dea Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 10 May 2010 14:07:13 -0400 Subject: [PATCH 28/76] GDBus Add TODO items about finding and launching bus instances --- gio/gdbusconnection.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index f26d48eee..f81c86b27 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -28,6 +28,34 @@ * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular * the mechanism VFuncs need to be able to set an error. * + * - Need to document usage of DBUS_SYSTEM_ADDRESS and + * DBUS_SESSION_BUS_ADDRESS environment variables. Also need to + * document other mechanisms/sources for determining the D-Bus + * address of a well-known bus. + * + * - e.g. on Win32 we need code like from here + * + * http://cgit.freedesktop.org/~david/gdbus-standalone/tree/gdbus/gdbusaddress.c#n900 + * + * that was never copied over here because it originally was copy-paste + * from the GPLv2 / AFL 2.1 libdbus sources. + * + * - on OS X we need to look in launchd for the address + * + * https://bugs.freedesktop.org/show_bug.cgi?id=14259 + * + * - on X11 we need to look in a X11 property on the X server + * - (we can also just use dbus-launch(1) from the D-Bus + * distribution) + * + * - (ideally) this requires D-Bus spec work because none of + * this has never really been specced out properly (excect + * the X11 bits) + * + * - Related to the above, we also need to be able to launch a message bus + * instance.... Since we don't want to write our own bus daemon we should + * launch dbus-daemon(1) (thus: Win32 and OS X need to bundle it) + * * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable * to specify where the nonce is stored. This will allow people to use * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir From 6e23b0b7850c170405aa25d9441a9cd8cc05a38b Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 10 May 2010 14:43:08 -0400 Subject: [PATCH 29/76] GDBus: Add TODO item about a need to validate data / messages --- gio/gdbusconnection.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index f81c86b27..a8301ae72 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -23,6 +23,11 @@ /* * TODO for GDBus: * + * - Validate all data (e.g. UTF-8) and check all the required D-Bus headers + * are present and forbidden ones aren't + * - When writing: g_dbus_message_to_blob() + * - When reading: g_dbus_message_new_from_blob() + * * - would be nice to expose GDBusAuthMechanism and an extension point * * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular From 2d208c9d364369d68a54cfd0682e17f2ce771db5 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 10 May 2010 16:20:59 -0400 Subject: [PATCH 30/76] GDBus: In gdbus(1), try Get() if GetAll() fails This fixes a problem with services that doesn't implement GetAll() for one reason or another. $ gdbus introspect --session --dest org.freedesktop.ReserveDevice1.Audio0 --object-path /org/freedesktop/ReserveDevice1/Audio0 node /org/freedesktop/ReserveDevice1/Audio0 { interface org.freedesktop.ReserveDevice1 { methods: RequestRelease(in i priority, out b result); properties: readonly i Priority = 0; readonly s ApplicationName = 'PulseAudio Sound Server'; readonly s ApplicationDeviceName = 'Internal Audio Analog Stereo'; }; interface org.freedesktop.DBus.Properties { methods: Get(in s interface, in s property, out v value); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s data); }; }; --- gio/gdbus-tool.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index ddec98517..ce5e5dba1 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -1044,6 +1044,34 @@ dump_interface (GDBusConnection *c, } g_variant_unref (result); } + else + { + guint n; + for (n = 0; o->properties != NULL && o->properties[n] != NULL; n++) + { + result = g_dbus_connection_call_sync (c, + name, + object_path, + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", o->name, o->properties[n]->name), + G_DBUS_CALL_FLAGS_NONE, + 3000, + NULL, + NULL); + if (result != NULL) + { + GVariant *property_value; + g_variant_get (result, + "(v)", + &property_value); + g_hash_table_insert (properties, + g_strdup (o->properties[n]->name), + g_variant_ref (property_value)); + g_variant_unref (result); + } + } + } } g_print ("%*sinterface %s {\n", indent, "", o->name); From 8c523c069b79a746c2dcdfe7e253513ff489cd39 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Tue, 11 May 2010 12:04:37 -0400 Subject: [PATCH 31/76] GDBus: Update TODO list --- gio/gdbusconnection.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index a8301ae72..4921e971c 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -85,6 +85,34 @@ * 3 -> /proc/18068/fd * * e.g. not of much use. + * + * - GDBus High-Level docs + * - Proxy: properties, signals... + * - Connection: IOStream based, ::close, connection setup steps + * mainloop integration, threading + * - Differences from libdbus (extend "Migrating from") + * - the message handling thread + * - Using GVariant instead of GValue + * - Explain why the high-level API is a good thing and what + * kind of pitfalls it avoids + * - Export objects before claiming names + * - Talk about auto-starting services (cf. GBusNameWatcherFlags) + * + * - Mention in all API that the GVariant is sunk. Also mention + * when the returned GVariant is floating. + * + * - Small example snippet for each method where useful (won't + * have to compile) where it makes sense (e.g. connetion_call() + * and using a floating GVariant). + * + * - Consistent timeout handling (25s vs 30s?) + * + * - GDBusProxy subclass example + * + * - Update GDBusAuthObserver (is_same_user(), s/deny/authorize/) + * + * - Remove properties on GDBusMethodInvocation + * */ #include "config.h" From 9a2422b216263dd7bc4f27a98bc89d5ea6dfe791 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 15:42:15 -0400 Subject: [PATCH 32/76] Trivial: rename a file --- docs/reference/gio/Makefile.am | 4 ++-- docs/reference/gio/gio-docs.xml | 2 +- .../gio/{migrating-dbus-glib.xml => migrating-gdbus.xml} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename docs/reference/gio/{migrating-dbus-glib.xml => migrating-gdbus.xml} (100%) diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am index 23bcd90e0..59703a0f7 100644 --- a/docs/reference/gio/Makefile.am +++ b/docs/reference/gio/Makefile.am @@ -117,7 +117,7 @@ content_files = \ migrating-posix.xml \ migrating-gnome-vfs.xml \ migrating-gconf.xml \ - migrating-dbus-glib.xml \ + migrating-gdbus.xml \ gio-querymodules.xml \ glib-compile-schemas.xml\ gsettings.xml \ @@ -130,7 +130,7 @@ expand_content_files = \ migrating-posix.xml \ migrating-gnome-vfs.xml \ migrating-gconf.xml \ - migrating-dbus-glib.xml + migrating-gdbus.xml extra_files = \ version.xml.in \ diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 2b9a7f232..9f3a972cd 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -175,7 +175,7 @@ - + diff --git a/docs/reference/gio/migrating-dbus-glib.xml b/docs/reference/gio/migrating-gdbus.xml similarity index 100% rename from docs/reference/gio/migrating-dbus-glib.xml rename to docs/reference/gio/migrating-gdbus.xml From 54a57bb894d3c098bf972ecec71823b2822128b6 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 15:50:19 -0400 Subject: [PATCH 33/76] Strip copyright headers from examples These are included wholesale in the docs, and the copyright headers make them even more overwhelming. Plus, we don't have copyright headers on examples anywhere else. --- gio/tests/gdbus-example-own-name.c | 13 ------------- gio/tests/gdbus-example-peer.c | 13 ------------- gio/tests/gdbus-example-server.c | 13 ------------- gio/tests/gdbus-example-subtree.c | 13 ------------- gio/tests/gdbus-example-unix-fd-client.c | 13 ------------- gio/tests/gdbus-example-watch-name.c | 13 ------------- gio/tests/gdbus-example-watch-proxy.c | 13 ------------- 7 files changed, 91 deletions(-) diff --git a/gio/tests/gdbus-example-own-name.c b/gio/tests/gdbus-example-own-name.c index 733c29ea4..0466cd4c6 100644 --- a/gio/tests/gdbus-example-own-name.c +++ b/gio/tests/gdbus-example-own-name.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - #include static void diff --git a/gio/tests/gdbus-example-peer.c b/gio/tests/gdbus-example-peer.c index 3a1385878..118624f34 100644 --- a/gio/tests/gdbus-example-peer.c +++ b/gio/tests/gdbus-example-peer.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - /* Usage examples (modulo addresses / credentials). diff --git a/gio/tests/gdbus-example-server.c b/gio/tests/gdbus-example-server.c index fe667c040..eb0d6e459 100644 --- a/gio/tests/gdbus-example-server.c +++ b/gio/tests/gdbus-example-server.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - #include #include diff --git a/gio/tests/gdbus-example-subtree.c b/gio/tests/gdbus-example-subtree.c index e8da6da88..1633accde 100644 --- a/gio/tests/gdbus-example-subtree.c +++ b/gio/tests/gdbus-example-subtree.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - #include #include #include diff --git a/gio/tests/gdbus-example-unix-fd-client.c b/gio/tests/gdbus-example-unix-fd-client.c index b89da8cb3..21e199e89 100644 --- a/gio/tests/gdbus-example-unix-fd-client.c +++ b/gio/tests/gdbus-example-unix-fd-client.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - #include #include diff --git a/gio/tests/gdbus-example-watch-name.c b/gio/tests/gdbus-example-watch-name.c index 39d0aed8d..769419b22 100644 --- a/gio/tests/gdbus-example-watch-name.c +++ b/gio/tests/gdbus-example-watch-name.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - #include static gchar *opt_name = NULL; diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index 7060b21ae..db543a836 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -1,16 +1,3 @@ -/* - * Copyright © 2010 Red Hat, Inc. - * - * This program 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 licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: David Zeuthen - */ - #include static gchar *opt_name = NULL; From 8d66ede1abbc4b84bcf13c4420719cb06fbe3b96 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 17:15:11 -0400 Subject: [PATCH 34/76] More gdbus migration stuff --- docs/reference/gio/migrating-gdbus.xml | 89 +++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 70e6f2d5b..1f33e0229 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -42,9 +42,9 @@ dbus-glibGDBus - #DBusGConnection#GDBusConnection - #DBusGProxy#GDBusProxy - #DBusGMethodInvocation#GDBusMethodInvocatoin + #DBusGConnection#GDBusConnection + #DBusGProxy#GDBusProxy + #DBusGMethodInvocation#GDBusMethodInvocatoin dbus_g_bus_get()g_bus_get_sync(), also see g_bus_get() dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see @@ -67,4 +67,87 @@ + +
+ Owning bus names + + Using dbus-glib, you typically call RequestName manually + to own a name, like in the following excerpt: + message); + g_error_free (error); + } + else { + g_warning ("Failed to acquire %s", NAME_TO_CLAIM); + } + goto out; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", + NAME_TO_CLAIM, error->message); + g_error_free (error); + } + else { + g_warning ("Failed to acquire %s", NAME_TO_CLAIM); + } + goto out; + } + + dbus_g_proxy_add_signal (system_bus_proxy, "NameLost", + G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (system_bus_proxy, "NameLost", + G_CALLBACK (name_lost), NULL, NULL); + ret = TRUE; +out: + return ret; +} +]]> + + + + While you can do things this way with GDBus too, it is much nicer + to use the high-level API for this: + + ...insert example here... + +
+ +
+ Creating proxies for well-known names + + +
From 1d43e4140b9c890eca50f3845bff3d800d66b6f9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 20:03:44 -0400 Subject: [PATCH 35/76] Line up prototypes --- gio/gdbusproxy.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index cb0a2f47b..830985890 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -1391,14 +1391,14 @@ validate_method_return (const char *method_name, * Since: 2.26 */ void -g_dbus_proxy_call (GDBusProxy *proxy, - const gchar *method_name, - GVariant *parameters, - GDBusCallFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_proxy_call (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *simple; gboolean was_split; @@ -1457,8 +1457,8 @@ g_dbus_proxy_call (GDBusProxy *proxy, */ GVariant * g_dbus_proxy_call_finish (GDBusProxy *proxy, - GAsyncResult *res, - GError **error) + GAsyncResult *res, + GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); GVariant *value; @@ -1527,13 +1527,13 @@ g_dbus_proxy_call_finish (GDBusProxy *proxy, * Since: 2.26 */ GVariant * -g_dbus_proxy_call_sync (GDBusProxy *proxy, - const gchar *method_name, - GVariant *parameters, - GDBusCallFlags flags, - gint timeout_msec, - GCancellable *cancellable, - GError **error) +g_dbus_proxy_call_sync (GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error) { GVariant *ret; gboolean was_split; From e4b1e48fca9d1d2ca7e0ec54ebc9ea421aebff71 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 20:57:44 -0400 Subject: [PATCH 36/76] Match up parameter names and similar cleanups to make gtk-doc happy. --- docs/reference/gio/gio-sections.txt | 1 - gio/gdbusconnection.c | 17 ++--- gio/gdbusconnection.h | 4 +- gio/gdbusintrospection.c | 102 ++++++++++++++++------------ gio/gunixcredentialsmessage.c | 2 +- 5 files changed, 71 insertions(+), 55 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index a5d61eccd..001217e92 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2196,7 +2196,6 @@ g_unix_credentials_message_get_type GCredentials GCredentials GCredentialsClass -GCredentialType g_credentials_new g_credentials_to_string g_credentials_get_native diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 4921e971c..d76db7beb 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -1435,6 +1435,7 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, /** * g_dbus_connection_send_message_with_reply_finish: + * @connection: a #GDBusConnection * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_send_message_with_reply(). * @error: Return location for error or %NULL. * @@ -1949,7 +1950,7 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * @stream: A #GIOStream. * @guid: The GUID to use if a authenticating as a server or %NULL. * @flags: Flags describing how to make the connection. - * @authentication_observer: A #GDBusAuthObserver or %NULL. + * @observer: A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. @@ -1958,7 +1959,7 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * with the end represented by @stream. * * If %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER is set in @flags, - * @auth_observer (if not %NULL) is used to assist in the client + * @observer (if not %NULL) is used to assist in the client * authentication process. * * When the operation is finished, @callback will be invoked. You can @@ -1975,7 +1976,7 @@ void g_dbus_connection_new (GIOStream *stream, const gchar *guid, GDBusConnectionFlags flags, - GDBusAuthObserver *authentication_observer, + GDBusAuthObserver *observer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -1989,7 +1990,7 @@ g_dbus_connection_new (GIOStream *stream, "stream", stream, "guid", guid, "flags", flags, - "authentication-observer", authentication_observer, + "authentication-observer", observer, NULL); } @@ -2031,7 +2032,7 @@ g_dbus_connection_new_finish (GAsyncResult *res, * @stream: A #GIOStream. * @guid: The GUID to use if a authenticating as a server or %NULL. * @flags: Flags describing how to make the connection. - * @authentication_observer: A #GDBusAuthObserver or %NULL. + * @observer: A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * @@ -2039,7 +2040,7 @@ g_dbus_connection_new_finish (GAsyncResult *res, * with the end represented by @stream. * * If %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER is set in @flags, - * @auth_observer (if not %NULL) is used to assist in the client + * @observer (if not %NULL) is used to assist in the client * authentication process. * * This is a synchronous failable constructor. See @@ -2053,7 +2054,7 @@ GDBusConnection * g_dbus_connection_new_sync (GIOStream *stream, const gchar *guid, GDBusConnectionFlags flags, - GDBusAuthObserver *authentication_observer, + GDBusAuthObserver *observer, GCancellable *cancellable, GError **error) { @@ -2065,7 +2066,7 @@ g_dbus_connection_new_sync (GIOStream *stream, "stream", stream, "guid", guid, "flags", flags, - "authentication-observer", authentication_observer, + "authentication-observer", observer, NULL); } diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index eef99d381..bb5475d73 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -102,7 +102,7 @@ GDBusConnection *g_bus_get_sync (GBusType bus_type, void g_dbus_connection_new (GIOStream *stream, const gchar *guid, GDBusConnectionFlags flags, - GDBusAuthObserver *auth_observer, + GDBusAuthObserver *observer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); @@ -111,7 +111,7 @@ GDBusConnection *g_dbus_connection_new_finish (GAsyncResult GDBusConnection *g_dbus_connection_new_sync (GIOStream *stream, const gchar *guid, GDBusConnectionFlags flags, - GDBusAuthObserver *auth_observer, + GDBusAuthObserver *observer, GCancellable *cancellable, GError **error); diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index 9ad46974c..7e461bd34 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -879,11 +879,11 @@ g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info, /** * g_dbus_node_info_generate_xml: - * @node_info: A #GDBusNodeInfo. + * @info: A #GDBusNodeInfo. * @indent: Indentation level. * @string_builder: A #GString to to append XML data to. * - * Appends an XML representation of @node_info (and its children) to @string_builder. + * Appends an XML representation of @info (and its children) to @string_builder. * * This function is typically used for generating introspection XML documents at run-time for * handling the org.freedesktop.DBus.Introspectable.Introspect method. @@ -891,17 +891,17 @@ g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo *info, * Since: 2.26 */ void -g_dbus_node_info_generate_xml (const GDBusNodeInfo *node_info, +g_dbus_node_info_generate_xml (const GDBusNodeInfo *info, guint indent, GString *string_builder) { guint n; g_string_append_printf (string_builder, "%*spath != NULL) - g_string_append_printf (string_builder, " name=\"%s\"", node_info->path); + if (info->path != NULL) + g_string_append_printf (string_builder, " name=\"%s\"", info->path); - if (node_info->interfaces == NULL && node_info->nodes == NULL && node_info->annotations == NULL) + if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL) { g_string_append (string_builder, "/>\n"); } @@ -909,18 +909,18 @@ g_dbus_node_info_generate_xml (const GDBusNodeInfo *node_info, { g_string_append (string_builder, ">\n"); - for (n = 0; node_info->annotations != NULL && node_info->annotations[n] != NULL; n++) - g_dbus_annotation_info_generate_xml (node_info->annotations[n], + for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) + g_dbus_annotation_info_generate_xml (info->annotations[n], indent + 2, string_builder); - for (n = 0; node_info->interfaces != NULL && node_info->interfaces[n] != NULL; n++) - g_dbus_interface_info_generate_xml (node_info->interfaces[n], + for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++) + g_dbus_interface_info_generate_xml (info->interfaces[n], indent + 2, string_builder); - for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++) - g_dbus_node_info_generate_xml (node_info->nodes[n], + for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++) + g_dbus_node_info_generate_xml (info->nodes[n], indent + 2, string_builder); @@ -931,7 +931,8 @@ g_dbus_node_info_generate_xml (const GDBusNodeInfo *node_info, /* ---------------------------------------------------------------------------------------------------- */ static GDBusAnnotationInfo ** -parse_data_steal_annotations (ParseData *data, guint *out_num_elements) +parse_data_steal_annotations (ParseData *data, + guint *out_num_elements) { GDBusAnnotationInfo **ret; if (out_num_elements != NULL) @@ -948,7 +949,8 @@ parse_data_steal_annotations (ParseData *data, guint *out_num_elements) } static GDBusArgInfo ** -parse_data_steal_args (ParseData *data, guint *out_num_elements) +parse_data_steal_args (ParseData *data, + guint *out_num_elements) { GDBusArgInfo **ret; if (out_num_elements != NULL) @@ -965,7 +967,8 @@ parse_data_steal_args (ParseData *data, guint *out_num_elements) } static GDBusArgInfo ** -parse_data_steal_out_args (ParseData *data, guint *out_num_elements) +parse_data_steal_out_args (ParseData *data, + guint *out_num_elements) { GDBusArgInfo **ret; if (out_num_elements != NULL) @@ -982,7 +985,8 @@ parse_data_steal_out_args (ParseData *data, guint *out_num_elements) } static GDBusMethodInfo ** -parse_data_steal_methods (ParseData *data, guint *out_num_elements) +parse_data_steal_methods (ParseData *data, + guint *out_num_elements) { GDBusMethodInfo **ret; if (out_num_elements != NULL) @@ -999,7 +1003,8 @@ parse_data_steal_methods (ParseData *data, guint *out_num_elements) } static GDBusSignalInfo ** -parse_data_steal_signals (ParseData *data, guint *out_num_elements) +parse_data_steal_signals (ParseData *data, + guint *out_num_elements) { GDBusSignalInfo **ret; if (out_num_elements != NULL) @@ -1016,7 +1021,8 @@ parse_data_steal_signals (ParseData *data, guint *out_num_elements) } static GDBusPropertyInfo ** -parse_data_steal_properties (ParseData *data, guint *out_num_elements) +parse_data_steal_properties (ParseData *data, + guint *out_num_elements) { GDBusPropertyInfo **ret; if (out_num_elements != NULL) @@ -1033,7 +1039,8 @@ parse_data_steal_properties (ParseData *data, guint *out_num_elements) } static GDBusInterfaceInfo ** -parse_data_steal_interfaces (ParseData *data, guint *out_num_elements) +parse_data_steal_interfaces (ParseData *data, + guint *out_num_elements) { GDBusInterfaceInfo **ret; if (out_num_elements != NULL) @@ -1050,7 +1057,8 @@ parse_data_steal_interfaces (ParseData *data, guint *out_num_elements) } static GDBusNodeInfo ** -parse_data_steal_nodes (ParseData *data, guint *out_num_elements) +parse_data_steal_nodes (ParseData *data, + guint *out_num_elements) { GDBusNodeInfo **ret; if (out_num_elements != NULL) @@ -1151,7 +1159,8 @@ parse_data_free_nodes (ParseData *data) /* ---------------------------------------------------------------------------------------------------- */ static GDBusAnnotationInfo * -parse_data_get_annotation (ParseData *data, gboolean create_new) +parse_data_get_annotation (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1)); @@ -1159,7 +1168,8 @@ parse_data_get_annotation (ParseData *data, gboolean create_new) } static GDBusArgInfo * -parse_data_get_arg (ParseData *data, gboolean create_new) +parse_data_get_arg (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1)); @@ -1167,7 +1177,8 @@ parse_data_get_arg (ParseData *data, gboolean create_new) } static GDBusArgInfo * -parse_data_get_out_arg (ParseData *data, gboolean create_new) +parse_data_get_out_arg (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1)); @@ -1175,7 +1186,8 @@ parse_data_get_out_arg (ParseData *data, gboolean create_new) } static GDBusMethodInfo * -parse_data_get_method (ParseData *data, gboolean create_new) +parse_data_get_method (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1)); @@ -1183,7 +1195,8 @@ parse_data_get_method (ParseData *data, gboolean create_new) } static GDBusSignalInfo * -parse_data_get_signal (ParseData *data, gboolean create_new) +parse_data_get_signal (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1)); @@ -1191,7 +1204,8 @@ parse_data_get_signal (ParseData *data, gboolean create_new) } static GDBusPropertyInfo * -parse_data_get_property (ParseData *data, gboolean create_new) +parse_data_get_property (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1)); @@ -1199,7 +1213,8 @@ parse_data_get_property (ParseData *data, gboolean create_new) } static GDBusInterfaceInfo * -parse_data_get_interface (ParseData *data, gboolean create_new) +parse_data_get_interface (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1)); @@ -1207,7 +1222,8 @@ parse_data_get_interface (ParseData *data, gboolean create_new) } static GDBusNodeInfo * -parse_data_get_node (ParseData *data, gboolean create_new) +parse_data_get_node (ParseData *data, + gboolean create_new) { if (create_new) g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1)); @@ -1281,12 +1297,12 @@ parse_data_free (ParseData *data) /* ---------------------------------------------------------------------------------------------------- */ static void -parser_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) +parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) { ParseData *data = user_data; GSList *stack; @@ -1621,10 +1637,10 @@ steal_annotations (ParseData *data) static void -parser_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) +parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) { ParseData *data = user_data; gboolean have_popped_annotations; @@ -2024,27 +2040,27 @@ g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info, /** * g_dbus_node_info_lookup_interface: - * @node_info: A #GDBusNodeInfo. + * @info: A #GDBusNodeInfo. * @name: A D-Bus interface name. * * Looks up information about an interface. * * This cost of this function is O(n) in number of interfaces. * - * Returns: A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @node_info. + * Returns: A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @info. * * Since: 2.26 */ const GDBusInterfaceInfo * -g_dbus_node_info_lookup_interface (const GDBusNodeInfo *node_info, +g_dbus_node_info_lookup_interface (const GDBusNodeInfo *info, const gchar *name) { guint n; const GDBusInterfaceInfo *result; - for (n = 0; node_info->interfaces != NULL && node_info->interfaces[n] != NULL; n++) + for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++) { - const GDBusInterfaceInfo *i = node_info->interfaces[n]; + const GDBusInterfaceInfo *i = info->interfaces[n]; if (g_strcmp0 (i->name, name) == 0) { diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index d1188ade2..7285fbc45 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -300,7 +300,7 @@ g_unix_credentials_message_new (void) } /** - * g_unix_credentials_message_new: + * g_unix_credentials_message_new_with_credentials: * @credentials: A #GCredentials object. * * Creates a new #GUnixCredentialsMessage holding @credentials. From 9b05e0bc3e88f9e54710aabb2ad29908739e6345 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 21:30:53 -0400 Subject: [PATCH 37/76] Complete the name owning section of the migration guide --- docs/reference/gio/migrating-gdbus.xml | 138 ++++++++++++++----------- 1 file changed, 77 insertions(+), 61 deletions(-) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 1f33e0229..821742644 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -74,75 +74,91 @@ Using dbus-glib, you typically call RequestName manually to own a name, like in the following excerpt: message); - g_error_free (error); - } - else { - g_warning ("Failed to acquire %s", NAME_TO_CLAIM); - } - goto out; + error = NULL; + res = dbus_g_proxy_call (system_bus_proxy, + "RequestName", + &error, + G_TYPE_STRING, NAME_TO_CLAIM, + G_TYPE_UINT, DBUS_NAME_FLAG_ALLOW_REPLACEMENT, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) + { + g_warning ("Failed to acquire %s: %s", + NAME_TO_CLAIM, error->message); + g_error_free (error); } - - if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - if (error != NULL) { - g_warning ("Failed to acquire %s: %s", - NAME_TO_CLAIM, error->message); - g_error_free (error); - } - else { - g_warning ("Failed to acquire %s", NAME_TO_CLAIM); - } - goto out; + else + { + g_warning ("Failed to acquire %s", NAME_TO_CLAIM); } + goto out; + } - dbus_g_proxy_add_signal (system_bus_proxy, "NameLost", - G_TYPE_STRING, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (system_bus_proxy, "NameLost", - G_CALLBACK (name_lost), NULL, NULL); - ret = TRUE; -out: - return ret; -} + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + if (error != NULL) + { + g_warning ("Failed to acquire %s: %s", + NAME_TO_CLAIM, error->message); + g_error_free (error); + } + else + { + g_warning ("Failed to acquire %s", NAME_TO_CLAIM); + } + exit (1); + } + + dbus_g_proxy_add_signal (system_bus_proxy, "NameLost", + G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (system_bus_proxy, "NameLost", + G_CALLBACK (on_name_lost), NULL, NULL); + + /* further setup ... */ ]]>
- While you can do things this way with GDBus too, it is much nicer - to use the high-level API for this: - - ...insert example here... + While you can do things this way with GDBus too, using + g_dbus_proxy_call_sync(), it is much nicer to use the high-level API + for this: + + Note that g_bus_own_name() works asynchronously and requires + you to enter your mainloop to await the on_name_aquired() + callback. Also note that in order to avoid race conditions (e.g. + when your service is activated by a method call), you have to export + your manager object before acquiring the + name. The on_bus_acquired() callback is the right place to do + such preparations. +
From 1af277f16779f072bc0c326aa8024747ad8c13e9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 21:32:29 -0400 Subject: [PATCH 38/76] Fix a typo --- docs/reference/gio/migrating-gdbus.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 821742644..2c2073aa8 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -44,7 +44,7 @@ #DBusGConnection#GDBusConnection #DBusGProxy#GDBusProxy - #DBusGMethodInvocation#GDBusMethodInvocatoin + #DBusGMethodInvocation#GDBusMethodInvocation dbus_g_bus_get()g_bus_get_sync(), also see g_bus_get() dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see From 26f65d83c5078e48e414a1cbe593bf349d96a033 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 21:59:42 -0400 Subject: [PATCH 39/76] Don't refer to nonexisting API. --- gio/gdbusconnection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index d76db7beb..bbfb95f9d 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -5239,7 +5239,7 @@ get_uninitialized_connection (GBusType bus_type, * The returned object is a singleton, that is, shared with other * callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the * event that you need a private message bus connection, use - * g_dbus_address_get_for_bus() and + * g_dbus_address_get_for_bus_sync() and * g_dbus_connection_new_for_address(). * * Note that the returned #GDBusConnection object will (usually) have From fdfd3d5e7536313dde1fc02b45ca04a6e4704b50 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 22:00:06 -0400 Subject: [PATCH 40/76] Fill out the proxy section of the migration guide --- docs/reference/gio/migrating-gdbus.xml | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 2c2073aa8..80322b2ac 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -164,6 +164,59 @@ on_name_acquired (GDBusConnection *connection,
Creating proxies for well-known names + dbus-glib lets you create proxy objects for well-known names, like the + following example: + + + For a #DBusGProxy constructed like this, method calls will be sent to + the current owner of the name, and that owner can change over time. + + + In contrast, #GDBusProxy instances are always bound to a unique name. + To get a proxy for a well-known name, you either have to call + GetNameOwner() yourself and construct a proxy for the unique name + of the current name owner, or use the high-level API. The latter + option is highly recommended: + + + Like g_bus_own_name(), g_bus_watch_proxy() is asynchronous and + you are expected to enter your mainloop to await the on_proxy_appeared() + callback. Note that GDBus also does all the setup operations for the + proxy asynchronously, and only calls your callback when the proxy + is ready for use.
From 9c128ca83536b6c6c823bb83606b09193d814738 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 22:00:54 -0400 Subject: [PATCH 41/76] Trivial formatting fix --- docs/reference/gio/migrating-gdbus.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 80322b2ac..41f5551dc 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -179,7 +179,7 @@ on_name_acquired (GDBusConnection *connection, In contrast, #GDBusProxy instances are always bound to a unique name. To get a proxy for a well-known name, you either have to call - GetNameOwner() yourself and construct a proxy for the unique name + GetNameOwner yourself and construct a proxy for the unique name of the current name owner, or use the high-level API. The latter option is highly recommended: Date: Tue, 11 May 2010 22:03:40 -0400 Subject: [PATCH 42/76] Trivial: tweak section heading --- docs/reference/gio/migrating-gdbus.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 41f5551dc..00df874e5 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -33,7 +33,7 @@
- Dbus-glib API conversion + API comparison dbus-glib APIs and their GDBus counterparts From d7095dd4700b38c202a43b7c21f93db20a9ede44 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 22:35:59 -0400 Subject: [PATCH 43/76] Document DBUS address env vars --- docs/reference/gio/overview.xml | 28 ++++++++++++++++++++++++++++ gio/gdbusintrospection.c | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/reference/gio/overview.xml b/docs/reference/gio/overview.xml index 6b3339d28..1a1ff0279 100644 --- a/docs/reference/gio/overview.xml +++ b/docs/reference/gio/overview.xml @@ -295,6 +295,34 @@ + + <envar>DBUS_SYSTEM_ADDRESS</envar> + + + This variable is consulted to find the address of the D-Bus system + bus. For the format of D-Bus addresses, see the D-Bus + specification. + + + + + <envar>DBUS_SESSION_ADDRESS</envar> + + + This variable is consulted to find the address of the D-Bus session bus. + + + + + <envar>DBUS_STARTER_BUS_TYPE</envar> + + + This variable is consulted to find out the 'starter' bus for an + application that has been started via D-Bus activation. The possible + values are 'system' or 'session'. + + + <envar>G_DBUS_DEBUG</envar> diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c index 7e461bd34..c1c4d35c2 100644 --- a/gio/gdbusintrospection.c +++ b/gio/gdbusintrospection.c @@ -226,7 +226,7 @@ g_dbus_arg_info_ref (GDBusArgInfo *info) } /** - * g_dbus_node_info_ref: + * g_dbus_annotation_info_ref: * @info: A #GDBusNodeInfo * * If @info is statically allocated does nothing. Otherwise increases From c4cf88c22f731878cbc740e4721e07215385201b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 22:50:40 -0400 Subject: [PATCH 44/76] Document remove_filter --- gio/gdbusconnection.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index bbfb95f9d..ca380e640 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -33,9 +33,7 @@ * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular * the mechanism VFuncs need to be able to set an error. * - * - Need to document usage of DBUS_SYSTEM_ADDRESS and - * DBUS_SESSION_BUS_ADDRESS environment variables. Also need to - * document other mechanisms/sources for determining the D-Bus + * - Need to document other mechanisms/sources for determining the D-Bus * address of a well-known bus. * * - e.g. on Win32 we need code like from here @@ -2367,6 +2365,15 @@ purge_all_filters (GDBusConnection *connection) } } +/** + * g_dbus_connection_remove_filter: + * @connection: a #GDBusConnection + * @filer_id: an identifier obtained from g_dbus_connection_add_filter() + * + * Removes a filter. + * + * Since: 2.26 + */ void g_dbus_connection_remove_filter (GDBusConnection *connection, guint filter_id) From ab2ff1a307f6bf7825e02b0d09e25b8ea7570c07 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 22:51:14 -0400 Subject: [PATCH 45/76] Remove properties from GDBusMethodInvocation class --- gio/gdbusconnection.c | 3 - gio/gdbusmethodinvocation.c | 330 ++---------------------------------- 2 files changed, 18 insertions(+), 315 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index ca380e640..dd4cbc2d9 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -108,9 +108,6 @@ * - GDBusProxy subclass example * * - Update GDBusAuthObserver (is_same_user(), s/deny/authorize/) - * - * - Remove properties on GDBusMethodInvocation - * */ #include "config.h" diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c index 767097d8a..404652e3d 100644 --- a/gio/gdbusmethodinvocation.c +++ b/gio/gdbusmethodinvocation.c @@ -63,20 +63,6 @@ struct _GDBusMethodInvocationPrivate gpointer user_data; }; -enum -{ - PROP_0, - PROP_SENDER, - PROP_OBJECT_PATH, - PROP_INTERFACE_NAME, - PROP_METHOD_NAME, - PROP_METHOD_INFO, - PROP_CONNECTION, - PROP_PARAMETERS, - PROP_MESSAGE, - PROP_USER_DATA -}; - G_DEFINE_TYPE (GDBusMethodInvocation, g_dbus_method_invocation, G_TYPE_OBJECT); static void @@ -95,298 +81,12 @@ g_dbus_method_invocation_finalize (GObject *object) G_OBJECT_CLASS (g_dbus_method_invocation_parent_class)->finalize (object); } -static void -g_dbus_method_invocation_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object); - - switch (prop_id) - { - case PROP_SENDER: - g_value_set_string (value, g_dbus_method_invocation_get_sender (invocation)); - break; - - case PROP_OBJECT_PATH: - g_value_set_string (value, g_dbus_method_invocation_get_object_path (invocation)); - break; - - case PROP_INTERFACE_NAME: - g_value_set_string (value, g_dbus_method_invocation_get_interface_name (invocation)); - break; - - case PROP_METHOD_NAME: - g_value_set_string (value, g_dbus_method_invocation_get_method_name (invocation)); - break; - - case PROP_METHOD_INFO: - g_value_set_boxed (value, g_dbus_method_invocation_get_method_info (invocation)); - break; - - case PROP_CONNECTION: - g_value_set_object (value, g_dbus_method_invocation_get_connection (invocation)); - break; - - case PROP_PARAMETERS: - g_value_set_boxed (value, g_dbus_method_invocation_get_parameters (invocation)); - break; - - case PROP_MESSAGE: - g_value_set_object (value, g_dbus_method_invocation_get_message (invocation)); - break; - - case PROP_USER_DATA: - g_value_set_pointer (value, g_dbus_method_invocation_get_user_data (invocation)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -g_dbus_method_invocation_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (object); - - switch (prop_id) - { - case PROP_SENDER: - invocation->priv->sender = g_value_dup_string (value); - break; - - case PROP_OBJECT_PATH: - invocation->priv->object_path = g_value_dup_string (value); - break; - - case PROP_INTERFACE_NAME: - invocation->priv->interface_name = g_value_dup_string (value); - break; - - case PROP_METHOD_NAME: - invocation->priv->method_name = g_value_dup_string (value); - break; - - case PROP_METHOD_INFO: - invocation->priv->method_info = g_value_dup_boxed (value); - break; - - case PROP_CONNECTION: - invocation->priv->connection = g_value_dup_object (value); - break; - - case PROP_PARAMETERS: - invocation->priv->parameters = g_value_dup_boxed (value); - break; - - case PROP_MESSAGE: - invocation->priv->message = g_value_dup_object (value); - break; - - case PROP_USER_DATA: - invocation->priv->user_data = g_value_get_pointer (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - static void g_dbus_method_invocation_class_init (GDBusMethodInvocationClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - gobject_class->finalize = g_dbus_method_invocation_finalize; - gobject_class->set_property = g_dbus_method_invocation_set_property; - gobject_class->get_property = g_dbus_method_invocation_get_property; - - /** - * GDBusMethodInvocation:sender: - * - * The bus name that invoked the method or %NULL if the connection is not a bus connection. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_SENDER, - g_param_spec_string ("sender", - P_("Sender"), - P_("The bus name that invoked the method."), - NULL, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:object-path: - * - * The object path the method was invoked on. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_OBJECT_PATH, - g_param_spec_string ("object-path", - P_("Object Path"), - P_("The object path the method was invoked on."), - NULL, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:interface-name: - * - * The name of the D-Bus interface the method was invoked on. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_INTERFACE_NAME, - g_param_spec_string ("interface-name", - P_("Interface Name"), - P_("The name of the D-Bus interface the method was invoked on."), - NULL, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:method-name: - * - * The name of the method that was invoked. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_METHOD_NAME, - g_param_spec_string ("method-name", - P_("Method Name"), - P_("The name of the method that was invoked."), - NULL, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:method-info: - * - * Information about the method that was invoked, if any. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_METHOD_INFO, - g_param_spec_boxed ("method-info", - P_("Method Info"), - P_("Information about the method that was invoked, if any."), - G_TYPE_DBUS_METHOD_INFO, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:connection: - * - * The #GDBusConnection the method was invoked on. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_CONNECTION, - g_param_spec_object ("connection", - P_("Connection"), - P_("The #GDBusConnection the method was invoked on."), - G_TYPE_DBUS_CONNECTION, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:message: - * - * The D-Bus message. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_MESSAGE, - g_param_spec_object ("message", - P_("Message"), - P_("The D-Bus Message."), - G_TYPE_DBUS_MESSAGE, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:parameters: - * - * The parameters as a #GVariant tuple. - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_PARAMETERS, - g_param_spec_boxed ("parameters", - P_("Parameters"), - P_("The parameters as a #GVariant tuple."), - G_TYPE_VARIANT, - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); - - /** - * GDBusMethodInvocation:user-data: - * - * The @user_data #gpointer passed to g_dbus_connection_register_object(). - * - * Since: 2.26 - */ - g_object_class_install_property (gobject_class, - PROP_USER_DATA, - g_param_spec_pointer ("user-data", - P_("User Data"), - P_("The gpointer passed to g_dbus_connection_register_object()."), - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB | - G_PARAM_STATIC_NICK)); + gobject_class->finalize = g_dbus_method_invocation_finalize; g_type_class_add_private (klass, sizeof (GDBusMethodInvocationPrivate)); } @@ -588,6 +288,9 @@ g_dbus_method_invocation_new (const gchar *sender, GVariant *parameters, gpointer user_data) { + GDBusMethodInvocation *invocation; + GDBusMethodInvocationPrivate *priv; + g_return_val_if_fail (sender == NULL || g_dbus_is_name (sender), NULL); g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), NULL); @@ -596,17 +299,20 @@ g_dbus_method_invocation_new (const gchar *sender, g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); g_return_val_if_fail (g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); - return G_DBUS_METHOD_INVOCATION (g_object_new (G_TYPE_DBUS_METHOD_INVOCATION, - "sender", sender, - "object-path", object_path, - "interface-name", interface_name, - "method-name", method_name, - "method-info", method_info, - "connection", connection, - "message", message, - "parameters", parameters, - "user-data", user_data, - NULL)); + invocation = G_DBUS_METHOD_INVOCATION (g_object_new (G_TYPE_DBUS_METHOD_INVOCATION, NULL)); + + priv = invocation->priv; + priv->sender = g_strdup (sender); + priv->object_path = g_strdup (object_path); + priv->interface_name = g_strdup (interface_name); + priv->method_name = g_strdup (method_name); + priv->method_info = g_dbus_method_info_ref ((GDBusMethodInfo *)method_info); + priv->connection = g_object_ref (connection); + priv->message = g_object_ref (message); + priv->parameters = g_variant_ref (parameters); + priv->user_data = user_data; + + return invocation; } /* ---------------------------------------------------------------------------------------------------- */ From a63d3bb868b0ee17404cf85520e48d441abbcdf3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 23:18:48 -0400 Subject: [PATCH 46/76] mention inline use of floating variants --- gio/gdbusconnection.c | 37 ++++++++++++++++++++++++++++++++++++- gio/gdbusproxy.c | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index dd4cbc2d9..93388679d 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -2365,7 +2365,7 @@ purge_all_filters (GDBusConnection *connection) /** * g_dbus_connection_remove_filter: * @connection: a #GDBusConnection - * @filer_id: an identifier obtained from g_dbus_connection_add_filter() + * @filter_id: an identifier obtained from g_dbus_connection_add_filter() * * Removes a filter. * @@ -4153,6 +4153,24 @@ add_call_flags (GDBusMessage *message, * not compatible with the D-Bus protocol, the operation fails with * %G_IO_ERROR_INVALID_ARGUMENT. * + * If the @parameters #GVariant is floating, it is consumed. This allows + * convenient 'inline' use of g_variant_new(), e.g.: + * |[ + * g_dbus_connection_call (connection, + * "org.freedesktop.StringThings", + * "/org/freedesktop/StringThings", + * "org.freedesktop.StringThings", + * "TwoStrings", + * g_variant_new ("(ss)", + * "Thing One", + * "Thing Two"), + * G_DBUS_CALL_FLAGS_NONE, + * -1, + * NULL, + * (GAsyncReadyCallback) two_strings_done, + * NULL); + * ]| + * * This is an asynchronous method. When the operation is finished, @callback will be invoked * in the thread-default main loop * of the thread you are calling this method from. You can then call @@ -4303,6 +4321,23 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * contains a value not compatible with the D-Bus protocol, the operation * fails with %G_IO_ERROR_INVALID_ARGUMENT. * + * If the @parameters #GVariant is floating, it is consumed. + * This allows convenient 'inline' use of g_variant_new(), e.g.: + * |[ + * g_dbus_connection_call_sync (connection, + * "org.freedesktop.StringThings", + * "/org/freedesktop/StringThings", + * "org.freedesktop.StringThings", + * "TwoStrings", + * g_variant_new ("(ss)", + * "Thing One", + * "Thing Two"), + * G_DBUS_CALL_FLAGS_NONE, + * -1, + * NULL, + * &error); + * ]| + * * The calling thread is blocked until a reply is received. See * g_dbus_connection_call() for the asynchronous version of * this method. diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 830985890..9d1d6535d 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -1381,12 +1381,28 @@ validate_method_return (const char *method_name, * compatible with the D-Bus protocol, the operation fails with * %G_IO_ERROR_INVALID_ARGUMENT. * - * This is an asynchronous method. When the operation is finished, @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_proxy_call_finish() to get the result of the operation. - * See g_dbus_proxy_call_sync() for the - * synchronous version of this method. + * If the @parameters #GVariant is floating, it is consumed. This allows + * convenient 'inline' use of g_variant_new(), e.g.: + * |[ + * g_dbus_proxy_call (proxy, + * "TwoStrings", + * g_variant_new ("(ss)", + * "Thing One", + * "Thing Two"), + * G_DBUS_CALL_FLAGS_NONE, + * -1, + * NULL, + * (GAsyncReadyCallback) two_strings_done, + * &data); + * ]| + * + * This is an asynchronous method. When the operation is finished, + * @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_proxy_call_finish() to get the result of + * the operation. See g_dbus_proxy_call_sync() for the synchronous + * version of this method. * * Since: 2.26 */ @@ -1517,6 +1533,20 @@ g_dbus_proxy_call_finish (GDBusProxy *proxy, * compatible with the D-Bus protocol, the operation fails with * %G_IO_ERROR_INVALID_ARGUMENT. * + * If the @parameters #GVariant is floating, it is consumed. This allows + * convenient 'inline' use of g_variant_new(), e.g.: + * |[ + * g_dbus_proxy_call_sync (proxy, + * "TwoStrings", + * g_variant_new ("(ss)", + * "Thing One", + * "Thing Two"), + * G_DBUS_CALL_FLAGS_NONE, + * -1, + * NULL, + * &error); + * ]| + * * The calling thread is blocked until a reply is received. See * g_dbus_proxy_call() for the asynchronous version of this * method. From e2b9d077659df82f9603352e6ce4ff1ceb4b1b8a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 11 May 2010 23:26:51 -0400 Subject: [PATCH 47/76] Update TODO list --- gio/gdbusconnection.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 93388679d..e6af1a04a 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -99,10 +99,6 @@ * - Mention in all API that the GVariant is sunk. Also mention * when the returned GVariant is floating. * - * - Small example snippet for each method where useful (won't - * have to compile) where it makes sense (e.g. connetion_call() - * and using a floating GVariant). - * * - Consistent timeout handling (25s vs 30s?) * * - GDBusProxy subclass example From af3afc804064ec17e89a6cdef4cc3e63015c8a77 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 12 May 2010 12:13:57 -0400 Subject: [PATCH 48/76] placeholder for more migration docs --- docs/reference/gio/migrating-gdbus.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 00df874e5..ba0278cda 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -219,4 +219,9 @@ on_proxy_appeared (GDBusConnection *connection, is ready for use. + +
+ Exporting objects +
+ From 371a3373bbf190054076896ed32d28d546f23930 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 12 May 2010 13:01:02 -0400 Subject: [PATCH 49/76] Correct env var names and add a note about priority --- docs/reference/gio/overview.xml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/reference/gio/overview.xml b/docs/reference/gio/overview.xml index 1a1ff0279..1ea257177 100644 --- a/docs/reference/gio/overview.xml +++ b/docs/reference/gio/overview.xml @@ -296,21 +296,29 @@
- <envar>DBUS_SYSTEM_ADDRESS</envar> + <envar>DBUS_SYSTEM_BUS_ADDRESS</envar> This variable is consulted to find the address of the D-Bus system bus. For the format of D-Bus addresses, see the D-Bus specification. + + Setting this variable overrides platform-specific ways of determining + the system bus address. + - <envar>DBUS_SESSION_ADDRESS</envar> + <envar>DBUS_SESSION_BUS_ADDRESS</envar> This variable is consulted to find the address of the D-Bus session bus. + + Setting this variable overrides platform-specific ways of determining + the session bus address. + From 7c0196f0267aa77c80fb85320ef9583c7fc64ad7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 12 May 2010 13:01:40 -0400 Subject: [PATCH 50/76] Update an example to the latest auth observer api --- gio/gdbusauthobserver.c | 17 ++++++++++------- gio/gdbusconnection.c | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index 6883058b5..948659b18 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -51,12 +51,18 @@ * GCredentials *credentials, * gpointer user_data) * { + * GCredentials *me; * gboolean deny; + * * deny = TRUE; + * me = g_credentials_new (); + * * if (credentials != NULL && - * g_credentials_has_unix_user (credentials) && - * g_credentials_get_unix_user (credentials) == getuid ()) + * !g_credentials_is_same_user (credentials, me)) * deny = FALSE; + * + * g_object_unref (me); + * * return deny; * } * @@ -114,10 +120,7 @@ G_DEFINE_TYPE (GDBusAuthObserver, g_dbus_auth_observer, G_TYPE_OBJECT); static void g_dbus_auth_observer_finalize (GObject *object) { - //GDBusAuthObserver *observer = G_DBUS_AUTH_OBSERVER (object); - - if (G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize (object); + G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize (object); } static gboolean @@ -133,7 +136,7 @@ g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - gobject_class->finalize = g_dbus_auth_observer_finalize; + gobject_class->finalize = g_dbus_auth_observer_finalize; klass->deny_authenticated_peer = g_dbus_auth_observer_deny_authenticated_peer_real; diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index e6af1a04a..ff425abc6 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -103,7 +103,7 @@ * * - GDBusProxy subclass example * - * - Update GDBusAuthObserver (is_same_user(), s/deny/authorize/) + * - Update GDBusAuthObserver (s/deny/authorize/) */ #include "config.h" From d40767fc62972f9cc85ebfb23e113068cc316f3a Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 12 May 2010 15:49:48 -0400 Subject: [PATCH 51/76] GDBus: Add an example of a GDBusProxy subclass --- docs/reference/gio/migrating-gdbus.xml | 55 +++ gio/gdbusconnection.c | 2 - gio/tests/Makefile.am | 4 + gio/tests/gdbus-example-proxy-subclass.c | 443 +++++++++++++++++++++++ 4 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 gio/tests/gdbus-example-proxy-subclass.c diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index ba0278cda..b8967fd96 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -218,6 +218,61 @@ on_proxy_appeared (GDBusConnection *connection, proxy asynchronously, and only calls your callback when the proxy is ready for use. + + + For an example of a #GDBusProxy-derived class that wraps a D-Bus + interface in a type-safe way, see . The comparison is as + follows: +
+ Wrapping the org.freedesktop.Accounts.User D-Bus interface in the AccountUser GObject type + + + D-Bus conceptGObject concept + + + + AutomaticLogin property + + AccountsUser:automatic-login GObject property + C getter: accounts_user_get_automatic_login() + Watch changes via the notify::automatic-login signal + + + + RealName property + + AccountsUser:real-name GObject property + C getter: accounts_user_get_real_name() + Watch changes via the notify::real-name signal + + + + UserName property + + AccountsUser:user-name GObject property + C getter: accounts_user_get_user_name() + Watch changes via the notify::user-name signal + + + + Changed signal + + AccountsUser::changed GObject signal + Watch via e.g. g_signal_connect() + + + + Frobnicate method + + Use accounts_user_frobnicate() + accounts_user_frobnicate_finish() or accounts_user_frobnicate_sync() to invoke + + + + +
+ + GDBusProxy subclass exampleFIXME: MISSING XINCLUDE CONTENT
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index ff425abc6..aa5ad059f 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -101,8 +101,6 @@ * * - Consistent timeout handling (25s vs 30s?) * - * - GDBusProxy subclass example - * * - Update GDBusAuthObserver (s/deny/authorize/) */ diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 4aab457e4..cbd63a16d 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -64,6 +64,7 @@ SAMPLE_PROGS = \ gdbus-example-server \ gdbus-example-subtree \ gdbus-example-peer \ + gdbus-example-proxy-subclass \ $(NULL) @@ -230,6 +231,9 @@ gdbus_example_subtree_LDADD = $(progs_ldadd) gdbus_example_peer_SOURCES = gdbus-example-peer.c gdbus_example_peer_LDADD = $(progs_ldadd) +gdbus_example_proxy_subclass_SOURCES = gdbus-example-proxy-subclass.c +gdbus_example_proxy_subclass_LDADD = $(progs_ldadd) + EXTRA_DIST += \ socket-common.c \ org.gtk.test.gschema \ diff --git a/gio/tests/gdbus-example-proxy-subclass.c b/gio/tests/gdbus-example-proxy-subclass.c new file mode 100644 index 000000000..6c0990966 --- /dev/null +++ b/gio/tests/gdbus-example-proxy-subclass.c @@ -0,0 +1,443 @@ + +#include + +/* ---------------------------------------------------------------------------------------------------- */ +/* The D-Bus interface definition we want to create a GDBusProxy-derived type for: */ +/* ---------------------------------------------------------------------------------------------------- */ + +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Definition of the AccountsUser type */ +/* ---------------------------------------------------------------------------------------------------- */ + +#define ACCOUNTS_TYPE_USER (accounts_user_get_type ()) +#define ACCOUNTS_USER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ACCOUNTS_TYPE_USER, AccountsUser)) +#define ACCOUNTS_USER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), ACCOUNTS_TYPE_USER, AccountsUserClass)) +#define ACCOUNTS_USER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ACCOUNTS_TYPE_USER, AccountsUserClass)) +#define ACCOUNTS_IS_USER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ACCOUNTS_TYPE_USER)) +#define ACCOUNTS_IS_USER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ACCOUNTS_TYPE_USER)) + +typedef struct _AccountsUser AccountsUser; +typedef struct _AccountsUserClass AccountsUserClass; +typedef struct _AccountsUserPrivate AccountsUserPrivate; + +struct _AccountsUser +{ + /*< private >*/ + GDBusProxy parent_instance; + AccountsUserPrivate *priv; +}; + +struct _AccountsUserClass +{ + /*< private >*/ + GDBusProxyClass parent_class; + void (*changed) (AccountsUser *user); +}; + +GType accounts_user_get_type (void) G_GNUC_CONST; + +const gchar *accounts_user_get_user_name (AccountsUser *user); +const gchar *accounts_user_get_real_name (AccountsUser *user); +gboolean accounts_user_get_automatic_login (AccountsUser *user); + +void accounts_user_frobnicate (AccountsUser *user, + const gchar *flux, + gint baz, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gchar *accounts_user_frobnicate_finish (AccountsUser *user, + GAsyncResult *res, + GError **error); +gchar *accounts_user_frobnicate_sync (AccountsUser *user, + const gchar *flux, + gint baz, + GCancellable *cancellable, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ +/* Implementation of the AccountsUser type */ +/* ---------------------------------------------------------------------------------------------------- */ + +/* A more efficient approach than parsing XML is to use const static + * GDBusInterfaceInfo, GDBusMethodInfo, ... structures + */ +static GDBusInterfaceInfo * +accounts_user_get_interface_info (void) +{ + static gsize has_info = 0; + static GDBusInterfaceInfo *info = NULL; + if (g_once_init_enter (&has_info)) + { + GDBusNodeInfo *introspection_data; + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + info = introspection_data->interfaces[0]; + g_once_init_leave (&has_info, 1); + } + return info; +} + +enum +{ + PROP_0, + PROP_USER_NAME, + PROP_REAL_NAME, + PROP_AUTOMATIC_LOGIN, +}; + +enum +{ + CHANGED_SIGNAL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE (AccountsUser, accounts_user, G_TYPE_DBUS_PROXY); + +static void +accounts_user_finalize (GObject *object) +{ + G_GNUC_UNUSED AccountsUser *user = ACCOUNTS_USER (object); + + if (G_OBJECT_CLASS (accounts_user_parent_class)->finalize != NULL) + G_OBJECT_CLASS (accounts_user_parent_class)->finalize (object); +} + +static void +accounts_user_init (AccountsUser *user) +{ + /* Sets the expected interface */ + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (user), accounts_user_get_interface_info ()); +} + +static void +accounts_user_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AccountsUser *user = ACCOUNTS_USER (object); + + switch (prop_id) + { + case PROP_USER_NAME: + g_value_set_string (value, accounts_user_get_user_name (user)); + break; + + case PROP_REAL_NAME: + g_value_set_string (value, accounts_user_get_real_name (user)); + break; + + case PROP_AUTOMATIC_LOGIN: + g_value_set_boolean (value, accounts_user_get_automatic_login (user)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +const gchar * +accounts_user_get_user_name (AccountsUser *user) +{ + GVariant *value; + const gchar *ret; + g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "UserName", NULL); + ret = g_variant_get_string (value, NULL); + g_variant_unref (value); + return ret; +} + +const gchar * +accounts_user_get_real_name (AccountsUser *user) +{ + GVariant *value; + const gchar *ret; + g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "RealName", NULL); + ret = g_variant_get_string (value, NULL); + g_variant_unref (value); + return ret; +} + +gboolean +accounts_user_get_automatic_login (AccountsUser *user) +{ + GVariant *value; + gboolean ret; + g_return_val_if_fail (ACCOUNTS_IS_USER (user), FALSE); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "AutomaticLogin", NULL); + ret = g_variant_get_boolean (value); + g_variant_unref (value); + return ret; +} + +static void +accounts_user_g_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters) +{ + AccountsUser *user = ACCOUNTS_USER (proxy); + if (g_strcmp0 (signal_name, "Changed") == 0) + g_signal_emit (user, signals[CHANGED_SIGNAL], 0); +} + +static void +accounts_user_g_properties_changed (GDBusProxy *proxy, + GVariant *changed_properties) +{ + AccountsUser *user = ACCOUNTS_USER (proxy); + GVariantIter *iter; + GVariant *item; + + g_variant_get (changed_properties, "a{sv}", &iter); + while ((item = g_variant_iter_next_value (iter)) != NULL) + { + const gchar *key; + g_variant_get (item, + "{sv}", + &key, + NULL); + if (g_strcmp0 (key, "AutomaticLogin") == 0) + g_object_notify (G_OBJECT (user), "automatic-login"); + else if (g_strcmp0 (key, "RealName") == 0) + g_object_notify (G_OBJECT (user), "real-name"); + else if (g_strcmp0 (key, "UserName") == 0) + g_object_notify (G_OBJECT (user), "user-name"); + } +} + +static void +accounts_user_class_init (AccountsUserClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->get_property = accounts_user_get_property; + gobject_class->finalize = accounts_user_finalize; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = accounts_user_g_signal; + proxy_class->g_properties_changed = accounts_user_g_properties_changed; + + g_object_class_install_property (gobject_class, + PROP_USER_NAME, + g_param_spec_string ("user-name", + "User Name", + "The user name of the user", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_REAL_NAME, + g_param_spec_string ("real-name", + "Real Name", + "The real name of the user", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_AUTOMATIC_LOGIN, + g_param_spec_boolean ("automatic-login", + "Automatic Login", + "Whether the user is automatically logged in", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + signals[CHANGED_SIGNAL] = g_signal_new ("changed", + ACCOUNTS_TYPE_USER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (AccountsUserClass, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +gchar * +accounts_user_frobnicate_sync (AccountsUser *user, + const gchar *flux, + gint baz, + GCancellable *cancellable, + GError **error) +{ + gchar *ret; + GVariant *value; + + g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); + + ret = NULL; + + value = g_dbus_proxy_call_sync (G_DBUS_PROXY (user), + "Frobnicate", + g_variant_new ("(si)", + flux, + baz), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (value != NULL) + { + g_variant_get (value, "(s)", &ret); + ret = g_strdup (ret); + g_variant_unref (value); + } + return ret; +} + +void +accounts_user_frobnicate (AccountsUser *user, + const gchar *flux, + gint baz, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (ACCOUNTS_IS_USER (user)); + g_dbus_proxy_call (G_DBUS_PROXY (user), + "Frobnicate", + g_variant_new ("(si)", + flux, + baz), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + + +gchar * +accounts_user_frobnicate_finish (AccountsUser *user, + GAsyncResult *res, + GError **error) +{ + gchar *ret; + GVariant *value; + + ret = NULL; + value = g_dbus_proxy_call_finish (G_DBUS_PROXY (user), res, error); + if (value != NULL) + { + g_variant_get (value, "(s)", &ret); + ret = g_strdup (ret); + g_variant_unref (value); + } + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Example usage of the AccountsUser type */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +print_user (AccountsUser *user) +{ + g_print (" user-name = `%s'\n", accounts_user_get_user_name (user)); + g_print (" real-name = `%s'\n", accounts_user_get_real_name (user)); + g_print (" automatic-login = %s\n", accounts_user_get_automatic_login (user) ? "true" : "false"); +} + +static void +on_changed (AccountsUser *user, + gpointer user_data) +{ + g_print ("+++ Received the AccountsUser::changed signal\n"); + print_user (user); +} + +static void +on_notify (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + AccountsUser *user = ACCOUNTS_USER (object); + g_print ("+++ Received the GObject::notify signal for property `%s'\n", + pspec->name); + print_user (user); +} + +static void +on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + AccountsUser *user = ACCOUNTS_USER (proxy); + + g_print ("+++ Acquired proxy for user\n"); + print_user (user); + + g_signal_connect (proxy, + "notify", + G_CALLBACK (on_notify), + NULL); + g_signal_connect (user, + "changed", + G_CALLBACK (on_changed), + NULL); +} + +static void +on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("--- Cannot create proxy for user: no remote object\n"); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +gint +main (gint argc, gchar *argv[]) +{ + guint watcher_id; + GMainLoop *loop; + + g_type_init (); + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SYSTEM, + "org.freedesktop.Accounts", + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + "/org/freedesktop/Accounts/User500", + "org.freedesktop.Accounts.User", + ACCOUNTS_TYPE_USER, + G_DBUS_PROXY_FLAGS_NONE, + on_proxy_appeared, + on_proxy_vanished, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + g_bus_unwatch_proxy (watcher_id); + + return 0; +} From b690e637d46057f6914a6b6f20b2688cd03f0ac5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 12 May 2010 17:56:56 -0400 Subject: [PATCH 52/76] Add some more verbiage --- docs/reference/gio/migrating-gdbus.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index b8967fd96..48a87fc9b 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -218,7 +218,17 @@ on_proxy_appeared (GDBusConnection *connection, proxy asynchronously, and only calls your callback when the proxy is ready for use. +
+
+ Client-side GObject bindings + + dbus-glib comes with dbus-binding-tool, which + can produce somewhat nice client-side wrappers for a D-Bus interface. + GDBus does not have code-generation at this point, but #GDBusProxy + is designed to allow the creating of client-side wrappers by + subclassing #GDBusProxy. + For an example of a #GDBusProxy-derived class that wraps a D-Bus interface in a type-safe way, see Date: Wed, 12 May 2010 20:43:40 -0400 Subject: [PATCH 53/76] GDBusProxy: Remove error in get_cached_property() and add set_cached_property() This makes it possible to use the cached properties mechanism even if constructing the proxy with the DO_NOT_LOAD_PROPERTIES flag. This is useful for cases where you obtain the and track object properties out-of-band. For example, in udisks, the plan is to have something like this Manager.GetObjects (out ao paths, out aa{sa{sv}} all_properties); Manager.ObjectAdded (o path, a{sa{sv}} all_properties); Manager.ObjectChanged (o path, a{sa{sv}} all_properties); Manager.ObjectRemoved (o path, a{sa{sv}} all_properties); E.g. the first GetObjects() call will return *all* data about *all* exported objects. Further, this way a client will only need to listen these three signals (three AddMatch) on the Manager object and it will never need to do GetAll() etc (e.g. can use DO_NOT_LOAD_PROPERTIES). (Of course this only works if the client is interested in all objects... while this is true for udisks it is generally not true for other D-Bus services). Also use expected_interface to check for programming errors. --- docs/reference/gio/gio-sections.txt | 3 +- gio/gdbusproxy.c | 162 +++++++++++++++++------ gio/gdbusproxy.h | 4 +- gio/gio.symbols | 1 + gio/tests/gdbus-example-proxy-subclass.c | 6 +- gio/tests/gdbus-example-watch-proxy.c | 2 +- gio/tests/gdbus-peer.c | 3 +- gio/tests/gdbus-proxy.c | 44 +++++- 8 files changed, 174 insertions(+), 51 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 001217e92..108567410 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2490,8 +2490,9 @@ g_dbus_proxy_get_object_path g_dbus_proxy_get_interface_name g_dbus_proxy_get_default_timeout g_dbus_proxy_set_default_timeout -g_dbus_proxy_get_cached_property_names g_dbus_proxy_get_cached_property +g_dbus_proxy_set_cached_property +g_dbus_proxy_get_cached_property_names g_dbus_proxy_set_interface_info g_dbus_proxy_get_interface_info g_dbus_proxy_call diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 9d1d6535d..c1c97d251 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -490,66 +490,149 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, return names; } +static const GDBusPropertyInfo * +lookup_property_info_or_warn (GDBusProxy *proxy, + const gchar *property_name) +{ + const GDBusPropertyInfo *info; + + if (proxy->priv->expected_interface == NULL) + return NULL; + + info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name); + if (info == NULL) + { + g_warning ("Trying to lookup property %s which isn't in expected interface %s", + property_name, + proxy->priv->expected_interface->name); + } + + return info; +} + /** * g_dbus_proxy_get_cached_property: * @proxy: A #GDBusProxy. * @property_name: Property name. - * @error: Return location for error or %NULL. * - * Looks up the value for a property from the cache. This call does no blocking IO. + * Looks up the value for a property from the cache. This call does no + * blocking IO. * - * Normally you will not need to modify the returned variant since it is updated automatically - * in response to org.freedesktop.DBus.Properties.PropertiesChanged - * D-Bus signals (which also causes #GDBusProxy::g-properties-changed to be emitted). + * If @proxy has an expected interface (see + * #GDBusProxy:g-interface-info), then @property_name (for existence) + * is checked against it. * - * However, for properties for which said D-Bus signal is not emitted, you - * can catch other signals and modify the returned variant accordingly (remember to emit - * #GDBusProxy::g-properties-changed yourself). - * - * Returns: A reference to the #GVariant instance that holds the value for @property_name or - * %NULL if @error is set. Free the reference with g_variant_unref(). + * Returns: A reference to the #GVariant instance that holds the value + * for @property_name or %NULL if the value is not in the cache. The + * returned reference must be freed with g_variant_unref(). * * Since: 2.26 */ GVariant * g_dbus_proxy_get_cached_property (GDBusProxy *proxy, - const gchar *property_name, - GError **error) + const gchar *property_name) { GVariant *value; g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); g_return_val_if_fail (property_name != NULL, NULL); - value = NULL; - - if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Properties are not available (proxy created with G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)")); - goto out; - } - value = g_hash_table_lookup (proxy->priv->properties, property_name); if (value == NULL) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("No property with name %s"), - property_name); + const GDBusPropertyInfo *info; + info = lookup_property_info_or_warn (proxy, property_name); + /* no difference */ goto out; } g_variant_ref (value); out: - return value; } +/** + * g_dbus_proxy_set_cached_property: + * @proxy: A #GDBusProxy + * @property_name: Property name. + * @value: Value for the property or %NULL to remove it from the cache. + * + * If @value is not %NULL, sets the cached value for the property with + * name @property_name to the value in @value. + * + * If @value is %NULL, then the cached value is removed from the + * property cache. + * + * If @proxy has an expected interface (see + * #GDBusProxy:g-interface-info), then @property_name (for existence) + * and @value (for the type) is checked against it. + * + * If the @value #GVariant is floating, it is consumed. This allows + * convenient 'inline' use of g_variant_new(), e.g. + * |[ + * g_dbus_proxy_set_cached_property (proxy, + * "SomeProperty", + * g_variant_new ("(si)", + * "A String", + * 42)); + * ]| + * + * Normally you will not need to use this method since @proxy is + * tracking changes using the + * org.freedesktop.DBus.Properties.PropertiesChanged + * D-Bus signal. However, for performance reasons an object may decide + * to not use this signal for some properties and instead use a + * proprietary out-of-band mechanism to transmit changes. + * + * As a concrete example, consider an object with a property + * ChatroomParticipants which is an array of + * strings. Instead of transmitting the same (long) array every time + * the property changes, it is more efficient to only transmit the + * delta using e.g. signals ChatroomParticipantJoined(String + * name) and ChatroomParticipantParted(String + * name). + * + * Since: 2.26 + */ +void +g_dbus_proxy_set_cached_property (GDBusProxy *proxy, + const gchar *property_name, + GVariant *value) +{ + const GDBusPropertyInfo *info; + + g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (property_name != NULL); + + if (value != NULL) + { + info = lookup_property_info_or_warn (proxy, property_name); + if (info != NULL) + { + if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0) + { + g_warning (_("Trying to set property %s of type %s but according to the expected " + "interface the type is %s"), + property_name, + g_variant_get_type_string (value), + info->signature); + goto out; + } + } + g_hash_table_insert (proxy->priv->properties, + g_strdup (property_name), + g_variant_ref_sink (value)); + } + else + { + g_hash_table_remove (proxy->priv->properties, property_name); + } + + out: + ; +} + /* ---------------------------------------------------------------------------------------------------- */ static void @@ -1306,13 +1389,15 @@ lookup_method_info_or_warn (GDBusProxy *proxy, { const GDBusMethodInfo *info; - if (!proxy->priv->expected_interface) + if (proxy->priv->expected_interface == NULL) return NULL; info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name); - if (!info) - g_warning ("Trying to invoke method %s which isn't in expected interface %s", - method_name, proxy->priv->expected_interface->name); + if (info == NULL) + { + g_warning ("Trying to invoke method %s which isn't in expected interface %s", + method_name, proxy->priv->expected_interface->name); + } return info; } @@ -1586,9 +1671,12 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy, if (proxy->priv->expected_interface) { expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name); - if (!expected_method_info) - g_warning ("Trying to invoke method %s which isn't in expected interface %s", - target_method_name, target_interface_name); + if (expected_method_info == NULL) + { + g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'", + target_method_name, + target_interface_name); + } } else { diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index ee9f62eda..fcf3f9649 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -122,8 +122,10 @@ GDBusInterfaceInfo *g_dbus_proxy_get_interface_info (GDBusProxy *pr void g_dbus_proxy_set_interface_info (GDBusProxy *proxy, GDBusInterfaceInfo *info); GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *proxy, + const gchar *property_name); +void g_dbus_proxy_set_cached_property (GDBusProxy *proxy, const gchar *property_name, - GError **error); + GVariant *value); gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, GError **error); void g_dbus_proxy_call (GDBusProxy *proxy, diff --git a/gio/gio.symbols b/gio/gio.symbols index e77274826..7f1302e3e 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1663,6 +1663,7 @@ g_dbus_proxy_new g_dbus_proxy_new_finish g_dbus_proxy_new_sync g_dbus_proxy_get_cached_property +g_dbus_proxy_set_cached_property g_dbus_proxy_get_cached_property_names g_dbus_proxy_get_connection g_dbus_proxy_get_default_timeout diff --git a/gio/tests/gdbus-example-proxy-subclass.c b/gio/tests/gdbus-example-proxy-subclass.c index 6c0990966..110cbdb0f 100644 --- a/gio/tests/gdbus-example-proxy-subclass.c +++ b/gio/tests/gdbus-example-proxy-subclass.c @@ -160,7 +160,7 @@ accounts_user_get_user_name (AccountsUser *user) GVariant *value; const gchar *ret; g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "UserName", NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "UserName"); ret = g_variant_get_string (value, NULL); g_variant_unref (value); return ret; @@ -172,7 +172,7 @@ accounts_user_get_real_name (AccountsUser *user) GVariant *value; const gchar *ret; g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "RealName", NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "RealName"); ret = g_variant_get_string (value, NULL); g_variant_unref (value); return ret; @@ -184,7 +184,7 @@ accounts_user_get_automatic_login (AccountsUser *user) GVariant *value; gboolean ret; g_return_val_if_fail (ACCOUNTS_IS_USER (user), FALSE); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "AutomaticLogin", NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "AutomaticLogin"); ret = g_variant_get_boolean (value); g_variant_unref (value); return ret; diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index db543a836..00768bc3e 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -32,7 +32,7 @@ print_properties (GDBusProxy *proxy) const gchar *key = property_names[n]; GVariant *value; gchar *value_str; - value = g_dbus_proxy_get_cached_property (proxy, key, NULL); + value = g_dbus_proxy_get_cached_property (proxy, key); value_str = g_variant_print (value, TRUE); g_print (" %s -> %s\n", key, value_str); g_variant_unref (value); diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index e96666a8c..8571e73f5 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -531,8 +531,7 @@ test_peer (void) g_assert_no_error (error); g_assert (proxy != NULL); error = NULL; - value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error); - g_assert_no_error (error); + value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty"); g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue"); /* try invoking a method */ diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c index 5df6ad532..a0fabd8a0 100644 --- a/gio/tests/gdbus-proxy.c +++ b/gio/tests/gdbus-proxy.c @@ -150,13 +150,11 @@ test_properties (GDBusConnection *connection, * * No need to test all properties - GVariant has already been tested */ - variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); - g_assert_no_error (error); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); g_assert (variant != NULL); g_assert_cmpint (g_variant_get_byte (variant), ==, 1); g_variant_unref (variant); - variant = g_dbus_proxy_get_cached_property (proxy, "o", &error); - g_assert_no_error (error); + variant = g_dbus_proxy_get_cached_property (proxy, "o"); g_assert (variant != NULL); g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path"); g_variant_unref (variant); @@ -180,11 +178,20 @@ test_properties (GDBusConnection *connection, g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); g_variant_unref (result); _g_assert_signal_received (proxy, "g-properties-changed"); - variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); - g_assert_no_error (error); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); g_assert (variant != NULL); g_assert_cmpint (g_variant_get_byte (variant), ==, 42); g_variant_unref (variant); + + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (142)); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 142); + g_variant_unref (variant); + + g_dbus_proxy_set_cached_property (proxy, "y", NULL); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); + g_assert (variant == NULL); } /* ---------------------------------------------------------------------------------------------------- */ @@ -352,6 +359,7 @@ static const gchar *frob_dbus_interface_xml = " " " " " " + " " " " ""; static GDBusInterfaceInfo *frob_dbus_interface_info; @@ -367,6 +375,12 @@ on_proxy_appeared (GDBusConnection *connection, test_properties (connection, name, name_owner, proxy); test_signals (connection, name, name_owner, proxy); + /* This is obviously wrong but expected interface is not set so we don't fail... */ + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!")); + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42)); + g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something")); + g_dbus_proxy_set_cached_property (proxy, "does-not-exist", NULL); + /* Now repeat the method tests, with an expected interface set */ g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); test_methods (connection, name, name_owner, proxy); @@ -376,6 +390,24 @@ on_proxy_appeared (GDBusConnection *connection, */ test_bogus_method_return (connection, name, name_owner, proxy); + /* Also check that we complain if setting a cached property of the wrong type */ + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) + { + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!")); + } + g_test_trap_assert_stderr ("*Trying to set property y of type s but according to the expected interface the type is y*"); + g_test_trap_assert_failed(); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) + { + g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something")); + } + g_test_trap_assert_stderr ("*Trying to lookup property does-not-exist which isn't in expected interface com.example.Frob*"); + g_test_trap_assert_failed(); + + /* this should work, however (since the type is correct) */ + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42)); + g_main_loop_quit (loop); } From cce08f197313bad1516924b3eb7305e7e6818971 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 12 May 2010 21:51:06 -0400 Subject: [PATCH 54/76] Add a note about implemented standard interfaces --- gio/gdbusconnection.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index aa5ad059f..3c26090a6 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3923,6 +3923,13 @@ obj_message_func (GDBusConnection *connection, * If an existing callback is already registered at @object_path and * @interface_name, then @error is set to #G_IO_ERROR_EXISTS. * + * GDBus automatically implements the standard D-Bus interfaces + * org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable + * and org.freedesktop.Peer, so you don't have to implement those for + * the objects you export. You can implement + * org.freedesktop.DBus.Properties yourself, e.g. to handle getting + * and setting of properties asynchronously. + * * See for an example of how to use this method. * * Returns: 0 if @error is set, otherwise a registration id (never 0) From 9695c23d4c29e79afbe14e3584b6c42e98e8f0d9 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 12 May 2010 22:09:18 -0400 Subject: [PATCH 55/76] GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; }; --- gio/gdbus-tool.c | 43 ++++++++++++++++++++++++++++++++ gio/tests/gdbus-example-server.c | 18 ++++++++++--- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index ce5e5dba1..96a66ca80 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -881,6 +881,20 @@ handle_call (gint *argc, /* TODO: dump annotations */ +static void +dump_annotation (const GDBusAnnotationInfo *o, + guint indent, + gboolean ignore_indent) +{ + guint n; + g_print ("%*s@%s(\"%s\")\n", + ignore_indent ? 0 : indent, "", + o->key, + o->value); + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + dump_annotation (o->annotations[n], indent + 2, FALSE); +} + static void dump_arg (const GDBusArgInfo *o, guint indent, @@ -888,6 +902,14 @@ dump_arg (const GDBusArgInfo *o, gboolean ignore_indent, gboolean include_newline) { + guint n; + + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + { + dump_annotation (o->annotations[n], indent, ignore_indent); + ignore_indent = FALSE; + } + g_print ("%*s%s%s %s%s", ignore_indent ? 0 : indent, "", direction, @@ -917,6 +939,10 @@ dump_method (const GDBusMethodInfo *o, guint m; guint name_len; guint total_num_args; + + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + dump_annotation (o->annotations[n], indent, FALSE); + g_print ("%*s%s(", indent, "", o->name); name_len = strlen (o->name); total_num_args = count_args (o->in_args) + count_args (o->out_args); @@ -924,6 +950,7 @@ dump_method (const GDBusMethodInfo *o, { gboolean ignore_indent = (m == 0); gboolean include_newline = (m != total_num_args - 1); + dump_arg (o->in_args[n], indent + name_len + 1, "in ", @@ -950,6 +977,10 @@ dump_signal (const GDBusSignalInfo *o, guint n; guint name_len; guint total_num_args; + + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + dump_annotation (o->annotations[n], indent, FALSE); + g_print ("%*s%s(", indent, "", o->name); name_len = strlen (o->name); total_num_args = count_args (o->args); @@ -972,6 +1003,8 @@ dump_property (const GDBusPropertyInfo *o, GVariant *value) { const gchar *access; + guint n; + if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE) access = "readonly"; else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE) @@ -980,6 +1013,10 @@ dump_property (const GDBusPropertyInfo *o, access = "readwrite"; else g_assert_not_reached (); + + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + dump_annotation (o->annotations[n], indent, FALSE); + if (value != NULL) { gchar *s = g_variant_print (value, FALSE); @@ -1074,6 +1111,9 @@ dump_interface (GDBusConnection *c, } } + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + dump_annotation (o->annotations[n], indent, FALSE); + g_print ("%*sinterface %s {\n", indent, "", o->name); if (o->methods != NULL) { @@ -1117,6 +1157,9 @@ dump_node (GDBusConnection *c, if (o->path != NULL) object_path_to_print = o->path; + for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++) + dump_annotation (o->annotations[n], indent, FALSE); + g_print ("%*snode %s", indent, "", object_path_to_print != NULL ? object_path_to_print : "(not set)"); if (o->interfaces != NULL || o->nodes != NULL) { diff --git a/gio/tests/gdbus-example-server.c b/gio/tests/gdbus-example-server.c index eb0d6e459..a943f6555 100644 --- a/gio/tests/gdbus-example-server.c +++ b/gio/tests/gdbus-example-server.c @@ -14,19 +14,31 @@ static GDBusNodeInfo *introspection_data = NULL; static const gchar introspection_xml[] = "" " " + " " + " " " " + " " " " " " " " " " - " " + " " + " " + " " " " " " " " + " " " " - " " + " " + " " + " " " " - " " + " " + " " + " " + " " + " " " " " " " " From ea1e0496b0329147b932d5a1486f5a81b4121651 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 12 May 2010 23:12:14 -0400 Subject: [PATCH 56/76] GDBus: add 'monitor' verb to gdbus(1) This uncovered a bug in name watching if the name wasn't activatable. Also provoked the need for on_connection variants of g_bus_watch_name (added g_bus_watch_proxy's variant as well). --- docs/reference/gio/gdbus.xml | 43 +++++ docs/reference/gio/gio-sections.txt | 2 + gio/gdbus-tool.c | 253 +++++++++++++++++++++++++++- gio/gdbusnamewatching.c | 69 +++++++- gio/gdbusnamewatching.h | 23 ++- gio/gdbusproxywatching.c | 86 ++++++++++ gio/gdbusproxywatching.h | 35 ++-- gio/gio.symbols | 2 + 8 files changed, 489 insertions(+), 24 deletions(-) diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml index 4bd0eb746..a83511a81 100644 --- a/docs/reference/gio/gdbus.xml +++ b/docs/reference/gio/gdbus.xml @@ -23,6 +23,19 @@ --dest bus_name --object-path /path/to/object + + gdbus + monitor + + --system + --session + --address address + + --dest bus_name + + --object-path /path/to/object + + gdbus call @@ -60,6 +73,13 @@ org.freedesktop.DBus.Introspectable interface. + + + + Monitors one or all objects owned by the owned of + bus_name. + + @@ -189,6 +209,29 @@ $ gdbus call --session \ 5000 (uint32 12,) + + Monitoring all objects on a service: + + +$ gdbus monitor --system --dest org.freedesktop.ConsoleKit +Monitoring signals from all objects owned by org.freedesktop.ConsoleKit +The name org.freedesktop.ConsoleKit is owned by :1.15 +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (false,) +/org/freedesktop/ConsoleKit/Seat1: org.freedesktop.ConsoleKit.Seat.ActiveSessionChanged ('',) +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (true,) +/org/freedesktop/ConsoleKit/Seat1: org.freedesktop.ConsoleKit.Seat.ActiveSessionChanged ('/org/freedesktop/ConsoleKit/Session2',) + + + Monitoring a single object on a service: + + +$ gdbus monitor --system --dest org.freedesktop.ConsoleKit --object-path /org/freedesktop/ConsoleKit/Session2 +Monitoring signals on object /org/freedesktop/ConsoleKit/Session2 owned by org.freedesktop.ConsoleKit +The name org.freedesktop.ConsoleKit is owned by :1.15 +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (false,) +/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (true,) + + diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 108567410..3047f8ede 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2446,6 +2446,7 @@ GBusNameAppearedCallback GBusNameVanishedCallback GBusNameWatcherFlags g_bus_watch_name +g_bus_watch_name_on_connection g_bus_unwatch_name
@@ -2454,6 +2455,7 @@ g_bus_unwatch_name GBusProxyAppearedCallback GBusProxyVanishedCallback g_bus_watch_proxy +g_bus_watch_proxy_on_connection g_bus_unwatch_proxy diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index 96a66ca80..95d81287a 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -92,6 +92,7 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout) s = g_strdup_printf (_("Commands:\n" " help Shows this information\n" " introspect Introspect a remote object\n" + " monitor Monitor a remote object\n" " call Invoke a method on a remote object\n" "\n" "Use \"%s COMMAND --help\" to get help on each command.\n"), @@ -1380,6 +1381,246 @@ handle_introspect (gint *argc, /* ---------------------------------------------------------------------------------------------------- */ +static gchar *opt_monitor_dest = NULL; +static gchar *opt_monitor_object_path = NULL; + +static guint monitor_filter_id = 0; + +static void +monitor_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) +{ + gchar *s; + s = g_variant_print (parameters, TRUE); + g_print ("%s: %s.%s %s\n", + object_path, + interface_name, + signal_name, + s); + g_free (s); +} + +static void +monitor_on_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + g_print ("The name %s is owned by %s\n", name, name_owner); + g_assert (monitor_filter_id == 0); + monitor_filter_id = g_dbus_connection_signal_subscribe (connection, + name_owner, + NULL, /* any interface */ + NULL, /* any member */ + opt_monitor_object_path, + NULL, /* arg0 */ + monitor_signal_cb, + NULL, /* user_data */ + NULL); /* user_data destroy notify */ +} + +static void +monitor_on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_print ("The name %s does not have an owner\n", name); + + if (monitor_filter_id != 0) + { + g_dbus_connection_signal_unsubscribe (connection, monitor_filter_id); + monitor_filter_id = 0; + } +} + +static const GOptionEntry monitor_entries[] = +{ + { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_monitor_dest, N_("Destination name to monitor"), NULL}, + { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_monitor_object_path, N_("Object path to monitor"), NULL}, + { NULL } +}; + +static gboolean +handle_monitor (gint *argc, + gchar **argv[], + gboolean request_completion, + const gchar *completion_cur, + const gchar *completion_prev) +{ + gint ret; + GOptionContext *o; + gchar *s; + GError *error; + GDBusConnection *c; + GVariant *result; + GDBusNodeInfo *node; + gboolean complete_names; + gboolean complete_paths; + GMainLoop *loop; + + ret = FALSE; + c = NULL; + node = NULL; + result = NULL; + + modify_argv0_for_command (argc, argv, "monitor"); + + o = g_option_context_new (NULL); + if (request_completion) + g_option_context_set_ignore_unknown_options (o, TRUE); + g_option_context_set_help_enabled (o, FALSE); + g_option_context_set_summary (o, _("Monitor a remote object.")); + g_option_context_add_main_entries (o, monitor_entries, NULL /* GETTEXT_PACKAGE*/); + g_option_context_add_group (o, connection_get_group ()); + + complete_names = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0) + { + complete_names = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + complete_paths = FALSE; + if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0) + { + complete_paths = TRUE; + remove_arg ((*argc) - 1, argc, argv); + } + + if (!g_option_context_parse (o, argc, argv, NULL)) + { + if (!request_completion) + { + s = g_option_context_get_help (o, FALSE, NULL); + g_printerr ("%s", s); + g_free (s); + goto out; + } + } + + error = NULL; + c = connection_get_dbus_connection (&error); + if (c == NULL) + { + if (request_completion) + { + if (g_strcmp0 (completion_prev, "--address") == 0) + { + g_print ("unix:\n" + "tcp:\n" + "nonce-tcp:\n"); + } + else + { + g_print ("--system \n--session \n--address \n"); + } + } + else + { + g_printerr (_("Error connecting: %s\n"), error->message); + g_error_free (error); + } + goto out; + } + + if (g_dbus_connection_get_unique_name (c) != NULL) + { + if (complete_names) + { + print_names (c, FALSE); + goto out; + } + /* this only makes sense on message bus connections */ + if (opt_monitor_dest == NULL) + { + if (request_completion) + g_print ("--dest \n"); + else + g_printerr (_("Error: Destination is not specified\n")); + goto out; + } + if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0) + { + print_names (c, g_str_has_prefix (opt_monitor_dest, ":")); + goto out; + } + } + if (complete_paths) + { + print_paths (c, opt_monitor_dest, "/"); + goto out; + } + if (opt_monitor_object_path == NULL) + { + if (request_completion) + { + g_print ("--object-path \n"); + goto out; + } + /* it's fine to not have an object path */ + } + if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0) + { + gchar *p; + s = g_strdup (opt_monitor_object_path); + p = strrchr (s, '/'); + if (p != NULL) + { + if (p == s) + p++; + *p = '\0'; + } + print_paths (c, opt_monitor_dest, s); + g_free (s); + goto out; + } + if (!request_completion && (opt_monitor_object_path != NULL && !g_variant_is_object_path (opt_monitor_object_path))) + { + g_printerr (_("Error: %s is not a valid object path\n"), opt_monitor_object_path); + goto out; + } + + /* All done with completion now */ + if (request_completion) + goto out; + + if (opt_monitor_object_path != NULL) + g_print ("Monitoring signals on object %s owned by %s\n", opt_monitor_object_path, opt_monitor_dest); + else + g_print ("Monitoring signals from all objects owned by %s\n", opt_monitor_dest); + + loop = g_main_loop_new (NULL, FALSE); + g_bus_watch_name_on_connection (c, + opt_monitor_dest, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + monitor_on_name_appeared, + monitor_on_name_vanished, + NULL, + NULL); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + ret = TRUE; + + out: + if (node != NULL) + g_dbus_node_info_unref (node); + if (result != NULL) + g_variant_unref (result); + if (c != NULL) + g_object_unref (c); + g_option_context_free (o); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + static gchar * pick_word_at (const gchar *s, gint cursor, @@ -1476,6 +1717,16 @@ main (gint argc, gchar *argv[]) ret = 0; goto out; } + else if (g_strcmp0 (command, "monitor") == 0) + { + if (handle_monitor (&argc, + &argv, + request_completion, + completion_cur, + completion_prev)) + ret = 0; + goto out; + } else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion) { const gchar *completion_line; @@ -1545,7 +1796,7 @@ main (gint argc, gchar *argv[]) { if (request_completion) { - g_print ("help \ncall \nintrospect \n"); + g_print ("help \ncall \nintrospect \nmonitor \n"); ret = 0; goto out; } diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 3d96ee0a9..903e44413 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -417,10 +417,11 @@ start_service_by_name_cb (GObject *source_object, * org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2 * was not provided by any .service files * - * so just report vanished. + * This doesn't mean that the name doesn't have an owner, just + * that it's not provided by a .service file. So proceed to + * invoke GetNameOwner(). */ - call_vanished_handler (client, FALSE); - client->initialized = TRUE; + invoke_get_name_owner (client); } if (result != NULL) @@ -586,6 +587,68 @@ g_bus_watch_name (GBusType bus_type, return client->id; } +/** + * g_bus_watch_name_on_connection: + * @connection: A #GDBusConnection that is not closed. + * @name: The name (well-known or unique) to watch. + * @flags: Flags from the #GBusNameWatcherFlags enumeration. + * @name_appeared_handler: Handler to invoke when @name is known to exist or %NULL. + * @name_vanished_handler: Handler to invoke when @name is known to not exist or %NULL. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Like g_bus_watch_name() but takes a #GDBusConnection instead of a + * #GBusType. + * + * Returns: An identifier (never 0) that an be used with + * g_bus_unwatch_name() to stop watching the name. + * + * Since: 2.26 + */ +guint g_bus_watch_name_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (g_dbus_is_name (name), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->ref_count = 1; + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->flags = flags; + client->name_appeared_handler = name_appeared_handler; + client->name_vanished_handler = name_vanished_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + client->connection = g_object_ref (connection); + G_UNLOCK (lock); + + has_connection (client); + + return client->id; +} + /** * g_bus_unwatch_name: * @watcher_id: An identifier obtained from g_bus_watch_name() diff --git a/gio/gdbusnamewatching.h b/gio/gdbusnamewatching.h index 1bbf8e0cd..a424f4362 100644 --- a/gio/gdbusnamewatching.h +++ b/gio/gdbusnamewatching.h @@ -58,14 +58,21 @@ typedef void (*GBusNameVanishedCallback) (GDBusConnection *connection, gpointer user_data); -guint g_bus_watch_name (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - GBusNameAppearedCallback name_appeared_handler, - GBusNameVanishedCallback name_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func); -void g_bus_unwatch_name (guint watcher_id); +guint g_bus_watch_name (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +guint g_bus_watch_name_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + GBusNameAppearedCallback name_appeared_handler, + GBusNameVanishedCallback name_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_bus_unwatch_name (guint watcher_id); G_END_DECLS diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 7e900393c..282254d46 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -25,6 +25,7 @@ #include #include "gdbusutils.h" +#include "gdbusconnection.h" #include "gdbusnamewatching.h" #include "gdbusproxywatching.h" #include "gdbuserror.h" @@ -362,6 +363,91 @@ g_bus_watch_proxy (GBusType bus_type, return client->id; } +/** + * g_bus_watch_proxy_on_connection: + * @connection: A #GDBusConnection that is not closed. + * @name: The name (well-known or unique) to watch. + * @flags: Flags from the #GBusNameWatcherFlags enumeration. + * @object_path: The object path of the remote object to watch. + * @interface_name: The D-Bus interface name for the proxy. + * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. + * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. + * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the + * requested proxy is available. + * @proxy_vanished_handler: Handler to invoke when @name is known to not exist + * and the previously created proxy is no longer available. + * @user_data: User data to pass to handlers. + * @user_data_free_func: Function for freeing @user_data or %NULL. + * + * Like g_bus_watch_proxy() but takes a #GDBusConnection instead of a + * #GBusType. + * + * Returns: An identifier (never 0) that can be used with + * g_bus_unwatch_proxy() to stop watching the remote object. + * + * Since: 2.26 + */ +guint +g_bus_watch_proxy_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func) +{ + Client *client; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (g_dbus_is_name (name), 0); + g_return_val_if_fail (g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0); + g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0); + + G_LOCK (lock); + + client = g_new0 (Client, 1); + client->id = next_global_id++; /* TODO: uh oh, handle overflow */ + client->name = g_strdup (name); + client->proxy_appeared_handler = proxy_appeared_handler; + client->proxy_vanished_handler = proxy_vanished_handler; + client->user_data = user_data; + client->user_data_free_func = user_data_free_func; + client->main_context = g_main_context_get_thread_default (); + if (client->main_context != NULL) + g_main_context_ref (client->main_context); + client->name_watcher_id = g_bus_watch_name_on_connection (connection, + name, + flags, + on_name_appeared, + on_name_vanished, + client, + NULL); + + client->object_path = g_strdup (object_path); + client->interface_name = g_strdup (interface_name); + client->interface_type = interface_type; + client->proxy_flags = proxy_flags; + client->initial_construction = TRUE; + + if (map_id_to_client == NULL) + { + map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (map_id_to_client, + GUINT_TO_POINTER (client->id), + client); + + G_UNLOCK (lock); + + return client->id; +} + + /** * g_bus_unwatch_proxy: * @watcher_id: An identifier obtained from g_bus_watch_proxy() diff --git a/gio/gdbusproxywatching.h b/gio/gdbusproxywatching.h index 2dddaffbf..37c88dd35 100644 --- a/gio/gdbusproxywatching.h +++ b/gio/gdbusproxywatching.h @@ -63,18 +63,29 @@ typedef void (*GBusProxyVanishedCallback) (GDBusConnection *connection, const gchar *name, gpointer user_data); -guint g_bus_watch_proxy (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GBusProxyAppearedCallback proxy_appeared_handler, - GBusProxyVanishedCallback proxy_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func); -void g_bus_unwatch_proxy (guint watcher_id); +guint g_bus_watch_proxy (GBusType bus_type, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +guint g_bus_watch_proxy_on_connection (GDBusConnection *connection, + const gchar *name, + GBusNameWatcherFlags flags, + const gchar *object_path, + const gchar *interface_name, + GType interface_type, + GDBusProxyFlags proxy_flags, + GBusProxyAppearedCallback proxy_appeared_handler, + GBusProxyVanishedCallback proxy_vanished_handler, + gpointer user_data, + GDestroyNotify user_data_free_func); +void g_bus_unwatch_proxy (guint watcher_id); G_END_DECLS diff --git a/gio/gio.symbols b/gio/gio.symbols index 7f1302e3e..0d7c106ad 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1652,6 +1652,7 @@ g_bus_unown_name #if IN_HEADER(__G_DBUS_NAME_WATCHING_H__) #if IN_FILE(__G_DBUS_NAME_WATCHING_C__) g_bus_watch_name +g_bus_watch_name_on_connection g_bus_unwatch_name #endif #endif @@ -1683,6 +1684,7 @@ g_dbus_proxy_call_sync #if IN_HEADER(__G_DBUS_PROXY_WATCHING_H__) #if IN_FILE(__G_DBUS_PROXY_WATCHING_C__) g_bus_watch_proxy +g_bus_watch_proxy_on_connection g_bus_unwatch_proxy #endif #endif From 9a065edf6f51be2ad189cfb02ddd2c806b656303 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 00:39:26 -0400 Subject: [PATCH 57/76] Add an example of exporting a GObject This is more manual work than dbus-glib. --- gio/gdbusconnection.c | 2 + gio/tests/Makefile.am | 4 + gio/tests/gdbus-example-export.c | 331 +++++++++++++++++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 gio/tests/gdbus-example-export.c diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 3c26090a6..559df84a0 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -156,6 +156,8 @@ * D-Bus subtree exampleFIXME: MISSING XINCLUDE CONTENT * * D-Bus UNIX File Descriptor exampleFIXME: MISSING XINCLUDE CONTENT + * + * Exporting a GObjectFIXME: MISSING XINCLUDE CONTENT */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index cbd63a16d..f38358046 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -58,6 +58,7 @@ SAMPLE_PROGS = \ httpd \ send-data \ filter-cat \ + gdbus-example-export \ gdbus-example-own-name \ gdbus-example-watch-name \ gdbus-example-watch-proxy \ @@ -234,6 +235,9 @@ gdbus_example_peer_LDADD = $(progs_ldadd) gdbus_example_proxy_subclass_SOURCES = gdbus-example-proxy-subclass.c gdbus_example_proxy_subclass_LDADD = $(progs_ldadd) +gdbus_example_export_SOURCES = gdbus-example-export.c +gdbus_example_export_LDADD = $(progs_ldadd) + EXTRA_DIST += \ socket-common.c \ org.gtk.test.gschema \ diff --git a/gio/tests/gdbus-example-export.c b/gio/tests/gdbus-example-export.c new file mode 100644 index 000000000..5965ed8ce --- /dev/null +++ b/gio/tests/gdbus-example-export.c @@ -0,0 +1,331 @@ +#include +#include + +* ---------------------------------------------------------------------------------------------------- */ + +/* The object we want to export */ +typedef struct _MyObjectClass MyObjectClass; +typedef struct _MyObject MyObject; + +struct _MyObjectClass +{ + GObjectClass parent_class; +}; + +struct _MyObject +{ + GObject parent_instance; + + gint count; + gchar *name; +}; + +enum +{ + PROP_0, + PROP_COUNT, + PROP_NAME +}; + +G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT); + +static void +my_object_finalize (GObject *object) +{ + MyObject *myobj = (MyObject*)object; + + g_free (myobj->name); + + G_OBJECT_CLASS (my_object_parent_class)->finalize (object); +} + +static void +my_object_init (MyObject *object) +{ + object->count = 0; + object->name = NULL; +} + +static void +my_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MyObject *myobj = (MyObject*)object; + + switch (prop_id) + { + case PROP_COUNT: + g_value_set_int (value, myobj->count); + break; + + case PROP_NAME: + g_value_set_string (value, myobj->name); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +my_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MyObject *myobj = (MyObject*)object; + + switch (prop_id) + { + case PROP_COUNT: + myobj->count = g_value_get_int (value); + break; + + case PROP_NAME: + g_free (myobj->name); + myobj->name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +my_object_class_init (MyObjectClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = my_object_finalize; + gobject_class->set_property = my_object_set_property; + gobject_class->get_property = my_object_get_property; + + g_object_class_install_property (gobject_class, + PROP_COUNT, + g_param_spec_int ("count", + "Count", + "Count", + 0, 99999, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "Name", + NULL, + G_PARAM_READWRITE)); +} + +/* A method that we want to export */ +void +my_object_change_count (MyObject *myobj, + gint change) +{ + myobj->count = 2 * myobj->count + change; + + g_object_notify (G_OBJECT (myobj), "count"); +} + +static GDBusNodeInfo *introspection_data = NULL; + +/* Introspection data for the service we are exporting */ +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + + +static void +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) +{ + MyObject *myobj = user_data; + + if (g_strcmp0 (method_name, "ChangeCount") == 0) + { + gint change; + g_variant_get (parameters, "(i)", &change); + + my_object_change_count (myobj, change); + + g_dbus_method_invocation_return_value (invocation, NULL); + } +} + +static GVariant * +handle_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GVariant *ret; + MyObject *myobj = user_data; + + ret = NULL; + if (g_strcmp0 (property_name, "Count") == 0) + { + ret = g_variant_new_int32 (myobj->count); + } + else if (g_strcmp0 (property_name, "Name") == 0) + { + ret = g_variant_new_string (myobj->name ? myobj->name : ""); + } + + return ret; +} + +static gboolean +handle_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + MyObject *myobj = user_data; + + if (g_strcmp0 (property_name, "Count") == 0) + { + g_object_set (myobj, "count", g_variant_get_int32 (value), NULL); + } + else if (g_strcmp0 (property_name, "Name") == 0) + { + g_object_set (myobj, "name", g_variant_get_string (value, NULL), NULL); + } + + return TRUE; +} + + +/* for now */ +static const GDBusInterfaceVTable interface_vtable = +{ + handle_method_call, + handle_get_property, + handle_set_property +}; + +static void +send_property_change (GObject *obj, + GParamSpec *pspec, + GDBusConnection *connection) +{ + GVariantBuilder *builder; + MyObject *myobj = (MyObject *)obj; + + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + + if (g_strcmp0 (pspec->name, "count") == 0) + g_variant_builder_add (builder, + "{sv}", + "Count", g_variant_new_int32 (myobj->count)); + else if (g_strcmp0 (pspec->name, "name") == 0) + g_variant_builder_add (builder, + "{sv}", + "Name", g_variant_new_string (myobj->name ? myobj->name : "")); + + g_dbus_connection_emit_signal (connection, + NULL, + "/org/myorg/MyObject", + "org.myorg.MyObject", + "PropertiesChanged", + g_variant_new ("(sa{sv})", + "org.myorg.MyObject", + builder), + NULL); +} + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + MyObject *myobj = user_data; + guint registration_id; + + g_signal_connect (myobj, "notify", + G_CALLBACK (send_property_change), connection); + registration_id = g_dbus_connection_register_object (connection, + "/org/myorg/MyObject", + "org.myorg.MyObject", + introspection_data->interfaces[0], + &interface_vtable, + myobj, + NULL, /* user_data_free_func */ + NULL); /* GError** */ + g_assert (registration_id > 0); +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + exit (1); +} + +int +main (int argc, char *argv[]) +{ + guint owner_id; + GMainLoop *loop; + MyObject *myobj; + + g_type_init (); + + /* We are lazy here - we don't want to manually provide + * the introspection data structures - so we just build + * them from XML. + */ + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + myobj = g_object_new (my_object_get_type (), NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.myorg.MyObject", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + myobj, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_bus_unown_name (owner_id); + + g_dbus_node_info_unref (introspection_data); + + g_object_unref (myobj); + + return 0; +} From 2d75583fb2a8fdb71b9ee880dc0cf4605ab7bc6c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 01:04:29 -0400 Subject: [PATCH 58/76] Fill out the export section of the migration guide --- docs/reference/gio/migrating-gdbus.xml | 66 ++++++++++++++++++++++++++ gio/tests/gdbus-example-export.c | 6 ++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 48a87fc9b..14e70b702 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -287,6 +287,72 @@ on_proxy_appeared (GDBusConnection *connection,
Exporting objects + + + With dbus-glib, exporting an object over D-Bus works by generating + a bunch of glue code from your introspection XML with + dbus-binding-tool. The glue code gets included in + your source, and you need to call + + dbus_g_object_type_install_info (TYPE_MYOBJECT, + &dbus_glib_myobject_object_info); + + in your class_init() function to tell dbus-glib about your type. + To actually export an instance, you call + + dbus_g_connection_register_g_object (system_bus_connection, + my_object_path, + G_OBJECT (my_object)); + + + + + The GDBus way of exporting an object works by embedding the + introspection XML in the source, creating introspection data + structures from it with g_dbus_node_info_new_for_xml(), and + passing that along when you register the object: + " + " " + " " + " " + " " + " " + " " + ""; + + /* parse introspection data */ + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + + / + id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/TestObject", + "org.gtk.GDBus.TestPeerInterface", + introspection_data->interfaces[0], + &interface_vtable, + NULL, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + +]]> + + + + The actual implementation of the exported object is done by specifying + a #GDBusInterfaceVTable that has method_call(), get_property() and + set_property() methods. There is no direct support beyond that for + exporting #GObjects, so there is quite a bit of manual work involved, + as you can see in the following example. + + + Since the VTable methods don't have any direct #GObject support, we + pass the exported object as @user_data. Also note that we have to handle + the emission of the PropertiesChanged signal ourselves, by connecting + to ::notify. + + Exporting a GObjectFIXME: MISSING XINCLUDE CONTENT
diff --git a/gio/tests/gdbus-example-export.c b/gio/tests/gdbus-example-export.c index 5965ed8ce..0f222cebf 100644 --- a/gio/tests/gdbus-example-export.c +++ b/gio/tests/gdbus-example-export.c @@ -1,7 +1,7 @@ #include #include -* ---------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------- */ /* The object we want to export */ typedef struct _MyObjectClass MyObjectClass; @@ -129,6 +129,8 @@ my_object_change_count (MyObject *myobj, g_object_notify (G_OBJECT (myobj), "count"); } +/* ---------------------------------------------------------------------------------------------------- */ + static GDBusNodeInfo *introspection_data = NULL; /* Introspection data for the service we are exporting */ @@ -247,7 +249,7 @@ send_property_change (GObject *obj, g_dbus_connection_emit_signal (connection, NULL, "/org/myorg/MyObject", - "org.myorg.MyObject", + "org.freedesktop.DBus.Properties", "PropertiesChanged", g_variant_new ("(sa{sv})", "org.myorg.MyObject", From 82158afdadd10e6ffd1540f695931f64957b59f6 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 11:56:15 -0400 Subject: [PATCH 59/76] GDBus: Catch up with new PropertiesChanged signal After a long discussion, this has finally been standardized in the D-Bus spec. See http://lists.freedesktop.org/archives/dbus/2010-May/012667.html http://lists.freedesktop.org/archives/dbus/2010-May/012712.html Signed-off-by: David Zeuthen --- gio/gdbusconnection.c | 1 + gio/gdbusproxy.c | 74 +++++++++++++++++++----- gio/gdbusproxy.h | 13 +++-- gio/gio-marshal.list | 1 + gio/tests/gdbus-example-export.c | 7 ++- gio/tests/gdbus-example-proxy-subclass.c | 34 ++++++----- gio/tests/gdbus-example-server.c | 12 ++-- gio/tests/gdbus-example-watch-proxy.c | 55 +++++++++++------- gio/tests/gdbus-testserver.py | 1 + 9 files changed, 136 insertions(+), 62 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 559df84a0..dd49189be 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3549,6 +3549,7 @@ static const gchar introspect_standard_interfaces[] = " \n" " \n" " \n" + " \n" " \n" "
\n" " \n" diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index c1c97d251..17619a148 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -387,10 +387,17 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) /** * GDBusProxy::g-properties-changed: * @proxy: The #GDBusProxy emitting the signal. - * @changed_properties: A #GVariant containing the properties that changed. + * @changed_properties: A #GVariant containing the properties that + * changed or %NULL if no properties changed. + * @invalidated_properties: A %NULL terminated list of properties that was + * invalidated or %NULL if no properties was invalidated. * - * Emitted when one or more D-Bus properties on @proxy changes. The cached properties - * are already replaced when this signal fires. + * Emitted when one or more D-Bus properties on @proxy changes. The + * local cache has already been updated when this signal fires. + * + * This signal corresponds to the + * PropertiesChanged D-Bus signal on the + * org.freedesktop.DBus.Properties interface. * * Since: 2.26 */ @@ -400,10 +407,11 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) G_STRUCT_OFFSET (GDBusProxyClass, g_properties_changed), NULL, NULL, - g_cclosure_marshal_VOID__BOXED, + _gio_marshal_VOID__BOXED_BOXED, G_TYPE_NONE, - 1, - G_TYPE_VARIANT); + 2, + G_TYPE_VARIANT, + G_TYPE_STRV); /** * GDBusProxy::g-signal: @@ -669,12 +677,17 @@ on_properties_changed (GDBusConnection *connection, GError *error; const gchar *interface_name_for_signal; GVariantIter *iter; + GVariantIter *invalidated_iter; GVariant *item; GVariant *changed_properties; GVariantBuilder *builder; + GPtrArray *p; + const gchar *str; + gchar **invalidated_properties; error = NULL; iter = NULL; + invalidated_iter = NULL; #if 0 // TODO! /* Ignore this signal if properties are not yet available @@ -686,27 +699,31 @@ on_properties_changed (GDBusConnection *connection, goto out; #endif - if (strcmp (g_variant_get_type_string (parameters), "(sa{sv})") != 0) + if (strcmp (g_variant_get_type_string (parameters), "(sa{sv}as)") != 0) { - g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv})'", + g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv}as)'", g_variant_get_type_string (parameters)); goto out; } g_variant_get (parameters, - "(sa{sv})", + "(sa{sv}as)", &interface_name_for_signal, - &iter); + &iter, + &invalidated_iter); if (g_strcmp0 (interface_name_for_signal, proxy->priv->interface_name) != 0) goto out; - builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + builder = NULL; while ((item = g_variant_iter_next_value (iter))) { const gchar *key; GVariant *value; + if (builder == NULL) + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + g_variant_get (item, "{sv}", &key, @@ -721,16 +738,45 @@ on_properties_changed (GDBusConnection *connection, g_strdup (key), g_variant_ref (value)); } - changed_properties = g_variant_builder_end (builder); + if (builder != NULL) + changed_properties = g_variant_builder_end (builder); + else + changed_properties = NULL; + + p = NULL; + while (g_variant_iter_loop (invalidated_iter, "s", &str)) + { + if (p == NULL) + p = g_ptr_array_new (); + g_ptr_array_add (p, (gpointer) str); + } + if (p != NULL) + { + g_ptr_array_add (p, NULL); + invalidated_properties = (gchar **) g_ptr_array_free (p, FALSE); + } + else + { + invalidated_properties = NULL; + } + /* emit signal */ - g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL], 0, changed_properties); + g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL], + 0, + changed_properties, + invalidated_properties); - g_variant_unref (changed_properties); + if (changed_properties != NULL) + g_variant_unref (changed_properties); + if (invalidated_properties != NULL) + g_free (invalidated_properties); out: if (iter != NULL) g_variant_iter_free (iter); + if (invalidated_iter != NULL) + g_variant_iter_free (invalidated_iter); } /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index fcf3f9649..f86055593 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -69,12 +69,13 @@ struct _GDBusProxyClass /*< public >*/ /* Signals */ - void (*g_properties_changed) (GDBusProxy *proxy, - GVariant *changed_properties); - void (*g_signal) (GDBusProxy *proxy, - const gchar *sender_name, - const gchar *signal_name, - GVariant *parameters); + void (*g_properties_changed) (GDBusProxy *proxy, + GVariant *changed_properties, + const gchar* const *invalidated_properties); + void (*g_signal) (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters); /*< private >*/ /* Padding for future expansion */ diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list index 8473ea70d..7899b9d69 100644 --- a/gio/gio-marshal.list +++ b/gio/gio-marshal.list @@ -8,3 +8,4 @@ BOOL:POINTER,INT BOOL:UINT VOID:STRING,STRING,BOXED VOID:BOOL,BOXED +VOID:BOXED,BOXED diff --git a/gio/tests/gdbus-example-export.c b/gio/tests/gdbus-example-export.c index 0f222cebf..9dee12f70 100644 --- a/gio/tests/gdbus-example-export.c +++ b/gio/tests/gdbus-example-export.c @@ -233,9 +233,11 @@ send_property_change (GObject *obj, GDBusConnection *connection) { GVariantBuilder *builder; + GVariantBuilder *invalidated_builder; MyObject *myobj = (MyObject *)obj; builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); if (g_strcmp0 (pspec->name, "count") == 0) g_variant_builder_add (builder, @@ -251,9 +253,10 @@ send_property_change (GObject *obj, "/org/myorg/MyObject", "org.freedesktop.DBus.Properties", "PropertiesChanged", - g_variant_new ("(sa{sv})", + g_variant_new ("(sa{sv}as)", "org.myorg.MyObject", - builder), + builder, + invalidated_builder), NULL); } diff --git a/gio/tests/gdbus-example-proxy-subclass.c b/gio/tests/gdbus-example-proxy-subclass.c index 110cbdb0f..19e313ce5 100644 --- a/gio/tests/gdbus-example-proxy-subclass.c +++ b/gio/tests/gdbus-example-proxy-subclass.c @@ -202,27 +202,31 @@ accounts_user_g_signal (GDBusProxy *proxy, } static void -accounts_user_g_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties) +accounts_user_g_properties_changed (GDBusProxy *proxy, + GVariant *changed_properties, + const gchar* const *invalidated_properties) { AccountsUser *user = ACCOUNTS_USER (proxy); GVariantIter *iter; GVariant *item; - g_variant_get (changed_properties, "a{sv}", &iter); - while ((item = g_variant_iter_next_value (iter)) != NULL) + if (changed_properties != NULL) { - const gchar *key; - g_variant_get (item, - "{sv}", - &key, - NULL); - if (g_strcmp0 (key, "AutomaticLogin") == 0) - g_object_notify (G_OBJECT (user), "automatic-login"); - else if (g_strcmp0 (key, "RealName") == 0) - g_object_notify (G_OBJECT (user), "real-name"); - else if (g_strcmp0 (key, "UserName") == 0) - g_object_notify (G_OBJECT (user), "user-name"); + g_variant_get (changed_properties, "a{sv}", &iter); + while ((item = g_variant_iter_next_value (iter)) != NULL) + { + const gchar *key; + g_variant_get (item, + "{sv}", + &key, + NULL); + if (g_strcmp0 (key, "AutomaticLogin") == 0) + g_object_notify (G_OBJECT (user), "automatic-login"); + else if (g_strcmp0 (key, "RealName") == 0) + g_object_notify (G_OBJECT (user), "real-name"); + else if (g_strcmp0 (key, "UserName") == 0) + g_object_notify (G_OBJECT (user), "user-name"); + } } } diff --git a/gio/tests/gdbus-example-server.c b/gio/tests/gdbus-example-server.c index a943f6555..c4574cd99 100644 --- a/gio/tests/gdbus-example-server.c +++ b/gio/tests/gdbus-example-server.c @@ -243,9 +243,10 @@ handle_set_property (GDBusConnection *connection, object_path, "org.freedesktop.DBus.Properties", "PropertiesChanged", - g_variant_new ("(sa{sv})", + g_variant_new ("(sa{sv}as)", interface_name, - builder), + builder, + NULL), &local_error); g_assert_no_error (local_error); } @@ -283,12 +284,14 @@ on_timeout_cb (gpointer user_data) { GDBusConnection *connection = G_DBUS_CONNECTION (user_data); GVariantBuilder *builder; + GVariantBuilder *invalidated_builder; GError *error; swap_a_and_b = !swap_a_and_b; error = NULL; builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); g_variant_builder_add (builder, "{sv}", "Foo", @@ -302,9 +305,10 @@ on_timeout_cb (gpointer user_data) "/org/gtk/GDBus/TestObject", "org.freedesktop.DBus.Properties", "PropertiesChanged", - g_variant_new ("(sa{sv})", + g_variant_new ("(sa{sv}as)", "org.gtk.GDBus.TestInterface", - builder), + builder, + invalidated_builder), &error); g_assert_no_error (error); diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index 00768bc3e..f4796eea8 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -42,32 +42,45 @@ print_properties (GDBusProxy *proxy) } static void -on_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties, - gpointer user_data) +on_properties_changed (GDBusProxy *proxy, + GVariant *changed_properties, + const gchar* const *invalidated_properties, + gpointer user_data) { - GVariantIter *iter; - GVariant *item; - g_print (" *** Properties Changed:\n"); - - g_variant_get (changed_properties, - "a{sv}", - &iter); - while ((item = g_variant_iter_next_value (iter))) + if (changed_properties != NULL) { - const gchar *key; - GVariant *value; - gchar *value_str; + GVariantIter *iter; + GVariant *item; - g_variant_get (item, - "{sv}", - &key, - &value); + g_print (" *** Properties Changed:\n"); + g_variant_get (changed_properties, + "a{sv}", + &iter); + while ((item = g_variant_iter_next_value (iter))) + { + const gchar *key; + GVariant *value; + gchar *value_str; + g_variant_get (item, + "{sv}", + &key, + &value); + value_str = g_variant_print (value, TRUE); + g_print (" %s -> %s\n", key, value_str); + g_free (value_str); + } + } - value_str = g_variant_print (value, TRUE); - g_print (" %s -> %s\n", key, value_str); - g_free (value_str); + if (invalidated_properties != NULL) + { + guint n; + g_print (" *** Properties Invalidated:\n"); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + const gchar *key = invalidated_properties[n]; + g_print (" %s\n", key); + } } } diff --git a/gio/tests/gdbus-testserver.py b/gio/tests/gdbus-testserver.py index 04d654e71..d31a49343 100755 --- a/gio/tests/gdbus-testserver.py +++ b/gio/tests/gdbus-testserver.py @@ -187,6 +187,7 @@ class TestService(dbus.service.Object): "PropertiesChanged") message.append("com.example.Frob") message.append({prop_name : prop_value}) + message.append([], signature="as") session_bus.send_message(message) # ---------------------------------------------------------------------------------------------------- From 107b4d4bae7dba4281bfaa0bef827f7b2376946a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 13:09:58 -0400 Subject: [PATCH 60/76] remove the redundant interface_name parameter --- gio/gdbusconnection.c | 12 +++++------- gio/gdbusconnection.h | 1 - gio/tests/gdbus-example-export.c | 1 - gio/tests/gdbus-example-peer.c | 1 - gio/tests/gdbus-example-server.c | 1 - gio/tests/gdbus-export.c | 12 ------------ gio/tests/gdbus-peer.c | 2 -- 7 files changed, 5 insertions(+), 25 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index dd49189be..6967ac096 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3894,7 +3894,6 @@ obj_message_func (GDBusConnection *connection, * g_dbus_connection_register_object: * @connection: A #GDBusConnection. * @object_path: The object path to register at. - * @interface_name: The D-Bus interface to register. * @introspection_data: Introspection data for the interface. * @vtable: A #GDBusInterfaceVTable to call into or %NULL. * @user_data: Data to pass to functions in @vtable. @@ -3902,7 +3901,7 @@ obj_message_func (GDBusConnection *connection, * @error: Return location for error or %NULL. * * Registers callbacks for exported objects at @object_path with the - * D-Bus interface @interface_name. + * D-Bus interface that is described in @introspection_data. * * Calls to functions in @vtable (and @user_data_free_func) will * happen in the thread-default main @@ -3943,7 +3942,6 @@ obj_message_func (GDBusConnection *connection, guint g_dbus_connection_register_object (GDBusConnection *connection, const gchar *object_path, - const gchar *interface_name, const GDBusInterfaceInfo *introspection_data, const GDBusInterfaceVTable *vtable, gpointer user_data, @@ -3957,8 +3955,8 @@ g_dbus_connection_register_object (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); - g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), 0); g_return_val_if_fail (introspection_data != NULL, 0); + g_return_val_if_fail (g_dbus_is_interface_name (introspection_data->name), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); ret = 0; @@ -3978,14 +3976,14 @@ g_dbus_connection_register_object (GDBusConnection *connection, g_hash_table_insert (connection->priv->map_object_path_to_eo, eo->object_path, eo); } - ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); + ei = g_hash_table_lookup (eo->map_if_name_to_ei, introspection_data->name); if (ei != NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, _("An object is already exported for the interface %s at %s"), - interface_name, + introspection_data->name, object_path); goto out; } @@ -3997,7 +3995,7 @@ g_dbus_connection_register_object (GDBusConnection *connection, ei->user_data_free_func = user_data_free_func; ei->vtable = vtable; ei->introspection_data = introspection_data; - ei->interface_name = g_strdup (interface_name); + ei->interface_name = g_strdup (introspection_data->name); ei->context = g_main_context_get_thread_default (); if (ei->context != NULL) g_main_context_ref (ei->context); diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index bb5475d73..329977759 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -308,7 +308,6 @@ struct _GDBusInterfaceVTable guint g_dbus_connection_register_object (GDBusConnection *connection, const gchar *object_path, - const gchar *interface_name, const GDBusInterfaceInfo *introspection_data, const GDBusInterfaceVTable *vtable, gpointer user_data, diff --git a/gio/tests/gdbus-example-export.c b/gio/tests/gdbus-example-export.c index 9dee12f70..5dda58da0 100644 --- a/gio/tests/gdbus-example-export.c +++ b/gio/tests/gdbus-example-export.c @@ -272,7 +272,6 @@ on_bus_acquired (GDBusConnection *connection, G_CALLBACK (send_property_change), connection); registration_id = g_dbus_connection_register_object (connection, "/org/myorg/MyObject", - "org.myorg.MyObject", introspection_data->interfaces[0], &interface_vtable, myobj, diff --git a/gio/tests/gdbus-example-peer.c b/gio/tests/gdbus-example-peer.c index 118624f34..1a11c6da2 100644 --- a/gio/tests/gdbus-example-peer.c +++ b/gio/tests/gdbus-example-peer.c @@ -146,7 +146,6 @@ on_new_connection (GDBusServer *server, g_object_ref (connection); registration_id = g_dbus_connection_register_object (connection, "/org/gtk/GDBus/TestObject", - "org.gtk.GDBus.TestPeerInterface", introspection_data->interfaces[0], &interface_vtable, NULL, /* user_data */ diff --git a/gio/tests/gdbus-example-server.c b/gio/tests/gdbus-example-server.c index c4574cd99..93dd53a7b 100644 --- a/gio/tests/gdbus-example-server.c +++ b/gio/tests/gdbus-example-server.c @@ -327,7 +327,6 @@ on_bus_acquired (GDBusConnection *connection, registration_id = g_dbus_connection_register_object (connection, "/org/gtk/GDBus/TestObject", - "org.gtk.GDBus.TestInterface", introspection_data->interfaces[0], &interface_vtable, NULL, /* user_data */ diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c index 77d7303c4..ff6cc28f2 100644 --- a/gio/tests/gdbus-export.c +++ b/gio/tests/gdbus-export.c @@ -983,7 +983,6 @@ test_object_registration (void) registration_id = g_dbus_connection_register_object (c, "/foo/boss", - foo_interface_info.name, &foo_interface_info, &foo_vtable, &data, @@ -996,7 +995,6 @@ test_object_registration (void) registration_id = g_dbus_connection_register_object (c, "/foo/boss", - bar_interface_info.name, &bar_interface_info, NULL, &data, @@ -1009,7 +1007,6 @@ test_object_registration (void) registration_id = g_dbus_connection_register_object (c, "/foo/boss/worker1", - foo_interface_info.name, &foo_interface_info, NULL, &data, @@ -1022,7 +1019,6 @@ test_object_registration (void) registration_id = g_dbus_connection_register_object (c, "/foo/boss/worker2", - bar_interface_info.name, &bar_interface_info, NULL, &data, @@ -1035,7 +1031,6 @@ test_object_registration (void) registration_id = g_dbus_connection_register_object (c, "/foo/boss/interns/intern1", - foo_interface_info.name, &foo_interface_info, NULL, &data, @@ -1049,7 +1044,6 @@ test_object_registration (void) /* ... and try again at another path */ registration_id = g_dbus_connection_register_object (c, "/foo/boss/interns/intern2", - bar_interface_info.name, &bar_interface_info, NULL, &data, @@ -1063,7 +1057,6 @@ test_object_registration (void) /* register at the same path/interface - this should fail */ registration_id = g_dbus_connection_register_object (c, "/foo/boss/interns/intern2", - bar_interface_info.name, &bar_interface_info, NULL, &data, @@ -1078,7 +1071,6 @@ test_object_registration (void) /* register at different interface - shouldn't fail */ registration_id = g_dbus_connection_register_object (c, "/foo/boss/interns/intern2", - foo_interface_info.name, &foo_interface_info, NULL, &data, @@ -1097,7 +1089,6 @@ test_object_registration (void) /* register it back */ registration_id = g_dbus_connection_register_object (c, "/foo/boss/interns/intern2", - foo_interface_info.name, &foo_interface_info, NULL, &data, @@ -1110,7 +1101,6 @@ test_object_registration (void) registration_id = g_dbus_connection_register_object (c, "/foo/boss/interns/intern3", - bar_interface_info.name, &bar_interface_info, NULL, &data, @@ -1167,7 +1157,6 @@ test_object_registration (void) */ registration_id = g_dbus_connection_register_object (c, "/foo/boss/executives/non_subtree_object", - bar_interface_info.name, &bar_interface_info, NULL, &data, @@ -1179,7 +1168,6 @@ test_object_registration (void) num_successful_registrations++; registration_id = g_dbus_connection_register_object (c, "/foo/boss/executives/non_subtree_object", - foo_interface_info.name, &foo_interface_info, NULL, &data, diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 8571e73f5..b0e49771e 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -243,7 +243,6 @@ on_new_connection (GDBusServer *server, error = NULL; reg_id = g_dbus_connection_register_object (connection, "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", test_interface_introspection_data, &test_interface_vtable, data, @@ -331,7 +330,6 @@ on_incoming_connection (GSocketService *service, error = NULL; reg_id = g_dbus_connection_register_object (connection, "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", &test_interface_introspection_data, &test_interface_vtable, data, From 33952347ff9bc2875e7e1a2709566b38fc391bda Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 14:01:41 -0400 Subject: [PATCH 61/76] GDBus: Make message serialization routines take capabilities param This is needed to e.g. allow encoding maybe types (once we add G_DBUS_CAPABILITY_FLAGS_MAYBE_TYPES) if, and only if, that capability has been negotiated with the peer (via authentication). --- gio/gdbusconnection.c | 1 + gio/gdbusmessage.c | 26 +++++++++++++++++--------- gio/gdbusmessage.h | 2 ++ gio/gdbusprivate.c | 1 + gio/tests/gdbus-serialization.c | 6 +++++- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 6967ac096..80ae8b6fd 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -1009,6 +1009,7 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, blob = g_dbus_message_to_blob (message, &blob_size, + connection->priv->capabilities, error); if (blob == NULL) goto out; diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index d2f02453d..9f871de97 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -1070,9 +1070,9 @@ parse_value_from_blob (GMemoryInputStream *mis, * Since: 2.26 */ gssize -g_dbus_message_bytes_needed (guchar *blob, - gsize blob_len, - GError **error) +g_dbus_message_bytes_needed (guchar *blob, + gsize blob_len, + GError **error) { gssize ret; @@ -1122,6 +1122,7 @@ g_dbus_message_bytes_needed (guchar *blob, * g_dbus_message_new_from_blob: * @blob: A blob represent a binary D-Bus message. * @blob_len: The length of @blob. + * @capabilities: A #GDBusCapabilityFlags describing what protocol features are supported. * @error: Return location for error or %NULL. * * Creates a new #GDBusMessage from the data stored at @blob. @@ -1132,9 +1133,10 @@ g_dbus_message_bytes_needed (guchar *blob, * Since: 2.26 */ GDBusMessage * -g_dbus_message_new_from_blob (guchar *blob, - gsize blob_len, - GError **error) +g_dbus_message_new_from_blob (guchar *blob, + gsize blob_len, + GDBusCapabilityFlags capabilities, + GError **error) { gboolean ret; GMemoryInputStream *mis; @@ -1149,6 +1151,8 @@ g_dbus_message_new_from_blob (guchar *blob, GVariantIter iter; GVariant *signature; + /* TODO: check against @capabilities */ + ret = FALSE; g_return_val_if_fail (blob != NULL, NULL); @@ -1565,6 +1569,7 @@ append_body_to_blob (GVariant *value, * g_dbus_message_to_blob: * @message: A #GDBusMessage. * @out_size: Return location for size of generated blob. + * @capabilities: A #GDBusCapabilityFlags describing what protocol features are supported. * @error: Return location for error. * * Serializes @message to a blob. @@ -1575,9 +1580,10 @@ append_body_to_blob (GVariant *value, * Since: 2.26 */ guchar * -g_dbus_message_to_blob (GDBusMessage *message, - gsize *out_size, - GError **error) +g_dbus_message_to_blob (GDBusMessage *message, + gsize *out_size, + GDBusCapabilityFlags capabilities, + GError **error) { GMemoryOutputStream *mos; GDataOutputStream *dos; @@ -1597,6 +1603,8 @@ g_dbus_message_to_blob (GDBusMessage *message, gint num_fds_in_message; gint num_fds_according_to_header; + /* TODO: check against @capabilities */ + ret = NULL; g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h index c30abcfe3..a01fb8b8a 100644 --- a/gio/gdbusmessage.h +++ b/gio/gdbusmessage.h @@ -158,6 +158,7 @@ const gchar *g_dbus_message_get_arg0 (GDBusMessage GDBusMessage *g_dbus_message_new_from_blob (guchar *blob, gsize blob_len, + GDBusCapabilityFlags capabilities, GError **error); gssize g_dbus_message_bytes_needed (guchar *blob, @@ -166,6 +167,7 @@ gssize g_dbus_message_bytes_needed (guchar guchar *g_dbus_message_to_blob (GDBusMessage *message, gsize *out_size, + GDBusCapabilityFlags capabilities, GError **error); gboolean g_dbus_message_to_gerror (GDBusMessage *message, diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 4cdc480e2..da06fe56c 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -583,6 +583,7 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream, message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer, worker->read_buffer_cur_size, + worker->capabilities, &error); if (message == NULL) { diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 9a96f50bc..90e380fec 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -522,6 +522,7 @@ check_serialization (GVariant *value, error = NULL; blob = g_dbus_message_to_blob (message, &blob_size, + G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_no_error (error); g_assert (blob != NULL); @@ -555,7 +556,10 @@ check_serialization (GVariant *value, /* Then serialize back and check that the body is identical */ error = NULL; - recovered_message = g_dbus_message_new_from_blob (blob, blob_size, &error); + recovered_message = g_dbus_message_new_from_blob (blob, + blob_size, + G_DBUS_CAPABILITY_FLAGS_NONE, + &error); g_assert_no_error (error); g_assert (recovered_message != NULL); g_assert (g_dbus_message_get_body (recovered_message) != NULL); From 9e90b381f58c4a06f49e622a07ee0b56fb52b3f1 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 14:25:29 -0400 Subject: [PATCH 62/76] Remove the credentials argument from g_unix_connect_send_credentials() Instead, make it always send the current credentials. --- gio/gdbusauth.c | 3 +-- gio/gunixconnection.c | 16 +++++++--------- gio/gunixconnection.h | 1 - 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index 3f7ccce4e..f774eeec7 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -612,7 +612,6 @@ _g_dbus_auth_run_client (GDBusAuth *auth, { credentials = g_credentials_new (); if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream), - credentials, cancellable, error)) goto out; @@ -641,7 +640,7 @@ _g_dbus_auth_run_client (GDBusAuth *auth, debug_print ("CLIENT: didn't send any credentials"); } - /* TODO: to reduce rountrips, try to pick an auth mechanism to start with */ + /* TODO: to reduce roundtrips, try to pick an auth mechanism to start with */ /* Get list of supported authentication mechanisms */ s = "AUTH\r\n"; diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c index 8b618cb0a..1d48ff594 100644 --- a/gio/gunixconnection.c +++ b/gio/gunixconnection.c @@ -299,34 +299,30 @@ gboolean g_unix_connection_create_pair (GUnixCo /** * g_unix_connection_send_credentials: * @connection: A #GUnixConnection. - * @credentials: A #GCredentials to send. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * - * Passes the credentials stored in @credentials to the recieving side + * Passes the credentials of the current user the receiving side * of the connection. The recieving end has to call * g_unix_connection_receive_credentials() (or similar) to accept the * credentials. * - * The credentials which the sender specifies are checked by the - * kernel. A process with effective user ID 0 is allowed to specify - * values that do not match its own. This means that the credentials - * can be used to authenticate other connections. - * * As well as sending the credentials this also writes a single NUL * byte to the stream, as this is required for credentials passing to * work on some implementations. * + * Note that this function only works on Linux, currently. + * * Returns: %TRUE on success, %FALSE if @error is set. * * Since: 2.26 */ gboolean g_unix_connection_send_credentials (GUnixConnection *connection, - GCredentials *credentials, GCancellable *cancellable, GError **error) { + GCredentials *credentials; GSocketControlMessage *scm; GSocket *socket; gboolean ret; @@ -334,11 +330,12 @@ g_unix_connection_send_credentials (GUnixConnection *connection, guchar nul_byte[1] = {'\0'}; g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); - g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; + credentials = g_credentials_new (); + vector.buffer = &nul_byte; vector.size = 1; scm = g_unix_credentials_message_new_with_credentials (credentials); @@ -362,6 +359,7 @@ g_unix_connection_send_credentials (GUnixConnection *connection, out: g_object_unref (socket); g_object_unref (scm); + g_object_unref (credentials); return ret; } diff --git a/gio/gunixconnection.h b/gio/gunixconnection.h index 9c691ea0d..c38b0c9c1 100644 --- a/gio/gunixconnection.h +++ b/gio/gunixconnection.h @@ -72,7 +72,6 @@ gint g_unix_connection_receive_fd (GUnixCo GError **error); gboolean g_unix_connection_send_credentials (GUnixConnection *connection, - GCredentials *credentials, GCancellable *cancellable, GError **error); From cb753dfd496ae70b069c1698da8211c454953f08 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 16:20:31 -0400 Subject: [PATCH 63/76] GDBus: Rename ::deny-authentication-peer to ::authorize-authenticated-peer --- docs/reference/gio/gio-sections.txt | 2 +- gio/gdbusauth.c | 8 ++-- gio/gdbusauthobserver.c | 74 ++++++++++++++++++----------- gio/gdbusauthobserver.h | 18 +++---- gio/gdbusconnection.c | 2 - gio/gio.symbols | 2 +- gio/tests/gdbus-peer.c | 20 ++++---- 7 files changed, 70 insertions(+), 56 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 3047f8ede..cc0a890d2 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2240,7 +2240,7 @@ g_dbus_is_interface_name GDBusAuthObserver GDBusAuthObserverClass g_dbus_auth_observer_new -g_dbus_auth_observer_deny_authenticated_peer +g_dbus_auth_observer_authorize_authenticated_peer G_DBUS_AUTH_OBSERVER G_IS_DBUS_AUTH_OBSERVER diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c index f774eeec7..a94fcfa14 100644 --- a/gio/gdbusauth.c +++ b/gio/gdbusauth.c @@ -1132,15 +1132,15 @@ _g_dbus_auth_run_server (GDBusAuth *auth, { case G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED: if (observer != NULL && - g_dbus_auth_observer_deny_authenticated_peer (observer, - auth->priv->stream, - credentials)) + !g_dbus_auth_observer_authorize_authenticated_peer (observer, + auth->priv->stream, + credentials)) { /* disconnect */ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Cancelled via GDBusAuthObserver::deny-authenticated-peer")); + _("Cancelled via GDBusAuthObserver::authorize-authenticated-peer")); goto out; } else diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c index 948659b18..d7fb73aad 100644 --- a/gio/gdbusauthobserver.c +++ b/gio/gdbusauthobserver.c @@ -46,24 +46,24 @@ * processes owned by the same uid as the server, you would do this: * Controlling Authentication * static gboolean - * on_deny_authenticated_peer (GDBusAuthObserver *observer, - * GIOStream *stream, - * GCredentials *credentials, - * gpointer user_data) + * on_authorize_authenticated_peer (GDBusAuthObserver *observer, + * GIOStream *stream, + * GCredentials *credentials, + * gpointer user_data) * { * GCredentials *me; - * gboolean deny; + * gboolean authorized; * - * deny = TRUE; + * authorized = FALSE; * me = g_credentials_new (); * * if (credentials != NULL && * !g_credentials_is_same_user (credentials, me)) - * deny = FALSE; + * authorized = TRUE; * * g_object_unref (me); * - * return deny; + * return authorized; * } * * static gboolean @@ -88,8 +88,8 @@ * NULL, /* GCancellable */ * &error); * g_signal_connect (observer, - * "deny-authenticated-peer", - * G_CALLBACK (on_deny_authenticated_peer), + * "authorize-authenticated-peer", + * G_CALLBACK (on_authorize_authenticated_peer), * NULL); * g_signal_connect (server, * "new-connection", @@ -107,7 +107,7 @@ struct _GDBusAuthObserverPrivate enum { - DENY_AUTHENTICATED_PEER_SIGNAL, + AUTHORIZE_AUTHENTICATED_PEER_SIGNAL, LAST_SIGNAL, }; @@ -124,11 +124,27 @@ g_dbus_auth_observer_finalize (GObject *object) } static gboolean -g_dbus_auth_observer_deny_authenticated_peer_real (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials) +g_dbus_auth_observer_authorize_authenticated_peer_real (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials) { - return FALSE; + 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 @@ -138,27 +154,27 @@ g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass) gobject_class->finalize = g_dbus_auth_observer_finalize; - klass->deny_authenticated_peer = g_dbus_auth_observer_deny_authenticated_peer_real; + klass->authorize_authenticated_peer = g_dbus_auth_observer_authorize_authenticated_peer_real; /** - * GDBusAuthObserver::deny-authenticated-peer: + * GDBusAuthObserver::authorize-authenticated-peer: * @observer: The #GDBusAuthObserver emitting the signal. * @stream: A #GIOStream for the #GDBusConnection. * @credentials: Credentials received from the peer or %NULL. * * Emitted to check if a peer that is successfully authenticated - * should be denied. + * is authorized. * - * Returns: %TRUE if the peer should be denied, %FALSE otherwise. + * Returns: %TRUE if the peer is authorized, %FALSE if not. * * Since: 2.26 */ - signals[DENY_AUTHENTICATED_PEER_SIGNAL] = - g_signal_new ("deny-authenticated-peer", + signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL] = + g_signal_new ("authorize-authenticated-peer", G_TYPE_DBUS_AUTH_OBSERVER, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GDBusAuthObserverClass, deny_authenticated_peer), - g_signal_accumulator_true_handled, + G_STRUCT_OFFSET (GDBusAuthObserverClass, authorize_authenticated_peer), + _g_signal_accumulator_false_handled, NULL, /* accu_data */ _gio_marshal_BOOLEAN__OBJECT_OBJECT, G_TYPE_BOOLEAN, @@ -197,27 +213,27 @@ g_dbus_auth_observer_new (void) /* ---------------------------------------------------------------------------------------------------- */ /** - * g_dbus_auth_observer_deny_authenticated_peer: + * g_dbus_auth_observer_authorize_authenticated_peer: * @observer: A #GDBusAuthObserver. * @stream: A #GIOStream for the #GDBusConnection. * @credentials: Credentials received from the peer or %NULL. * - * Emits the #GDBusAuthObserver::deny-authenticated-peer signal on @observer. + * Emits the #GDBusAuthObserver::authorize-authenticated-peer signal on @observer. * * Returns: %TRUE if the peer should be denied, %FALSE otherwise. * * Since: 2.26 */ gboolean -g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials) +g_dbus_auth_observer_authorize_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials) { gboolean denied; denied = FALSE; g_signal_emit (observer, - signals[DENY_AUTHENTICATED_PEER_SIGNAL], + signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL], 0, stream, credentials, diff --git a/gio/gdbusauthobserver.h b/gio/gdbusauthobserver.h index ac3e234f4..b40836508 100644 --- a/gio/gdbusauthobserver.h +++ b/gio/gdbusauthobserver.h @@ -40,7 +40,7 @@ typedef struct _GDBusAuthObserverPrivate GDBusAuthObserverPrivate; /** * GDBusAuthObserverClass: - * @deny_authenticated_peer: Signal class handler for the #GDBusAuthObserver::deny-authenticated-peer signal. + * @authorize_authenticated_peer: Signal class handler for the #GDBusAuthObserver::authorize-authenticated-peer signal. * * Class structure for #GDBusAuthObserverClass. * @@ -54,9 +54,9 @@ struct _GDBusAuthObserverClass /*< public >*/ /* Signals */ - gboolean (*deny_authenticated_peer) (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials); + gboolean (*authorize_authenticated_peer) (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials); /*< private >*/ @@ -93,11 +93,11 @@ struct _GDBusAuthObserver GDBusAuthObserverPrivate *priv; }; -GType g_dbus_auth_observer_get_type (void) G_GNUC_CONST; -GDBusAuthObserver *g_dbus_auth_observer_new (void); -gboolean g_dbus_auth_observer_deny_authenticated_peer (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials); +GType g_dbus_auth_observer_get_type (void) G_GNUC_CONST; +GDBusAuthObserver *g_dbus_auth_observer_new (void); +gboolean g_dbus_auth_observer_authorize_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials); G_END_DECLS diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 80ae8b6fd..ccb2729d2 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -100,8 +100,6 @@ * when the returned GVariant is floating. * * - Consistent timeout handling (25s vs 30s?) - * - * - Update GDBusAuthObserver (s/deny/authorize/) */ #include "config.h" diff --git a/gio/gio.symbols b/gio/gio.symbols index 0d7c106ad..da6eb93d2 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1475,7 +1475,7 @@ g_dbus_address_get_stream_sync #if IN_FILE(__G_DBUS_AUTH_OBSERVER_C__) g_dbus_auth_observer_get_type G_GNUC_CONST g_dbus_auth_observer_new -g_dbus_auth_observer_deny_authenticated_peer +g_dbus_auth_observer_authorize_authenticated_peer #endif #endif diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index b0e49771e..f78aa5c3f 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -203,24 +203,24 @@ on_proxy_signal_received (GDBusProxy *proxy, /* ---------------------------------------------------------------------------------------------------- */ static gboolean -on_deny_authenticated_peer (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials, - gpointer user_data) +on_authorize_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials, + gpointer user_data) { PeerData *data = user_data; - gboolean deny_peer; + gboolean authorized; data->num_connection_attempts++; - deny_peer = FALSE; + authorized = TRUE; if (!data->accept_connection) { - deny_peer = TRUE; + authorized = FALSE; g_main_loop_quit (loop); } - return deny_peer; + return authorized; } /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ @@ -280,8 +280,8 @@ service_thread_func (gpointer user_data) G_CALLBACK (on_new_connection), data); g_signal_connect (observer, - "deny-authenticated-peer", - G_CALLBACK (on_deny_authenticated_peer), + "authorize-authenticated-peer", + G_CALLBACK (on_authorize_authenticated_peer), data); g_object_unref (observer); From 60e7ae26af2fc31d59e36f8798d383fa9d87db92 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 16:32:11 -0400 Subject: [PATCH 64/76] GDBus: Add GDBusAuthObserver param in g_dbus_connection_new_for_address() This is to match g_dbus_connection_new(). This extension allows us to extend GDBusAuthObserver to also be used in client-side authentication in the future (right now it's only used on the server-side). --- gio/gdbus-tool.c | 1 + gio/gdbusconnection.c | 18 ++++++++++++++---- gio/gdbusconnection.h | 2 ++ gio/tests/gdbus-example-peer.c | 1 + gio/tests/gdbus-peer.c | 4 ++++ gio/tests/gdbus-tests.c | 1 + 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index 95d81287a..5780c261b 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -424,6 +424,7 @@ connection_get_dbus_connection (GError **error) { c = g_dbus_connection_new_for_address_sync (opt_connection_address, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ NULL, /* GCancellable */ error); } diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index ccb2729d2..b02f1d6c6 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -1948,8 +1948,7 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * Asynchronously sets up a D-Bus connection for exchanging D-Bus messages * with the end represented by @stream. * - * If %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER is set in @flags, - * @observer (if not %NULL) is used to assist in the client + * If @observer is not %NULL it may be used to control the * authentication process. * * When the operation is finished, @callback will be invoked. You can @@ -2029,8 +2028,7 @@ g_dbus_connection_new_finish (GAsyncResult *res, * Synchronously sets up a D-Bus connection for exchanging D-Bus messages * with the end represented by @stream. * - * If %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER is set in @flags, - * @observer (if not %NULL) is used to assist in the client + * If @observer is not %NULL it may be used to control the * authentication process. * * This is a synchronous failable constructor. See @@ -2066,6 +2064,7 @@ g_dbus_connection_new_sync (GIOStream *stream, * g_dbus_connection_new_for_address: * @address: A D-Bus address. * @flags: Flags describing how to make the connection. + * @observer: A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. @@ -2084,6 +2083,9 @@ g_dbus_connection_new_sync (GIOStream *stream, * then call g_dbus_connection_new_finish() to get the result of the * operation. * + * If @observer is not %NULL it may be used to control the + * authentication process. + * * This is a asynchronous failable constructor. See * g_dbus_connection_new_for_address_sync() for the synchronous * version. @@ -2093,6 +2095,7 @@ g_dbus_connection_new_sync (GIOStream *stream, void g_dbus_connection_new_for_address (const gchar *address, GDBusConnectionFlags flags, + GDBusAuthObserver *observer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -2105,6 +2108,7 @@ g_dbus_connection_new_for_address (const gchar *address, user_data, "address", address, "flags", flags, + "authentication-observer", observer, NULL); } @@ -2145,6 +2149,7 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, * g_dbus_connection_new_for_address_sync: * @address: A D-Bus address. * @flags: Flags describing how to make the connection. + * @observer: A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * @@ -2161,6 +2166,9 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, * This is a synchronous failable constructor. See * g_dbus_connection_new_for_address() for the asynchronous version. * + * If @observer is not %NULL it may be used to control the + * authentication process. + * * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). * * Since: 2.26 @@ -2168,6 +2176,7 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, GDBusConnection * g_dbus_connection_new_for_address_sync (const gchar *address, GDBusConnectionFlags flags, + GDBusAuthObserver *observer, GCancellable *cancellable, GError **error) { @@ -2178,6 +2187,7 @@ g_dbus_connection_new_for_address_sync (const gchar *address, error, "address", address, "flags", flags, + "authentication-observer", observer, NULL); } diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index 329977759..42ba47676 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -117,6 +117,7 @@ GDBusConnection *g_dbus_connection_new_sync (GIOStream void g_dbus_connection_new_for_address (const gchar *address, GDBusConnectionFlags flags, + GDBusAuthObserver *observer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); @@ -124,6 +125,7 @@ GDBusConnection *g_dbus_connection_new_for_address_finish (GAsyncResult GError **error); GDBusConnection *g_dbus_connection_new_for_address_sync (const gchar *address, GDBusConnectionFlags flags, + GDBusAuthObserver *observer, GCancellable *cancellable, GError **error); diff --git a/gio/tests/gdbus-example-peer.c b/gio/tests/gdbus-example-peer.c index 1a11c6da2..649b0476b 100644 --- a/gio/tests/gdbus-example-peer.c +++ b/gio/tests/gdbus-example-peer.c @@ -259,6 +259,7 @@ main (int argc, char *argv[]) error = NULL; connection = g_dbus_connection_new_for_address_sync (opt_address, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ NULL, /* GCancellable */ &error); if (connection == NULL) diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index f78aa5c3f..2444a1d1d 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -478,6 +478,7 @@ test_peer (void) * will fail because the nonce file doesn't exist */ "nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus", G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ NULL, /* cancellable */ &error); _g_assert_error_domain (error, G_IO_ERROR); @@ -500,6 +501,7 @@ test_peer (void) error = NULL; c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ NULL, /* cancellable */ &error); g_assert_no_error (error); @@ -624,6 +626,7 @@ test_peer (void) error = NULL; c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ NULL, /* cancellable */ &error); _g_assert_error_domain (error, G_IO_ERROR); @@ -639,6 +642,7 @@ test_peer (void) data.accept_connection = TRUE; c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ NULL, /* cancellable */ &error); g_assert_no_error (error); diff --git a/gio/tests/gdbus-tests.c b/gio/tests/gdbus-tests.c index d801e1d56..347ff3664 100644 --- a/gio/tests/gdbus-tests.c +++ b/gio/tests/gdbus-tests.c @@ -147,6 +147,7 @@ _g_bus_get_priv (GBusType bus_type, ret = g_dbus_connection_new_for_address_sync (address, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, /* GDBusAuthObserver */ cancellable, error); g_free (address); From 4ad4c306c3b80620185cf975b402e17a6174aea9 Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Thu, 13 May 2010 19:20:26 +0200 Subject: [PATCH 65/76] Plug some mem leaks g_variant_get (v, "s", &str) returns a string copy; use "&s" instead. Signed-off-by: David Zeuthen --- gio/gdbus-tool.c | 20 ++++++++++---------- gio/gdbusconnection.c | 22 ++++++++++------------ gio/gdbusmessage.c | 2 +- gio/gdbusnameowning.c | 4 ++-- gio/gdbusnamewatching.c | 4 ++-- gio/gdbusproxy.c | 4 ++-- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c index 5780c261b..5b5489354 100644 --- a/gio/gdbus-tool.c +++ b/gio/gdbus-tool.c @@ -167,7 +167,7 @@ print_methods (GDBusConnection *c, g_variant_unref (result); goto out; } - g_variant_get (result, "(s)", &xml_data); + g_variant_get (result, "(&s)", &xml_data); error = NULL; node = g_dbus_node_info_new_for_xml (xml_data, &error); @@ -229,7 +229,9 @@ print_paths (GDBusConnection *c, g_variant_unref (result); goto out; } - g_variant_get (result, "(s)", &xml_data); + g_variant_get (result, "(&s)", &xml_data); + + //g_printerr ("xml=`%s'", xml_data); error = NULL; node = g_dbus_node_info_new_for_xml (xml_data, &error); @@ -241,8 +243,6 @@ print_paths (GDBusConnection *c, goto out; } - //g_printerr ("xml=`%s'", xml_data); - //g_printerr ("bar `%s'\n", path); if (node->interfaces != NULL) @@ -308,7 +308,7 @@ print_names (GDBusConnection *c, } g_variant_get (result, "(as)", &iter); while (g_variant_iter_loop (iter, "s", &str)) - g_hash_table_insert (name_set, g_strdup (str), NULL); + g_hash_table_insert (name_set, str, NULL); g_variant_iter_free (iter); g_variant_unref (result); @@ -337,7 +337,7 @@ print_names (GDBusConnection *c, } g_variant_get (result, "(as)", &iter); while (g_variant_iter_loop (iter, "s", &str)) - g_hash_table_insert (name_set, g_strdup (str), NULL); + g_hash_table_insert (name_set, str, NULL); g_variant_iter_free (iter); g_variant_unref (result); @@ -476,7 +476,7 @@ call_helper_get_method_in_signature (GDBusConnection *c, goto out; } - g_variant_get (result, "(s)", &xml_data); + g_variant_get (result, "(&s)", &xml_data); node_info = g_dbus_node_info_new_for_xml (xml_data, error); if (node_info == NULL) goto out; @@ -1071,14 +1071,14 @@ dump_interface (GDBusConnection *c, &iter); while ((item = g_variant_iter_next_value (iter))) { - const gchar *key; + gchar *key; GVariant *value; g_variant_get (item, "{sv}", &key, &value); - g_hash_table_insert (properties, g_strdup (key), g_variant_ref (value)); + g_hash_table_insert (properties, key, g_variant_ref (value)); } } g_variant_unref (result); @@ -1354,7 +1354,7 @@ handle_introspect (gint *argc, g_variant_get_type_string (result)); goto out; } - g_variant_get (result, "(s)", &xml_data); + g_variant_get (result, "(&s)", &xml_data); error = NULL; node = g_dbus_node_info_new_for_xml (xml_data, &error); diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index b02f1d6c6..d9c161555 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -1839,7 +1839,6 @@ initable_init (GInitable *initable, if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { GVariant *hello_result; - const gchar *s; hello_result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", /* name */ @@ -1854,8 +1853,7 @@ initable_init (GInitable *initable, if (hello_result == NULL) goto out; - g_variant_get (hello_result, "(s)", &s); - connection->priv->bus_unique_name = g_strdup (s); + g_variant_get (hello_result, "(s)", &connection->priv->bus_unique_name); g_variant_unref (hello_result); //g_debug ("unique name is `%s'", connection->priv->bus_unique_name); } @@ -3217,12 +3215,12 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect if (is_get) g_variant_get (g_dbus_message_get_body (message), - "(ss)", + "(&s&s)", &interface_name, &property_name); else g_variant_get (g_dbus_message_get_body (message), - "(ssv)", + "(&s&sv)", &interface_name, &property_name, NULL); @@ -3321,12 +3319,12 @@ handle_getset_property (GDBusConnection *connection, if (is_get) g_variant_get (g_dbus_message_get_body (message), - "(ss)", + "(&s&s)", &interface_name, &property_name); else g_variant_get (g_dbus_message_get_body (message), - "(ssv)", + "(&s&sv)", &interface_name, &property_name, NULL); @@ -3456,7 +3454,7 @@ validate_and_maybe_schedule_property_get_all (GDBusConnection *connec handled = FALSE; g_variant_get (g_dbus_message_get_body (message), - "(s)", + "(&s)", &interface_name); if (vtable == NULL || vtable->get_property == NULL) @@ -3498,7 +3496,7 @@ handle_get_all_properties (GDBusConnection *connection, handled = FALSE; g_variant_get (g_dbus_message_get_body (message), - "(s)", + "(&s)", &interface_name); /* Fail with org.freedesktop.DBus.Error.InvalidArgs if there is @@ -4651,11 +4649,11 @@ handle_subtree_method_invocation (GDBusConnection *connection, else if (is_property_get || is_property_set || is_property_get_all) { if (is_property_get) - g_variant_get (g_dbus_message_get_body (message), "(ss)", &interface_name, NULL); + g_variant_get (g_dbus_message_get_body (message), "(&s&s)", &interface_name, NULL); else if (is_property_set) - g_variant_get (g_dbus_message_get_body (message), "(ssv)", &interface_name, NULL, NULL); + g_variant_get (g_dbus_message_get_body (message), "(&s&sv)", &interface_name, NULL, NULL); else if (is_property_get_all) - g_variant_get (g_dbus_message_get_body (message), "(s)", &interface_name, NULL, NULL); + g_variant_get (g_dbus_message_get_body (message), "(&s)", &interface_name, NULL, NULL); else g_assert_not_reached (); diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 9f871de97..1fc3c090e 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -2247,7 +2247,7 @@ g_dbus_message_to_gerror (GDBusMessage *message, if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)"))) { const gchar *error_message; - g_variant_get (body, "(s)", &error_message); + g_variant_get (body, "(&s)", &error_message); g_dbus_error_set_dbus_error (error, error_name, error_message, diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index 90c0e8479..ead9a5f15 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -261,7 +261,7 @@ on_name_lost_or_acquired (GDBusConnection *connection, if (g_strcmp0 (signal_name, "NameLost") == 0) { - g_variant_get (parameters, "(s)", &name); + g_variant_get (parameters, "(&s)", &name); if (g_strcmp0 (name, client->name) == 0) { call_lost_handler (client); @@ -269,7 +269,7 @@ on_name_lost_or_acquired (GDBusConnection *connection, } else if (g_strcmp0 (signal_name, "NameAcquired") == 0) { - g_variant_get (parameters, "(s)", &name); + g_variant_get (parameters, "(&s)", &name); if (g_strcmp0 (name, client->name) == 0) { call_acquired_handler (client); diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index 903e44413..f93a2ced7 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -289,7 +289,7 @@ on_name_owner_changed (GDBusConnection *connection, goto out; g_variant_get (parameters, - "(sss)", + "(&s&s&s)", &name, &old_owner, &new_owner); @@ -336,7 +336,7 @@ get_name_owner_cb (GObject *source_object, NULL); if (result != NULL) { - g_variant_get (result, "(s)", &name_owner); + g_variant_get (result, "(&s)", &name_owner); } if (name_owner != NULL) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 17619a148..b068710d8 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -848,7 +848,7 @@ process_get_all_reply (GDBusProxy *proxy, g_variant_iter_init (&iter, g_variant_get_child_value (result, 0)); while ((item = g_variant_iter_next_value (&iter)) != NULL) { - const gchar *key; + gchar *key; GVariant *value; g_variant_get (item, @@ -858,7 +858,7 @@ process_get_all_reply (GDBusProxy *proxy, //g_print ("got %s -> %s\n", key, g_variant_markup_print (value, FALSE, 0, 0)); g_hash_table_insert (proxy->priv->properties, - g_strdup (key), + key, value); /* steals value */ } out: From e3f5d3c00595ca017d83015fa2b9832d1c87828f Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Thu, 13 May 2010 19:01:04 +0200 Subject: [PATCH 66/76] Make GVariant handling in PropertiesChanged more efficient There's no need to re-build the a{sv} array, just get it right out of the parameters. Also avoid some string copies. Signed-off-by: David Zeuthen --- gio/gdbusproxy.c | 82 ++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 66 deletions(-) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index b068710d8..bf0e2d197 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -411,7 +411,7 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) G_TYPE_NONE, 2, G_TYPE_VARIANT, - G_TYPE_STRV); + G_TYPE_STRV | G_SIGNAL_TYPE_STATIC_SCOPE); /** * GDBusProxy::g-signal: @@ -676,18 +676,13 @@ on_properties_changed (GDBusConnection *connection, GDBusProxy *proxy = G_DBUS_PROXY (user_data); GError *error; const gchar *interface_name_for_signal; - GVariantIter *iter; - GVariantIter *invalidated_iter; - GVariant *item; GVariant *changed_properties; - GVariantBuilder *builder; - GPtrArray *p; - const gchar *str; gchar **invalidated_properties; + GVariantIter iter; + gchar *key; + GVariant *value; error = NULL; - iter = NULL; - invalidated_iter = NULL; #if 0 // TODO! /* Ignore this signal if properties are not yet available @@ -696,70 +691,32 @@ on_properties_changed (GDBusConnection *connection, * org.freedesktop.DBus.Properties.GetAll() returns) */ if (!proxy->priv->properties_available) - goto out; + return; #endif - if (strcmp (g_variant_get_type_string (parameters), "(sa{sv}as)") != 0) + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) { g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv}as)'", g_variant_get_type_string (parameters)); - goto out; + return; } g_variant_get (parameters, - "(sa{sv}as)", + "(&s@a{sv}^a&s)", &interface_name_for_signal, - &iter, - &invalidated_iter); + &changed_properties, + &invalidated_properties); if (g_strcmp0 (interface_name_for_signal, proxy->priv->interface_name) != 0) goto out; - builder = NULL; - while ((item = g_variant_iter_next_value (iter))) + g_variant_iter_init (&iter, changed_properties); + while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { - const gchar *key; - GVariant *value; - - if (builder == NULL) - builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); - - g_variant_get (item, - "{sv}", - &key, - &value); - g_hash_table_insert (proxy->priv->properties, - g_strdup (key), - value); /* steals value */ - - g_variant_builder_add (builder, - "{sv}", - g_strdup (key), - g_variant_ref (value)); + key, /* adopts string */ + value); /* adopts value */ } - if (builder != NULL) - changed_properties = g_variant_builder_end (builder); - else - changed_properties = NULL; - - p = NULL; - while (g_variant_iter_loop (invalidated_iter, "s", &str)) - { - if (p == NULL) - p = g_ptr_array_new (); - g_ptr_array_add (p, (gpointer) str); - } - if (p != NULL) - { - g_ptr_array_add (p, NULL); - invalidated_properties = (gchar **) g_ptr_array_free (p, FALSE); - } - else - { - invalidated_properties = NULL; - } - /* emit signal */ g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL], @@ -767,16 +724,9 @@ on_properties_changed (GDBusConnection *connection, changed_properties, invalidated_properties); - if (changed_properties != NULL) - g_variant_unref (changed_properties); - if (invalidated_properties != NULL) - g_free (invalidated_properties); - out: - if (iter != NULL) - g_variant_iter_free (iter); - if (invalidated_iter != NULL) - g_variant_iter_free (invalidated_iter); + g_variant_unref (changed_properties); + g_free (invalidated_properties); } /* ---------------------------------------------------------------------------------------------------- */ From 3ca28ef718d402bd65cd9f291c67b299f1ef74cf Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 16:57:29 -0400 Subject: [PATCH 67/76] GDBus: Update docs for GDBusProxy::g-properties-changed signal Also update the example. See https://bugzilla.gnome.org/show_bug.cgi?id=618559 for more details. Signed-off-by: David Zeuthen --- gio/gdbusproxy.c | 10 +++++----- gio/tests/gdbus-example-watch-proxy.c | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index bf0e2d197..07b7f8e31 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -387,13 +387,13 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) /** * GDBusProxy::g-properties-changed: * @proxy: The #GDBusProxy emitting the signal. - * @changed_properties: A #GVariant containing the properties that - * changed or %NULL if no properties changed. - * @invalidated_properties: A %NULL terminated list of properties that was - * invalidated or %NULL if no properties was invalidated. + * @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. + * 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 corresponds to the * PropertiesChanged D-Bus signal on the diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index f4796eea8..972a66dec 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -47,8 +47,11 @@ on_properties_changed (GDBusProxy *proxy, const gchar* const *invalidated_properties, gpointer user_data) { + /* Note that we are guaranteed that changed_properties and + * invalidated_properties are never NULL + */ - if (changed_properties != NULL) + if (g_variant_n_children (changed_properties) > 0) { GVariantIter *iter; GVariant *item; @@ -72,7 +75,7 @@ on_properties_changed (GDBusProxy *proxy, } } - if (invalidated_properties != NULL) + if (g_strv_length ((GStrv) invalidated_properties) > 0) { guint n; g_print (" *** Properties Invalidated:\n"); From 0e2c708bb298c98c136d507427e7b731b5cbd962 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 17:10:15 -0400 Subject: [PATCH 68/76] GDBus: Don't take a GError for g_dbus_proxy_get_cached_property_names() We stopped doing this for get_cached_property() so no reason to do it here. Signed-off-by: David Zeuthen --- gio/gdbusproxy.c | 29 +++++++++------------------ gio/gdbusproxy.h | 3 +-- gio/tests/gdbus-example-watch-proxy.c | 2 +- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 07b7f8e31..4e24d1f59 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -445,6 +445,10 @@ static void g_dbus_proxy_init (GDBusProxy *proxy) { proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, G_TYPE_DBUS_PROXY, GDBusProxyPrivate); + proxy->priv->properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); } /* ---------------------------------------------------------------------------------------------------- */ @@ -452,18 +456,16 @@ g_dbus_proxy_init (GDBusProxy *proxy) /** * g_dbus_proxy_get_cached_property_names: * @proxy: A #GDBusProxy. - * @error: Return location for error or %NULL. * * Gets the names of all cached properties on @proxy. * - * Returns: A %NULL-terminated array of strings or %NULL if @error is set. Free with - * g_strfreev(). + * Returns: A %NULL-terminated array of strings or %NULL if @proxy has + * no cached properties. Free the returned array with g_strfreev(). * * Since: 2.26 */ gchar ** -g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, - GError **error) +g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy) { gchar **names; GPtrArray *p; @@ -471,18 +473,10 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, const gchar *key; g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); names = NULL; - - if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Properties are not available (proxy created with G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)")); - goto out; - } + if (g_hash_table_size (proxy->priv->properties) == 0) + goto out; p = g_ptr_array_new (); @@ -790,11 +784,6 @@ process_get_all_reply (GDBusProxy *proxy, goto out; } - proxy->priv->properties = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) g_variant_unref); - g_variant_iter_init (&iter, g_variant_get_child_value (result, 0)); while ((item = g_variant_iter_next_value (&iter)) != NULL) { diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index f86055593..48688531e 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -127,8 +127,7 @@ GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *pr void g_dbus_proxy_set_cached_property (GDBusProxy *proxy, const gchar *property_name, GVariant *value); -gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, - GError **error); +gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy); void g_dbus_proxy_call (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index 972a66dec..4f4195fd0 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -26,7 +26,7 @@ print_properties (GDBusProxy *proxy) g_print (" properties:\n"); - property_names = g_dbus_proxy_get_cached_property_names (proxy, NULL); + property_names = g_dbus_proxy_get_cached_property_names (proxy); for (n = 0; property_names != NULL && property_names[n] != NULL; n++) { const gchar *key = property_names[n]; From 51446baa52e8cebea57124eb99a32e77a13b1551 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 17:20:39 -0400 Subject: [PATCH 69/76] GDBus: subscribe to PropertiesChanged() before calling GetAll() Otherwise there's a slight chance of a race. --- gio/gdbusproxy.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 4e24d1f59..7399ee67f 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -71,6 +71,8 @@ struct _GDBusProxyPrivate guint properties_changed_subscriber_id; guint signals_subscriber_id; + + gboolean initialized; }; enum @@ -648,12 +650,17 @@ on_signal_received (GDBusConnection *connection, { GDBusProxy *proxy = G_DBUS_PROXY (user_data); + if (!proxy->priv->initialized) + goto out; + g_signal_emit (proxy, signals[SIGNAL_SIGNAL], 0, sender_name, signal_name, parameters); + out: + ; } /* ---------------------------------------------------------------------------------------------------- */ @@ -677,22 +684,17 @@ on_properties_changed (GDBusConnection *connection, GVariant *value; error = NULL; + changed_properties = NULL; + invalidated_properties = NULL; -#if 0 // TODO! - /* Ignore this signal if properties are not yet available - * - * (can happen in the window between subscribing to PropertiesChanged() and until - * org.freedesktop.DBus.Properties.GetAll() returns) - */ - if (!proxy->priv->properties_available) - return; -#endif + if (!proxy->priv->initialized) + goto out; if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) { g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv}as)'", g_variant_get_type_string (parameters)); - return; + goto out; } g_variant_get (parameters, @@ -719,7 +721,8 @@ on_properties_changed (GDBusConnection *connection, invalidated_properties); out: - g_variant_unref (changed_properties); + if (changed_properties != NULL) + g_variant_unref (changed_properties); g_free (invalidated_properties); } @@ -815,6 +818,8 @@ initable_init (GInitable *initable, ret = FALSE; + subscribe_to_signals (proxy); + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) { /* load all properties synchronously */ @@ -836,11 +841,10 @@ initable_init (GInitable *initable, g_variant_unref (result); } - subscribe_to_signals (proxy); - ret = TRUE; out: + proxy->priv->initialized = TRUE; return ret; } @@ -896,6 +900,8 @@ async_initable_init_async (GAsyncInitable *initable, user_data, NULL); + subscribe_to_signals (proxy); + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) { /* load all properties asynchronously */ @@ -944,11 +950,10 @@ async_initable_init_finish (GAsyncInitable *initable, process_get_all_reply (proxy, result); } - subscribe_to_signals (proxy); - ret = TRUE; out: + proxy->priv->initialized = TRUE; return ret; } From 1fd55b8bbfe58adb749d4eee68ca5a71e56e6f82 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 17:44:42 -0400 Subject: [PATCH 70/76] GDBus: Remove g_dbus_is_activated() It's generally hard to get this right so don't attempt to do so. --- docs/reference/gio/gio-sections.txt | 1 - gio/gdbusutils.c | 20 -------------------- gio/gdbusutils.h | 2 -- gio/gio.symbols | 1 - 4 files changed, 24 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index cc0a890d2..a0ffd51ab 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2225,7 +2225,6 @@ g_dbus_address_get_for_bus_sync
gdbusutils -g_dbus_is_activated g_dbus_generate_guid g_dbus_is_guid g_dbus_is_name diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c index fcaf3c3d5..2a1ca67eb 100644 --- a/gio/gdbusutils.c +++ b/gio/gdbusutils.c @@ -357,25 +357,5 @@ g_dbus_is_guid (const gchar *string) /* ---------------------------------------------------------------------------------------------------- */ -/** - * g_dbus_is_activated: - * - * Determine if the process has been activated by a message bus. - * - * Returns: %TRUE if this process has been started by a message bus, %FALSE otherwise. - * - * Since: 2.26 - */ -gboolean -g_dbus_is_activated (void) -{ - /* TODO: technically this will make child processes forked by us - * return TRUE too.. - */ - return g_getenv ("DBUS_STARTER_BUS_TYPE") != NULL; -} - -/* ---------------------------------------------------------------------------------------------------- */ - #define __G_DBUS_UTILS_C__ #include "gioaliasdef.c" diff --git a/gio/gdbusutils.h b/gio/gdbusutils.h index d6f345073..4d8a7ab26 100644 --- a/gio/gdbusutils.h +++ b/gio/gdbusutils.h @@ -35,8 +35,6 @@ gboolean g_dbus_is_unique_name (const gchar *string); gboolean g_dbus_is_member_name (const gchar *string); gboolean g_dbus_is_interface_name (const gchar *string); -gboolean g_dbus_is_activated (void); - G_END_DECLS #endif /* __G_DBUS_UTILS_H__ */ diff --git a/gio/gio.symbols b/gio/gio.symbols index da6eb93d2..c7adc7ccc 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1705,7 +1705,6 @@ g_dbus_server_get_guid #if IN_HEADER(__G_DBUS_UTILS_H__) #if IN_FILE(__G_DBUS_UTILS_C__) g_dbus_generate_guid -g_dbus_is_activated g_dbus_is_guid g_dbus_is_interface_name g_dbus_is_member_name From 68078ed648eec314507ff997e89b053a1d9a6891 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 18:04:48 -0400 Subject: [PATCH 71/76] GDBus: Nuke G_BUS_TYPE_NONE --- gio/gdbusnameowning.c | 1 - gio/gdbusnamewatching.c | 1 - gio/gdbusproxywatching.c | 3 +-- gio/gioenums.h | 12 +++++------- gio/tests/gdbus-peer.c | 1 - 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index ead9a5f15..e14913217 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -591,7 +591,6 @@ g_bus_own_name (GBusType bus_type, { Client *client; - g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0); g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0); G_LOCK (lock); diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c index f93a2ced7..9fe944d81 100644 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c @@ -551,7 +551,6 @@ g_bus_watch_name (GBusType bus_type, { Client *client; - g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0); g_return_val_if_fail (g_dbus_is_name (name), 0); G_LOCK (lock); diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c index 282254d46..2edcb82a4 100644 --- a/gio/gdbusproxywatching.c +++ b/gio/gdbusproxywatching.c @@ -255,7 +255,7 @@ on_name_vanished (GDBusConnection *connection, /** * g_bus_watch_proxy: - * @bus_type: The type of bus to watch a name on (can't be #G_BUS_TYPE_NONE). + * @bus_type: The type of bus to watch a name on. * @name: The name (well-known or unique) to watch. * @flags: Flags from the #GBusNameWatcherFlags enumeration. * @object_path: The object path of the remote object to watch. @@ -318,7 +318,6 @@ g_bus_watch_proxy (GBusType bus_type, { Client *client; - g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0); g_return_val_if_fail (g_dbus_is_name (name), 0); g_return_val_if_fail (g_variant_is_object_path (object_path), 0); g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0); diff --git a/gio/gioenums.h b/gio/gioenums.h index 71ed59a7c..936f99177 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -740,21 +740,19 @@ typedef enum { /** * GBusType: - * @G_BUS_TYPE_NONE: Not a message bus connection. - * @G_BUS_TYPE_SESSION: The login session message bus. + * @G_BUS_TYPE_STARTER: An alias for the message bus that activated the process, if any. * @G_BUS_TYPE_SYSTEM: The system-wide message bus. - * @G_BUS_TYPE_STARTER: Connect to the bus that activated the program. + * @G_BUS_TYPE_SESSION: The login session message bus. * - * An enumeration to specify the type of a #GDBusConnection. + * An enumeration for well-known message buses. * * Since: 2.26 */ typedef enum { - G_BUS_TYPE_NONE = -1, - G_BUS_TYPE_SESSION = 0, + G_BUS_TYPE_STARTER = 0, G_BUS_TYPE_SYSTEM = 1, - G_BUS_TYPE_STARTER = 2 + G_BUS_TYPE_SESSION = 2 } GBusType; /** diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 2444a1d1d..63ddc6d7f 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -510,7 +510,6 @@ test_peer (void) g_main_loop_run (loop); g_assert_cmpint (data.current_connections->len, ==, 1); g_assert_cmpint (data.num_connection_attempts, ==, 1); - //g_assert (g_dbus_connection_get_bus_type (c) == G_BUS_TYPE_NONE); g_assert (g_dbus_connection_get_unique_name (c) == NULL); g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); From dd3551e7c3ae5e6e8b21dd77ce09d72b799e7b1d Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 13 May 2010 18:19:16 -0400 Subject: [PATCH 72/76] GDBus: update gdbus(1) man page --- docs/reference/gio/gdbus.xml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/reference/gio/gdbus.xml b/docs/reference/gio/gdbus.xml index a83511a81..e2f33546d 100644 --- a/docs/reference/gio/gdbus.xml +++ b/docs/reference/gio/gdbus.xml @@ -76,7 +76,7 @@ - Monitors one or all objects owned by the owned of + Monitors one or all objects owned by the owner of bus_name. @@ -225,11 +225,13 @@ The name org.freedesktop.ConsoleKit is owned by :1.15 Monitoring a single object on a service: -$ gdbus monitor --system --dest org.freedesktop.ConsoleKit --object-path /org/freedesktop/ConsoleKit/Session2 -Monitoring signals on object /org/freedesktop/ConsoleKit/Session2 owned by org.freedesktop.ConsoleKit -The name org.freedesktop.ConsoleKit is owned by :1.15 -/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (false,) -/org/freedesktop/ConsoleKit/Session2: org.freedesktop.ConsoleKit.Session.ActiveChanged (true,) +$ gdbus monitor --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/AccessPoint/4141 +Monitoring signals on object /org/freedesktop/NetworkManager/AccessPoint/4141 owned by org.freedesktop.NetworkManager +The name org.freedesktop.NetworkManager is owned by :1.5 +/org/freedesktop/NetworkManager/AccessPoint/4141: org.freedesktop.NetworkManager.AccessPoint.PropertiesChanged ({'Strength': <byte 0x5c>},) +/org/freedesktop/NetworkManager/AccessPoint/4141: org.freedesktop.NetworkManager.AccessPoint.PropertiesChanged ({'Strength': <byte 0x64>},) +/org/freedesktop/NetworkManager/AccessPoint/4141: org.freedesktop.NetworkManager.AccessPoint.PropertiesChanged ({'Strength': <byte 0x5e>},) +/org/freedesktop/NetworkManager/AccessPoint/4141: org.freedesktop.NetworkManager.AccessPoint.PropertiesChanged ({'Strength': <byte 0x64>},) From 8f89b63930181696a3f4a9c441ffd97230577d64 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 20:29:04 -0400 Subject: [PATCH 73/76] Clean up platform-specific includes The problem was pointed out in bug 618029. To solve it, we moved the GUnixFDList typedef to giotypes.h. --- gio/gdbusmessage.c | 2 ++ gio/gdbusmessage.h | 6 ------ gio/giotypes.h | 1 + gio/gunixfdlist.h | 3 +-- gio/gunixfdmessage.c | 1 + gio/gunixfdmessage.h | 2 +- gio/tests/gdbus-example-unix-fd-client.c | 1 + gio/tests/gdbus-peer.c | 1 + 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 1fc3c090e..bfc46f096 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -38,6 +38,8 @@ #include "gioerror.h" #ifdef G_OS_UNIX +#include "gunixfdlist.h" + #include #include #include diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h index a01fb8b8a..b77440917 100644 --- a/gio/gdbusmessage.h +++ b/gio/gdbusmessage.h @@ -25,10 +25,6 @@ #include -#ifdef G_OS_UNIX -#include -#endif - G_BEGIN_DECLS #define G_TYPE_DBUS_MESSAGE (g_dbus_message_get_type ()) @@ -111,11 +107,9 @@ guchar *g_dbus_message_get_header_fields (GDBusMessage GVariant *g_dbus_message_get_body (GDBusMessage *message); void g_dbus_message_set_body (GDBusMessage *message, GVariant *body); -#ifdef G_OS_UNIX GUnixFDList *g_dbus_message_get_unix_fd_list (GDBusMessage *message); void g_dbus_message_set_unix_fd_list (GDBusMessage *message, GUnixFDList *fd_list); -#endif guint32 g_dbus_message_get_reply_serial (GDBusMessage *message); void g_dbus_message_set_reply_serial (GDBusMessage *message, diff --git a/gio/giotypes.h b/gio/giotypes.h index 22a00266c..1c635d501 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -335,6 +335,7 @@ struct _GOutputVector { typedef struct _GCredentials GCredentials; typedef struct _GUnixCredentialsMessage GUnixCredentialsMessage; +typedef struct _GUnixFDList GUnixFDList; typedef struct _GDBusMessage GDBusMessage; typedef struct _GDBusConnection GDBusConnection; typedef struct _GMessageBusConnection GMessageBusConnection; diff --git a/gio/gunixfdlist.h b/gio/gunixfdlist.h index 638b685e7..12b6ee830 100644 --- a/gio/gunixfdlist.h +++ b/gio/gunixfdlist.h @@ -23,7 +23,7 @@ #ifndef __G_UNIX_FD_LIST_H__ #define __G_UNIX_FD_LIST_H__ -#include +#include G_BEGIN_DECLS @@ -41,7 +41,6 @@ G_BEGIN_DECLS typedef struct _GUnixFDListPrivate GUnixFDListPrivate; typedef struct _GUnixFDListClass GUnixFDListClass; -typedef struct _GUnixFDList GUnixFDList; struct _GUnixFDListClass { diff --git a/gio/gunixfdmessage.c b/gio/gunixfdmessage.c index bb37d5f59..5413be3c8 100644 --- a/gio/gunixfdmessage.c +++ b/gio/gunixfdmessage.c @@ -39,6 +39,7 @@ #include #include "gunixfdmessage.h" +#include "gunixfdlist.h" #include "gioerror.h" #include "gioalias.h" diff --git a/gio/gunixfdmessage.h b/gio/gunixfdmessage.h index 44b47c119..3bfa05822 100644 --- a/gio/gunixfdmessage.h +++ b/gio/gunixfdmessage.h @@ -23,8 +23,8 @@ #ifndef __G_UNIX_FD_MESSAGE_H__ #define __G_UNIX_FD_MESSAGE_H__ -#include #include +#include G_BEGIN_DECLS diff --git a/gio/tests/gdbus-example-unix-fd-client.c b/gio/tests/gdbus-example-unix-fd-client.c index 21e199e89..500058b47 100644 --- a/gio/tests/gdbus-example-unix-fd-client.c +++ b/gio/tests/gdbus-example-unix-fd-client.c @@ -7,6 +7,7 @@ #include #include +#include /* see gdbus-example-server.c for the server implementation */ static gint diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 63ddc6d7f..929d5c2a1 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -30,6 +30,7 @@ #include #include +#include #include "gdbus-tests.h" From e72f7f52de9e482eb9d5dd1c47eb0d1bea5f4631 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 21:45:18 -0400 Subject: [PATCH 74/76] Add some more details to the long description --- gio/gdbusproxy.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 7399ee67f..911c65950 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -53,6 +53,20 @@ * for unique name bus and does not track whether the name * vanishes. Use g_bus_watch_proxy() to construct #GDBusProxy proxies * for owners of a well-known names. + * + * By default, #GDBusProxy will cache all properties (and listen for + * their changes) of the remote object, and proxy all signals that gets + * emitted. This behaviour can be changed by passing suitable + * #GDBusProxyFlags when the proxy is created. + * + * The generic #GDBusProxy::g-properties-changed and #GDBusProxy::g-signal + * signals are not very convenient to work with. Therefore, the recommended + * way of working with proxies is to subclass #GDBusProxy, and have + * more natural properties and signals in your derived class. The + * @interface_type argument of g_bus_watch_proxy() lets you obtain + * instances of your derived class when using the high-level API. + * + * See for an example. */ struct _GDBusProxyPrivate From bdc29f82d39619747ac564918e041181b26449ee Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 21:53:51 -0400 Subject: [PATCH 75/76] Document more floating variant details. --- gio/gdbusconnection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index d9c161555..edebd1887 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -4083,6 +4083,8 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, * * Emits a signal. * + * If the parameters GVariant is floating, it is consumed. + * * This can only fail if @parameters is not compatible with the D-Bus protocol. * * Returns: %TRUE unless @error is set. From 6e8637e4783ae4e573f6784f005920930d9fca87 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 13 May 2010 22:15:47 -0400 Subject: [PATCH 76/76] The default timeout is 25s --- gio/gdbusconnection.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index edebd1887..c3d17af55 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -95,11 +95,6 @@ * kind of pitfalls it avoids * - Export objects before claiming names * - Talk about auto-starting services (cf. GBusNameWatcherFlags) - * - * - Mention in all API that the GVariant is sunk. Also mention - * when the returned GVariant is floating. - * - * - Consistent timeout handling (25s vs 30s?) */ #include "config.h" @@ -1288,7 +1283,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect out_serial = &serial; if (timeout_msec == -1) - timeout_msec = 30 * 1000; /* TODO: check 30 secs is the default timeout */ + timeout_msec = 25 * 1000; simple = g_simple_async_result_new (G_OBJECT (connection), callback,