/*
* dbus.c - Source for D-Bus utilities
*
* Copyright (C) 2005-2008 Collabora Ltd.
* Copyright (C) 2005-2008 Nokia Corporation
*
* 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* SECTION:dbus
* @title: D-Bus utilities
* @short_description: some D-Bus utility functions
*
* D-Bus utility functions used in telepathy-glib.
*/
/**
* SECTION:asv
* @title: Manipulating a{sv} mappings
* @short_description: Functions to manipulate mappings from string to
* variant, as represented in dbus-glib by a #GHashTable from string
* to #GValue
*
* Mappings from string to variant (D-Bus signature a{sv}) are commonly used
* to provide extensibility, but in dbus-glib they're somewhat awkward to deal
* with.
*
* These functions provide convenient access to the values in such
* a mapping.
*
* They also work around the fact that none of the #GHashTable public API
* takes a const pointer to a #GHashTable, even the read-only methods that
* logically ought to.
*
* Parts of telepathy-glib return const pointers to #GHashTable, to encourage
* the use of this API.
*
* Since: 0.7.9
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "telepathy-glib/_gen/signals-marshal.h"
#include "telepathy-glib/_gen/tp-cli-dbus-daemon-body.h"
/**
* tp_asv_size:
* @asv: a GHashTable
*
* Return the size of @asv as if via g_hash_table_size().
*
* The only difference is that this version takes a const #GHashTable and
* casts it.
*
* Since: 0.7.12
*/
/* (#define + static inline in dbus.h) */
/**
* tp_dbus_g_method_return_not_implemented:
* @context: The D-Bus method invocation context
*
* Return the Telepathy error NotImplemented from the method invocation
* given by @context.
*/
void
tp_dbus_g_method_return_not_implemented (DBusGMethodInvocation *context)
{
GError e = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Not implemented" };
dbus_g_method_return_error (context, &e);
}
/**
* tp_get_bus:
*
*
*
* Returns: a connection to the starter or session D-Bus daemon.
*/
DBusGConnection *
tp_get_bus (void)
{
static DBusGConnection *bus = NULL;
if (bus == NULL)
{
GError *error = NULL;
bus = dbus_g_bus_get (DBUS_BUS_STARTER, &error);
if (bus == NULL)
{
g_warning ("Failed to connect to starter bus: %s", error->message);
exit (1);
}
}
return bus;
}
/**
* tp_get_bus_proxy:
*
*
*
* Returns: a proxy for the bus daemon object on the starter or session bus.
*/
DBusGProxy *
tp_get_bus_proxy (void)
{
static DBusGProxy *bus_proxy = NULL;
if (bus_proxy == NULL)
{
DBusGConnection *bus = tp_get_bus ();
bus_proxy = dbus_g_proxy_new_for_name (bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus");
if (bus_proxy == NULL)
g_error ("Failed to get proxy object for bus.");
}
return bus_proxy;
}
/**
* TpDBusNameType:
* @TP_DBUS_NAME_TYPE_UNIQUE: accept unique names like :1.123
* (not including the name of the bus daemon itself)
* @TP_DBUS_NAME_TYPE_WELL_KNOWN: accept well-known names like
* com.example.Service (not including the name of the bus daemon itself)
* @TP_DBUS_NAME_TYPE_BUS_DAEMON: accept the name of the bus daemon
* itself, which has the syntax of a well-known name, but behaves like a
* unique name
* @TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON: accept either unique or well-known
* names, but not the bus daemon
* @TP_DBUS_NAME_TYPE_ANY: accept any of the above
*
* A set of flags indicating which D-Bus bus names are acceptable.
* They can be combined with the bitwise-or operator to accept multiple
* types. %TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON and %TP_DBUS_NAME_TYPE_ANY are
* the bitwise-or of other appropriate types, for convenience.
*
* Since: 0.7.1
*/
/**
* tp_dbus_check_valid_bus_name:
* @name: a possible bus name
* @allow_types: some combination of %TP_DBUS_NAME_TYPE_UNIQUE,
* %TP_DBUS_NAME_TYPE_WELL_KNOWN or %TP_DBUS_NAME_TYPE_BUS_DAEMON
* (often this will be %TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON or
* %TP_DBUS_NAME_TYPE_ANY)
* @error: used to raise %TP_DBUS_ERROR_INVALID_BUS_NAME if %FALSE is returned
*
* Check that the given string is a valid D-Bus bus name of an appropriate
* type.
*
* Returns: %TRUE if @name is valid
*
* Since: 0.7.1
*/
gboolean
tp_dbus_check_valid_bus_name (const gchar *name,
TpDBusNameType allow_types,
GError **error)
{
gboolean dot = FALSE;
gboolean unique;
gchar last;
const gchar *ptr;
g_return_val_if_fail (name != NULL, FALSE);
if (name[0] == '\0')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"The empty string is not a valid bus name");
return FALSE;
}
if (!tp_strdiff (name, DBUS_SERVICE_DBUS))
{
if (allow_types & TP_DBUS_NAME_TYPE_BUS_DAEMON)
return TRUE;
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"The D-Bus daemon's bus name is not acceptable here");
return FALSE;
}
unique = (name[0] == ':');
if (unique && (allow_types & TP_DBUS_NAME_TYPE_UNIQUE) == 0)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"A well-known bus name not starting with ':'%s is required",
allow_types & TP_DBUS_NAME_TYPE_BUS_DAEMON
? " (or the bus daemon itself)"
: "");
return FALSE;
}
if (!unique && (allow_types & TP_DBUS_NAME_TYPE_WELL_KNOWN) == 0)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"A unique bus name starting with ':'%s is required",
allow_types & TP_DBUS_NAME_TYPE_BUS_DAEMON
? " (or the bus daemon itself)"
: "");
return FALSE;
}
if (strlen (name) > 255)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name: too long (> 255 characters)");
return FALSE;
}
last = '\0';
for (ptr = name + (unique ? 1 : 0); *ptr != '\0'; ptr++)
{
if (*ptr == '.')
{
dot = TRUE;
if (last == '.')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': contains '..'", name);
return FALSE;
}
else if (last == '\0')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': must not start with '.'", name);
return FALSE;
}
}
else if (g_ascii_isdigit (*ptr))
{
if (!unique)
{
if (last == '.')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': a digit may not follow '.' "
"except in a unique name starting with ':'", name);
return FALSE;
}
else if (last == '\0')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': must not start with a digit",
name);
return FALSE;
}
}
}
else if (!g_ascii_isalpha (*ptr) && *ptr != '_' && *ptr != '-')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': contains invalid character '%c'",
name, *ptr);
return FALSE;
}
last = *ptr;
}
if (last == '.')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': must not end with '.'", name);
return FALSE;
}
if (!dot)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
"Invalid bus name '%s': must contain '.'", name);
return FALSE;
}
return TRUE;
}
/**
* tp_dbus_check_valid_interface_name:
* @name: a possible interface name
* @error: used to raise %TP_DBUS_ERROR_INVALID_INTERFACE_NAME if %FALSE is
* returned
*
* Check that the given string is a valid D-Bus interface name. This is
* also appropriate to use to check for valid error names.
*
* Returns: %TRUE if @name is valid
*
* Since: 0.7.1
*/
gboolean
tp_dbus_check_valid_interface_name (const gchar *name,
GError **error)
{
gboolean dot = FALSE;
gchar last;
const gchar *ptr;
g_return_val_if_fail (name != NULL, FALSE);
if (name[0] == '\0')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"The empty string is not a valid interface name");
return FALSE;
}
if (strlen (name) > 255)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name: too long (> 255 characters)");
return FALSE;
}
last = '\0';
for (ptr = name; *ptr != '\0'; ptr++)
{
if (*ptr == '.')
{
dot = TRUE;
if (last == '.')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': contains '..'", name);
return FALSE;
}
else if (last == '\0')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': must not start with '.'",
name);
return FALSE;
}
}
else if (g_ascii_isdigit (*ptr))
{
if (last == '\0')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': must not start with a digit",
name);
return FALSE;
}
else if (last == '.')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': a digit must not follow '.'",
name);
return FALSE;
}
}
else if (!g_ascii_isalpha (*ptr) && *ptr != '_')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': contains invalid character '%c'",
name, *ptr);
return FALSE;
}
last = *ptr;
}
if (last == '.')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': must not end with '.'", name);
return FALSE;
}
if (!dot)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
"Invalid interface name '%s': must contain '.'", name);
return FALSE;
}
return TRUE;
}
/**
* tp_dbus_check_valid_member_name:
* @name: a possible member name
* @error: used to raise %TP_DBUS_ERROR_INVALID_MEMBER_NAME if %FALSE is
* returned
*
* Check that the given string is a valid D-Bus member (method or signal) name.
*
* Returns: %TRUE if @name is valid
*
* Since: 0.7.1
*/
gboolean
tp_dbus_check_valid_member_name (const gchar *name,
GError **error)
{
const gchar *ptr;
g_return_val_if_fail (name != NULL, FALSE);
if (name[0] == '\0')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_MEMBER_NAME,
"The empty string is not a valid method or signal name");
return FALSE;
}
if (strlen (name) > 255)
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_MEMBER_NAME,
"Invalid method or signal name: too long (> 255 characters)");
return FALSE;
}
for (ptr = name; *ptr != '\0'; ptr++)
{
if (g_ascii_isdigit (*ptr))
{
if (ptr == name)
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_MEMBER_NAME,
"Invalid method or signal name '%s': must not start with "
"a digit", name);
return FALSE;
}
}
else if (!g_ascii_isalpha (*ptr) && *ptr != '_')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_MEMBER_NAME,
"Invalid method or signal name '%s': contains invalid "
"character '%c'",
name, *ptr);
return FALSE;
}
}
return TRUE;
}
/**
* tp_dbus_check_valid_object_path:
* @path: a possible object path
* @error: used to raise %TP_DBUS_ERROR_INVALID_OBJECT_PATH if %FALSE is
* returned
*
* Check that the given string is a valid D-Bus object path.
*
* Returns: %TRUE if @path is valid
*
* Since: 0.7.1
*/
gboolean
tp_dbus_check_valid_object_path (const gchar *path, GError **error)
{
const gchar *ptr;
g_return_val_if_fail (path != NULL, FALSE);
if (path[0] != '/')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_OBJECT_PATH,
"Invalid object path '%s': must start with '/'",
path);
return FALSE;
}
if (path[1] == '\0')
return TRUE;
for (ptr = path + 1; *ptr != '\0'; ptr++)
{
if (*ptr == '/')
{
if (ptr[-1] == '/')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_OBJECT_PATH,
"Invalid object path '%s': contains '//'", path);
return FALSE;
}
}
else if (!g_ascii_isalnum (*ptr) && *ptr != '_')
{
g_set_error (error, TP_DBUS_ERRORS,
TP_DBUS_ERROR_INVALID_OBJECT_PATH,
"Invalid object path '%s': contains invalid character '%c'",
path, *ptr);
return FALSE;
}
}
if (ptr[-1] == '/')
{
g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_OBJECT_PATH,
"Invalid object path '%s': is not '/' but does end with '/'",
path);
return FALSE;
}
return TRUE;
}
/**
* TpDBusDaemonClass:
*
* The class of #TpDBusDaemon.
*
* Since: 0.7.1
*/
struct _TpDBusDaemonClass
{
/**/
TpProxyClass parent_class;
gpointer priv;
};
/**
* TpDBusDaemon:
*
* A subclass of #TpProxy that represents the D-Bus daemon. It mainly provides
* functionality to manage well-known names on the bus.
*
* Since: 0.7.1
*/
struct _TpDBusDaemon
{
/**/
TpProxy parent;
TpDBusDaemonPrivate *priv;
};
struct _TpDBusDaemonPrivate
{
/* dup'd name => _NameOwnerWatch */
GHashTable *name_owner_watches;
};
G_DEFINE_TYPE (TpDBusDaemon, tp_dbus_daemon, TP_TYPE_PROXY);
/**
* tp_dbus_daemon_new:
* @connection: a connection to D-Bus
*
*
*
* Returns: a new proxy for signals and method calls on the bus daemon
* to which @connection is connected
*
* Since: 0.7.1
*/
TpDBusDaemon *
tp_dbus_daemon_new (DBusGConnection *connection)
{
g_return_val_if_fail (connection != NULL, NULL);
return TP_DBUS_DAEMON (g_object_new (TP_TYPE_DBUS_DAEMON,
"dbus-connection", connection,
"bus-name", DBUS_SERVICE_DBUS,
"object-path", DBUS_PATH_DBUS,
NULL));
}
typedef struct
{
TpDBusDaemonNameOwnerChangedCb callback;
gpointer user_data;
GDestroyNotify destroy;
gchar *last_owner;
} _NameOwnerWatch;
typedef struct
{
TpDBusDaemonNameOwnerChangedCb callback;
gpointer user_data;
GDestroyNotify destroy;
} _NameOwnerSubWatch;
static void
_tp_dbus_daemon_name_owner_changed_multiple (TpDBusDaemon *self,
const gchar *name,
const gchar *new_owner,
gpointer user_data)
{
GArray *array = user_data;
guint i;
for (i = 0; i < array->len; i++)
{
_NameOwnerSubWatch *watch = &g_array_index (array, _NameOwnerSubWatch,
i);
watch->callback (self, name, new_owner, watch->user_data);
}
}
static void
_tp_dbus_daemon_name_owner_changed_multiple_free (gpointer data)
{
GArray *array = data;
guint i;
for (i = 0; i < array->len; i++)
{
_NameOwnerSubWatch *watch = &g_array_index (array, _NameOwnerSubWatch,
i);
if (watch->destroy)
watch->destroy (watch->user_data);
}
g_array_free (array, TRUE);
}
static void
_tp_dbus_daemon_name_owner_changed (TpDBusDaemon *self,
const gchar *name,
const gchar *new_owner)
{
_NameOwnerWatch *watch = g_hash_table_lookup (self->priv->name_owner_watches,
name);
if (watch == NULL)
return;
/* This is partly to handle the case where an owner change happens
* while GetNameOwner is in flight, partly to be able to optimize by only
* calling GetNameOwner if we didn't already know, and partly because of a
* dbus-glib bug that means we get every signal twice
* (it thinks org.freedesktop.DBus is both a well-known name and a unique
* name). */
if (!tp_strdiff (watch->last_owner, new_owner))
return;
g_free (watch->last_owner);
watch->last_owner = g_strdup (new_owner);
watch->callback (self, name, new_owner, watch->user_data);
}
static void
_tp_dbus_daemon_name_owner_changed_cb (TpDBusDaemon *self,
const gchar *name,
const gchar *old_owner,
const gchar *new_owner,
gpointer user_data,
GObject *object)
{
_tp_dbus_daemon_name_owner_changed (self, name, new_owner);
}
static void
_tp_dbus_daemon_got_name_owner (TpDBusDaemon *self,
const gchar *owner,
const GError *error,
gpointer user_data,
GObject *user_object)
{
gchar *name = user_data;
if (error != NULL)
owner = "";
_tp_dbus_daemon_name_owner_changed (self, name, owner);
}
/**
* TpDBusDaemonNameOwnerChangedCb:
* @bus_daemon: The D-Bus daemon
* @name: The name whose ownership has changed or been discovered
* @new_owner: The unique name that now owns @name
* @user_data: Arbitrary user-supplied data as passed to
* tp_dbus_daemon_watch_name_owner()
*
* The signature of the callback called by tp_dbus_daemon_watch_name_owner().
*
* Since: 0.7.1
*/
/**
* tp_dbus_daemon_watch_name_owner:
* @self: The D-Bus daemon
* @name: The name whose ownership is to be watched
* @callback: Callback to call when the ownership is discovered or changes
* @user_data: Arbitrary data to pass to @callback
* @destroy: Called to destroy @user_data when the name owner watch is
* cancelled due to tp_dbus_daemon_cancel_name_owner_watch()
*
* Arrange for @callback to be called with the owner of @name as soon as
* possible (which might even be before this function returns!), then
* again every time the ownership of @name changes.
*
* If multiple watches are registered for the same @name, they will be called
* in the order they were registered.
*
* Since: 0.7.1
*/
void
tp_dbus_daemon_watch_name_owner (TpDBusDaemon *self,
const gchar *name,
TpDBusDaemonNameOwnerChangedCb callback,
gpointer user_data,
GDestroyNotify destroy)
{
_NameOwnerWatch *watch = g_hash_table_lookup (self->priv->name_owner_watches,
name);
g_return_if_fail (TP_IS_DBUS_DAEMON (self));
g_return_if_fail (name != NULL);
g_return_if_fail (callback != NULL);
if (watch == NULL)
{
/* Allocate a single watch (common case) */
watch = g_slice_new (_NameOwnerWatch);
watch->callback = callback;
watch->user_data = user_data;
watch->destroy = destroy;
watch->last_owner = NULL;
g_hash_table_insert (self->priv->name_owner_watches, g_strdup (name),
watch);
tp_cli_dbus_daemon_call_get_name_owner (self, -1, name,
_tp_dbus_daemon_got_name_owner,
g_strdup (name), g_free, NULL);
}
else
{
_NameOwnerSubWatch tmp = { callback, user_data, destroy };
if (watch->callback == _tp_dbus_daemon_name_owner_changed_multiple)
{
/* The watch is already a "multiplexer", just append to it */
GArray *array = watch->user_data;
g_array_append_val (array, tmp);
}
else
{
/* Replace the old contents of the watch with one that dispatches
* the signal to (potentially) more than one watcher */
GArray *array = g_array_sized_new (FALSE, FALSE,
sizeof (_NameOwnerSubWatch), 2);
/* The new watcher */
g_array_append_val (array, tmp);
/* The old watcher */
tmp.callback = watch->callback;
tmp.user_data = watch->user_data;
tmp.destroy = watch->destroy;
g_array_prepend_val (array, tmp);
watch->callback = _tp_dbus_daemon_name_owner_changed_multiple;
watch->user_data = array;
watch->destroy = _tp_dbus_daemon_name_owner_changed_multiple_free;
}
if (watch->last_owner != NULL)
{
/* FIXME: should avoid reentrancy? */
callback (self, name, watch->last_owner, user_data);
}
}
}
/**
* tp_dbus_daemon_cancel_name_owner_watch:
* @self: the D-Bus daemon
* @name: the name that was being watched
* @callback: the callback that was called
* @user_data: the user data that was provided
*
* If there was a previous call to tp_dbus_daemon_watch_name_owner()
* with exactly the given @name, @callback and @user_data, remove it.
*
* If more than one watch matching the details provided was active, remove
* only the most recently added one.
*
* Returns: %TRUE if there was such a watch, %FALSE otherwise
*
* Since: 0.7.1
*/
gboolean
tp_dbus_daemon_cancel_name_owner_watch (TpDBusDaemon *self,
const gchar *name,
TpDBusDaemonNameOwnerChangedCb callback,
gconstpointer user_data)
{
_NameOwnerWatch *watch = g_hash_table_lookup (self->priv->name_owner_watches,
name);
g_return_val_if_fail (TP_IS_DBUS_DAEMON (self), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (callback != NULL, FALSE);
if (watch == NULL)
{
/* No watch at all */
return FALSE;
}
else if (watch->callback == callback && watch->user_data == user_data)
{
/* Simple case: there is one name-owner watch and it's what we wanted */
if (watch->destroy)
watch->destroy (watch->user_data);
g_free (watch->last_owner);
g_slice_free (_NameOwnerWatch, watch);
g_hash_table_remove (self->priv->name_owner_watches, name);
return TRUE;
}
else if (watch->callback == _tp_dbus_daemon_name_owner_changed_multiple)
{
/* Complicated case: this watch is a "multiplexer", we need to check
* its contents */
GArray *array = watch->user_data;
guint i;
for (i = 1; i <= array->len; i++)
{
_NameOwnerSubWatch *entry = &g_array_index (array,
_NameOwnerSubWatch, array->len - i);
if (entry->callback == callback && entry->user_data == user_data)
{
if (entry->destroy != NULL)
entry->destroy (entry->user_data);
g_array_remove_index (array, array->len - i);
if (array->len == 0)
{
watch->destroy (watch->user_data);
g_free (watch->last_owner);
g_slice_free (_NameOwnerWatch, watch);
g_hash_table_remove (self->priv->name_owner_watches, name);
}
return TRUE;
}
}
}
/* We haven't found it */
return FALSE;
}
/* for internal use (TpChannel, TpConnection _new convenience functions) */
gboolean
_tp_dbus_daemon_get_name_owner (TpDBusDaemon *self,
gint timeout_ms,
const gchar *well_known_name,
gchar **unique_name,
GError **error)
{
DBusGProxy *iface = tp_proxy_borrow_interface_by_id ((TpProxy *) self,
TP_IFACE_QUARK_DBUS_DAEMON, error);
if (iface == NULL)
return FALSE;
return dbus_g_proxy_call_with_timeout (iface, "GetNameOwner", timeout_ms,
error,
G_TYPE_STRING, well_known_name,
G_TYPE_INVALID,
G_TYPE_STRING, unique_name,
G_TYPE_INVALID);
}
static GObject *
tp_dbus_daemon_constructor (GType type,
guint n_params,
GObjectConstructParam *params)
{
GObjectClass *object_class =
(GObjectClass *) tp_dbus_daemon_parent_class;
TpDBusDaemon *self = TP_DBUS_DAEMON (object_class->constructor (type,
n_params, params));
TpProxy *as_proxy = (TpProxy *) self;
/* Connect to my own NameOwnerChanged signal.
* The proxy hasn't had a chance to become invalid yet, so we can
* assume that this signal connection will work */
tp_cli_dbus_daemon_connect_to_name_owner_changed (self,
_tp_dbus_daemon_name_owner_changed_cb, NULL, NULL, NULL, NULL);
g_assert (!tp_strdiff (as_proxy->bus_name, DBUS_SERVICE_DBUS));
g_assert (!tp_strdiff (as_proxy->object_path, DBUS_PATH_DBUS));
return (GObject *) self;
}
static void
tp_dbus_daemon_init (TpDBusDaemon *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_DBUS_DAEMON,
TpDBusDaemonPrivate);
self->priv->name_owner_watches = g_hash_table_new_full (g_str_hash,
g_str_equal, g_free, NULL);
}
static void
tp_dbus_daemon_dispose (GObject *object)
{
TpDBusDaemon *self = TP_DBUS_DAEMON (object);
if (self->priv->name_owner_watches != NULL)
{
GHashTable *tmp = self->priv->name_owner_watches;
self->priv->name_owner_watches = NULL;
g_hash_table_destroy (tmp);
}
G_OBJECT_CLASS (tp_dbus_daemon_parent_class)->dispose (object);
}
static void
tp_dbus_daemon_class_init (TpDBusDaemonClass *klass)
{
TpProxyClass *proxy_class = (TpProxyClass *) klass;
GObjectClass *object_class = (GObjectClass *) klass;
g_type_class_add_private (klass, sizeof (TpDBusDaemonPrivate));
object_class->constructor = tp_dbus_daemon_constructor;
object_class->dispose = tp_dbus_daemon_dispose;
proxy_class->interface = TP_IFACE_QUARK_DBUS_DAEMON;
tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_DBUS_DAEMON,
tp_cli_dbus_daemon_add_signals);
}
/* Auto-generated implementation of _tp_register_dbus_glib_marshallers */
#include "_gen/register-dbus-glib-marshallers-body.h"
/**
* tp_asv_get_boolean:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @valid: Either %NULL, or a location to store %TRUE if the key actually
* exists and has a boolean value
*
* If a value for @key in @asv is present and boolean, return it,
* and set *@valid to %TRUE if @valid is not %NULL.
*
* Otherwise return %FALSE, and set *@valid to %FALSE if @valid is not %NULL.
*
* (FIXME: should we also allow 'i' and 'u' with nonzero <=> True?)
*
* Returns: a boolean value for @key
* Since: 0.7.9
*/
gboolean
tp_asv_get_boolean (const GHashTable *asv,
const gchar *key,
gboolean *valid)
{
GValue *value;
g_return_val_if_fail (asv != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL || !G_VALUE_HOLDS_BOOLEAN (value))
{
if (valid != NULL)
*valid = FALSE;
return FALSE;
}
if (valid != NULL)
*valid = TRUE;
return g_value_get_boolean (value);
}
/**
* tp_asv_get_bytes:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
*
* If a value for @key in @asv is present and is an array of bytes
* (its GType is %DBUS_TYPE_G_UCHAR_ARRAY), return it.
*
* Otherwise return %NULL.
*
* The returned value is not copied, and is only valid as long as the value
* for @key in @asv is not removed or altered. Copy it with
* g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, ...) if you need to keep
* it for longer.
*
* Returns: the string value of @key, or %NULL
* Since: 0.7.9
*/
const GArray *
tp_asv_get_bytes (const GHashTable *asv,
const gchar *key)
{
GValue *value;
g_return_val_if_fail (asv != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL || !G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY))
return NULL;
return g_value_get_boxed (value);
}
/**
* tp_asv_get_string:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
*
* If a value for @key in @asv is present and is a string, return it.
*
* Otherwise return %NULL.
*
* The returned value is not copied, and is only valid as long as the value
* for @key in @asv is not removed or altered. Copy it with g_strdup() if you
* need to keep it for longer.
*
* Returns: the string value of @key, or %NULL
* Since: 0.7.9
*/
const gchar *
tp_asv_get_string (const GHashTable *asv,
const gchar *key)
{
GValue *value;
g_return_val_if_fail (asv != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL || !G_VALUE_HOLDS_STRING (value))
return NULL;
return g_value_get_string (value);
}
/**
* tp_asv_get_int32:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @valid: Either %NULL, or a location in which to store %TRUE on success or
* %FALSE on failure
*
* If a value for @key in @asv is present, has an integer type used by
* dbus-glib (guchar, gint, guint, gint64 or guint64) and fits in the
* range of a gint32, return it, and if @valid is not %NULL, set *@valid to
* %TRUE.
*
* Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
*
* Returns: the 32-bit signed integer value of @key, or 0
* Since: 0.7.9
*/
gint32
tp_asv_get_int32 (const GHashTable *asv,
const gchar *key,
gboolean *valid)
{
gint64 i;
guint64 u;
gint32 ret;
GValue *value;
g_return_val_if_fail (asv != NULL, 0);
g_return_val_if_fail (key != NULL, 0);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL)
goto return_invalid;
switch (G_VALUE_TYPE (value))
{
case G_TYPE_UCHAR:
ret = g_value_get_uchar (value);
break;
case G_TYPE_UINT:
u = g_value_get_uint (value);
if (G_UNLIKELY (u > G_MAXINT32))
goto return_invalid;
ret = u;
break;
case G_TYPE_INT:
ret = g_value_get_int (value);
break;
case G_TYPE_INT64:
i = g_value_get_int64 (value);
if (G_UNLIKELY (i < G_MININT32 || i > G_MAXINT32))
goto return_invalid;
ret = i;
break;
case G_TYPE_UINT64:
u = g_value_get_uint64 (value);
if (G_UNLIKELY (u > G_MAXINT32))
goto return_invalid;
ret = u;
break;
default:
goto return_invalid;
}
if (valid != NULL)
*valid = TRUE;
return ret;
return_invalid:
if (valid != NULL)
*valid = FALSE;
return 0;
}
/**
* tp_asv_get_uint32:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @valid: Either %NULL, or a location in which to store %TRUE on success or
* %FALSE on failure
*
* If a value for @key in @asv is present, has an integer type used by
* dbus-glib (guchar, gint, guint, gint64 or guint64) and fits in the
* range of a guint32, return it, and if @valid is not %NULL, set *@valid to
* %TRUE.
*
* Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
*
* Returns: the 32-bit unsigned integer value of @key, or 0
* Since: 0.7.9
*/
guint32
tp_asv_get_uint32 (const GHashTable *asv,
const gchar *key,
gboolean *valid)
{
gint64 i;
guint64 u;
guint32 ret;
GValue *value;
g_return_val_if_fail (asv != NULL, 0);
g_return_val_if_fail (key != NULL, 0);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL)
goto return_invalid;
switch (G_VALUE_TYPE (value))
{
case G_TYPE_UCHAR:
ret = g_value_get_uchar (value);
break;
case G_TYPE_UINT:
ret = g_value_get_uint (value);
break;
case G_TYPE_INT:
i = g_value_get_int (value);
if (G_UNLIKELY (i < 0))
goto return_invalid;
ret = i;
break;
case G_TYPE_INT64:
i = g_value_get_int64 (value);
if (G_UNLIKELY (i < 0 || i > G_MAXUINT32))
goto return_invalid;
ret = i;
break;
case G_TYPE_UINT64:
u = g_value_get_uint64 (value);
if (G_UNLIKELY (u > G_MAXUINT32))
goto return_invalid;
ret = u;
break;
default:
goto return_invalid;
}
if (valid != NULL)
*valid = TRUE;
return ret;
return_invalid:
if (valid != NULL)
*valid = FALSE;
return 0;
}
/**
* tp_asv_get_int64:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @valid: Either %NULL, or a location in which to store %TRUE on success or
* %FALSE on failure
*
* If a value for @key in @asv is present, has an integer type used by
* dbus-glib (guchar, gint, guint, gint64 or guint64) and fits in the
* range of a gint64, return it, and if @valid is not %NULL, set *@valid to
* %TRUE.
*
* Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
*
* Returns: the 64-bit signed integer value of @key, or 0
* Since: 0.7.9
*/
gint64
tp_asv_get_int64 (const GHashTable *asv,
const gchar *key,
gboolean *valid)
{
gint64 ret;
guint64 u;
GValue *value;
g_return_val_if_fail (asv != NULL, 0);
g_return_val_if_fail (key != NULL, 0);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL)
goto return_invalid;
switch (G_VALUE_TYPE (value))
{
case G_TYPE_UCHAR:
ret = g_value_get_uchar (value);
break;
case G_TYPE_UINT:
ret = g_value_get_uint (value);
break;
case G_TYPE_INT:
ret = g_value_get_int (value);
break;
case G_TYPE_INT64:
ret = g_value_get_int64 (value);
break;
case G_TYPE_UINT64:
u = g_value_get_uint64 (value);
if (G_UNLIKELY (u > G_MAXINT64))
goto return_invalid;
ret = u;
break;
default:
goto return_invalid;
}
if (valid != NULL)
*valid = TRUE;
return ret;
return_invalid:
if (valid != NULL)
*valid = FALSE;
return 0;
}
/**
* tp_asv_get_uint64:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @valid: Either %NULL, or a location in which to store %TRUE on success or
* %FALSE on failure
*
* If a value for @key in @asv is present, has an integer type used by
* dbus-glib (guchar, gint, guint, gint64 or guint64) and is non-negative,
* return it, and if @valid is not %NULL, set *@valid to %TRUE.
*
* Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
*
* Returns: the 64-bit unsigned integer value of @key, or 0
* Since: 0.7.9
*/
guint64
tp_asv_get_uint64 (const GHashTable *asv,
const gchar *key,
gboolean *valid)
{
gint64 tmp;
guint64 ret;
GValue *value;
g_return_val_if_fail (asv != NULL, 0);
g_return_val_if_fail (key != NULL, 0);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL)
goto return_invalid;
switch (G_VALUE_TYPE (value))
{
case G_TYPE_UCHAR:
ret = g_value_get_uchar (value);
break;
case G_TYPE_UINT:
ret = g_value_get_uint (value);
break;
case G_TYPE_INT:
tmp = g_value_get_int (value);
if (G_UNLIKELY (tmp < 0))
goto return_invalid;
ret = tmp;
break;
case G_TYPE_INT64:
tmp = g_value_get_int64 (value);
if (G_UNLIKELY (tmp < 0))
goto return_invalid;
ret = tmp;
break;
case G_TYPE_UINT64:
ret = g_value_get_uint64 (value);
break;
default:
goto return_invalid;
}
if (valid != NULL)
*valid = TRUE;
return ret;
return_invalid:
if (valid != NULL)
*valid = FALSE;
return 0;
}
/* FIXME: reviewers: should this succeed on all numeric types, or just on
* doubles? */
/**
* tp_asv_get_double:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @valid: Either %NULL, or a location in which to store %TRUE on success or
* %FALSE on failure
*
* If a value for @key in @asv is present and has any numeric type used by
* dbus-glib (guchar, gint, guint, gint64, guint64 or gdouble),
* return it as a double, and if @valid is not %NULL, set *@valid to %TRUE.
*
* Otherwise, return 0.0, and if @valid is not %NULL, set *@valid to %FALSE.
*
* Returns: the double precision floating-point value of @key, or 0.0
* Since: 0.7.9
*/
gdouble
tp_asv_get_double (const GHashTable *asv,
const gchar *key,
gboolean *valid)
{
gdouble ret;
GValue *value;
g_return_val_if_fail (asv != NULL, 0.0);
g_return_val_if_fail (key != NULL, 0.0);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL)
goto return_invalid;
switch (G_VALUE_TYPE (value))
{
case G_TYPE_DOUBLE:
ret = g_value_get_double (value);
break;
case G_TYPE_UCHAR:
ret = g_value_get_uchar (value);
break;
case G_TYPE_UINT:
ret = g_value_get_uint (value);
break;
case G_TYPE_INT:
ret = g_value_get_int (value);
break;
case G_TYPE_INT64:
ret = g_value_get_int64 (value);
break;
case G_TYPE_UINT64:
ret = g_value_get_uint64 (value);
break;
default:
goto return_invalid;
}
if (valid != NULL)
*valid = TRUE;
return ret;
return_invalid:
if (valid != NULL)
*valid = FALSE;
return 0;
}
/**
* tp_asv_get_object_path:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
*
* If a value for @key in @asv is present and is an object path, return it.
*
* Otherwise return %NULL.
*
* The returned value is not copied, and is only valid as long as the value
* for @key in @asv is not removed or altered. Copy it with g_strdup() if you
* need to keep it for longer.
*
* Returns: the object-path value of @key, or %NULL
* Since: 0.7.9
*/
const gchar *
tp_asv_get_object_path (const GHashTable *asv,
const gchar *key)
{
GValue *value;
g_return_val_if_fail (asv != NULL, 0);
g_return_val_if_fail (key != NULL, 0);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL || !G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
return NULL;
return g_value_get_boxed (value);
}
/**
* tp_asv_get_boxed:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
* @type: The type that the key's value should have, which must be derived
* from %G_TYPE_BOXED
*
* If a value for @key in @asv is present and is of the desired type,
* return it.
*
* Otherwise return %NULL.
*
* The returned value is not copied, and is only valid as long as the value
* for @key in @asv is not removed or altered. Copy it, for instance with
* g_boxed_copy(), if you need to keep it for longer.
*
* Returns: the value of @key, or %NULL
* Since: 0.7.9
*/
gpointer
tp_asv_get_boxed (const GHashTable *asv,
const gchar *key,
GType type)
{
GValue *value;
g_return_val_if_fail (asv != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL || !G_VALUE_HOLDS (value, type))
return NULL;
return g_value_get_boxed (value);
}
/**
* tp_asv_get_strv:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
*
* If a value for @key in @asv is present and is an array of strings (strv),
* return it.
*
* Otherwise return %NULL.
*
* The returned value is not copied, and is only valid as long as the value
* for @key in @asv is not removed or altered. Copy it with g_strdupv() if you
* need to keep it for longer.
*
* Returns: the %NULL-terminated string-array value of @key, or %NULL
* Since: 0.7.9
*/
const gchar * const *
tp_asv_get_strv (const GHashTable *asv,
const gchar *key)
{
GValue *value;
g_return_val_if_fail (asv != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
value = g_hash_table_lookup ((GHashTable *) asv, key);
if (value == NULL || !G_VALUE_HOLDS (value, G_TYPE_STRV))
return NULL;
return g_value_get_boxed (value);
}
/**
* tp_asv_lookup:
* @asv: A GHashTable where the keys are strings and the values are GValues
* @key: The key to look up
*
* If a value for @key in @asv is present, return it. Otherwise return %NULL.
*
* The returned value is not copied, and is only valid as long as the value
* for @key in @asv is not removed or altered. Copy it with (for instance)
* g_value_copy() if you need to keep it for longer.
*
* Returns: the value of @key, or %NULL
* Since: 0.7.9
*/
const GValue *
tp_asv_lookup (const GHashTable *asv,
const gchar *key)
{
g_return_val_if_fail (asv != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
return g_hash_table_lookup ((GHashTable *) asv, key);
}