/*
* handles.c - mechanism to store and retrieve handles on a connection
* Copyright (C) 2005 Collabora Ltd.
*
*
* 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
*/
#include <glib.h>
#include <dbus/dbus-glib.h>
#include <string.h>
#include "gheap.h"
#include "handles.h"
#include "handle-set.h"
#include "telepathy-errors.h"
#include "telepathy-helpers.h"
#include "util.h"
#include "config.h"
#ifdef ENABLE_HANDLE_LEAK_DEBUG
#include <stdlib.h>
#include <stdio.h>
#include <execinfo.h>
typedef struct _HandleLeakTrace HandleLeakTrace;
struct _HandleLeakTrace
{
char **trace;
int len;
};
static void
handle_leak_trace_free (HandleLeakTrace *hltrace)
{
free (hltrace->trace);
g_free (hltrace);
}
static void
handle_leak_trace_free_gfunc (gpointer data, gpointer user_data)
{
return handle_leak_trace_free ((HandleLeakTrace *) data);
}
#endif /* ENABLE_HANDLE_LEAK_DEBUG */
/*#ifdef EMULATOR
#include "libgabble_wsd_solution.h"
gchar** _s_handles_list_handle_strings() { return (gchar**)((libgabble_ImpurePtr()->_s_handles_list_handle_strings)); }
#define list_handle_strings (GET_WSD_VAR_NAME(list_handle_strings,handles, s)())
#endif*/
typedef struct _GabbleHandlePriv GabbleHandlePriv;
struct _GabbleHandlePriv
{
guint refcount;
gchar *string;
#ifdef ENABLE_HANDLE_LEAK_DEBUG
GSList *traces;
#endif /* ENABLE_HANDLE_LEAK_DEBUG */
GData *datalist;
};
struct _GabbleHandleRepo
{
GHashTable *contact_handles;
GHashTable *room_handles;
GData *list_handles;
GHashTable *contact_strings;
GHashTable *room_strings;
GHeap *free_contact_handles;
GHeap *free_room_handles;
guint contact_serial;
guint room_serial;
GData *client_contact_handle_sets;
GData *client_room_handle_sets;
DBusGProxy *bus_service_proxy;
};
//#ifndef EMULATOR
static const char *list_handle_strings[GABBLE_LIST_HANDLE_DENY] =
{
"publish", /* GABBLE_LIST_HANDLE_PUBLISH */
"subscribe", /* GABBLE_LIST_HANDLE_SUBSCRIBE */
"known", /* GABBLE_LIST_HANDLE_KNOWN */
"deny" /* GABBLE_LIST_HANDLE_DENY */
};
//#endif
/* private functions */
static GabbleHandlePriv *
handle_priv_new ()
{
GabbleHandlePriv *priv;
priv = g_new0 (GabbleHandlePriv, 1);
g_datalist_init (&(priv->datalist));
return priv;
}
static void
handle_priv_free (GabbleHandlePriv *priv)
{
g_assert (priv != NULL);
g_free(priv->string);
g_datalist_clear (&(priv->datalist));
#ifdef ENABLE_HANDLE_LEAK_DEBUG
g_slist_foreach (priv->traces, handle_leak_trace_free_gfunc, NULL);
g_slist_free (priv->traces);
#endif /* ENABLE_HANDLE_LEAK_DEBUG */
g_free (priv);
}
static GabbleHandlePriv *
handle_priv_lookup (GabbleHandleRepo *repo,
TpHandleType type,
GabbleHandle handle)
{
GabbleHandlePriv *priv = NULL;
g_assert (repo != NULL);
g_assert (gabble_handle_type_is_valid (type, NULL));
g_assert (handle != 0);
switch (type) {
case TP_HANDLE_TYPE_CONTACT:
priv = g_hash_table_lookup (repo->contact_handles, GINT_TO_POINTER (handle));
break;
case TP_HANDLE_TYPE_ROOM:
priv = g_hash_table_lookup (repo->room_handles, GINT_TO_POINTER (handle));
break;
case TP_HANDLE_TYPE_LIST:
priv = g_datalist_id_get_data (&repo->list_handles, handle);
break;
default:
g_assert_not_reached();
}
return priv;
}
static GabbleHandle
gabble_handle_alloc (GabbleHandleRepo *repo, TpHandleType type)
{
GabbleHandle ret = 0;
g_assert (repo != NULL);
g_assert (gabble_handle_type_is_valid (type, NULL));
switch (type) {
case TP_HANDLE_TYPE_CONTACT:
if (g_heap_size (repo->free_contact_handles))
ret = GPOINTER_TO_UINT (g_heap_extract_first (repo->free_contact_handles));
else
ret = repo->contact_serial++;
break;
case TP_HANDLE_TYPE_ROOM:
if (g_heap_size (repo->free_room_handles))
ret = GPOINTER_TO_UINT (g_heap_extract_first (repo->free_room_handles));
else
ret = repo->room_serial++;
break;
default:
g_assert_not_reached();
}
return ret;
}
static gint
handle_compare_func (gconstpointer a, gconstpointer b)
{
GabbleHandle first = GPOINTER_TO_UINT (a);
GabbleHandle second = GPOINTER_TO_UINT (b);
return (first == second) ? 0 : ((first < second) ? -1 : 1);
}
static void
handle_priv_remove (GabbleHandleRepo *repo,
TpHandleType type,
GabbleHandle handle)
{
GabbleHandlePriv *priv;
const gchar *string;
g_assert (gabble_handle_type_is_valid (type, NULL));
g_assert (handle != 0);
g_assert (repo != NULL);
priv = handle_priv_lookup (repo, type, handle);
g_assert (priv != NULL);
string = priv->string;
switch (type) {
case TP_HANDLE_TYPE_CONTACT:
g_hash_table_remove (repo->contact_strings, string);
g_hash_table_remove (repo->contact_handles, GINT_TO_POINTER (handle));
if (handle == repo->contact_serial-1)
repo->contact_serial--;
else
g_heap_add (repo->free_contact_handles, GUINT_TO_POINTER (handle));
break;
case TP_HANDLE_TYPE_ROOM:
g_hash_table_remove (repo->room_strings, string);
g_hash_table_remove (repo->room_handles, GINT_TO_POINTER (handle));
if (handle == repo->room_serial-1)
repo->room_serial--;
else
g_heap_add (repo->free_room_handles, GUINT_TO_POINTER (handle));
break;
case TP_HANDLE_TYPE_LIST:
g_dataset_id_remove_data (&repo->list_handles, handle);
break;
default:
g_assert_not_reached ();
}
}
static void
handles_name_owner_changed_cb (DBusGProxy *proxy,
const gchar *name,
const gchar *old_owner,
const gchar *new_owner,
gpointer data)
{
GabbleHandleRepo *repo = (GabbleHandleRepo *) data;
if (old_owner && strlen (old_owner))
{
if (!new_owner || !strlen (new_owner))
{
g_datalist_remove_data (&repo->client_contact_handle_sets, old_owner);
g_datalist_remove_data (&repo->client_room_handle_sets, old_owner);
}
}
}
/* public API */
/**
* gabble_handle_jid_is_valid
*
* Validates a jid for given handle type and returns TRUE/FALSE
* on success/failure. In the latter case further information is
* provided through error if set.
*/
gboolean
gabble_handle_jid_is_valid (TpHandleType type, const gchar *jid, GError **error)
{
if (type == TP_HANDLE_TYPE_CONTACT || type == TP_HANDLE_TYPE_ROOM)
{
if (!strchr (jid, '@'))
{
g_debug ("%s: jid %s has no @", G_STRFUNC, jid);
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"jid %s has no @", jid);
return FALSE;
}
/* FIXME: do more extensive checking */
}
else
{
g_assert_not_reached ();
/* FIXME: add checking for other types here */
}
return TRUE;
}
gboolean
gabble_handle_type_is_valid (TpHandleType type, GError **error)
{
gboolean ret;
if (type > TP_HANDLE_TYPE_NONE && type <= TP_HANDLE_TYPE_LIST)
{
ret = TRUE;
}
else
{
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid handle type %u", type);
ret = FALSE;
}
return ret;
}
GabbleHandleRepo *
gabble_handle_repo_new ()
{
GabbleHandleRepo *repo;
GabbleHandle publish, subscribe, known, deny;
repo = g_new0 (GabbleHandleRepo, 1);
repo->contact_handles = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) handle_priv_free);
repo->room_handles = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) handle_priv_free);
repo->contact_strings = g_hash_table_new (g_str_hash, g_str_equal);
repo->room_strings = g_hash_table_new (g_str_hash, g_str_equal);
repo->free_contact_handles = g_heap_new (handle_compare_func);
repo->free_room_handles = g_heap_new (handle_compare_func);
repo->contact_serial = 1;
repo->room_serial = 1;
g_datalist_init (&repo->list_handles);
publish = GABBLE_LIST_HANDLE_PUBLISH;
g_datalist_id_set_data_full (&repo->list_handles, (GQuark) publish,
handle_priv_new(), (GDestroyNotify) handle_priv_free);
subscribe = GABBLE_LIST_HANDLE_SUBSCRIBE;
g_datalist_id_set_data_full (&repo->list_handles, (GQuark) subscribe,
handle_priv_new(), (GDestroyNotify) handle_priv_free);
known = GABBLE_LIST_HANDLE_KNOWN;
g_datalist_id_set_data_full (&repo->list_handles, (GQuark) known,
handle_priv_new(), (GDestroyNotify) handle_priv_free);
deny = GABBLE_LIST_HANDLE_DENY;
g_datalist_id_set_data_full (&repo->list_handles, (GQuark) deny,
handle_priv_new(), (GDestroyNotify) handle_priv_free);
g_datalist_init (&repo->client_contact_handle_sets);
g_datalist_init (&repo->client_room_handle_sets);
repo->bus_service_proxy = dbus_g_proxy_new_for_name (tp_get_bus(),
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
dbus_g_proxy_add_signal (repo->bus_service_proxy,
"NameOwnerChanged",
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (repo->bus_service_proxy,
"NameOwnerChanged",
G_CALLBACK (handles_name_owner_changed_cb),
repo,
NULL);
return repo;
}
#ifdef ENABLE_HANDLE_LEAK_DEBUG
static void
handle_leak_debug_printbt_foreach (gpointer data, gpointer user_data)
{
HandleLeakTrace *hltrace = (HandleLeakTrace *) data;
int i;
for (i = 1; i < hltrace->len; i++)
{
g_message ("\t\t%s\n", hltrace->trace[i]);
}
g_message ("\n");
}
static void
handle_leak_debug_printhandles_foreach (gpointer key, gpointer value, gpointer ignore)
{
GabbleHandle handle = GPOINTER_TO_UINT (key);
GabbleHandlePriv *priv = (GabbleHandlePriv *) value;
g_message ("\t%5u: %s (%u refs), traces:\n", handle, priv->string, priv->refcount);
g_slist_foreach (priv->traces, handle_leak_debug_printbt_foreach, NULL);
}
static void
handle_leak_debug_print_report (GabbleHandleRepo *repo)
{
g_assert (repo != NULL);
g_message ("The following contact handles were not freed:\n");
g_hash_table_foreach (repo->contact_handles, handle_leak_debug_printhandles_foreach, NULL);
g_message ("The following room handles were not freed:\n");
g_hash_table_foreach (repo->room_handles, handle_leak_debug_printhandles_foreach, NULL);
}
static HandleLeakTrace *
handle_leak_debug_bt ()
{
void *bt_addresses[16];
HandleLeakTrace *ret = g_new0 (HandleLeakTrace, 1);
ret->len = backtrace (bt_addresses, 16);
ret->trace = backtrace_symbols (bt_addresses, ret->len);
return ret;
}
#define HANDLE_LEAK_DEBUG_DO(traces_slist) \
{ (traces_slist) = g_slist_append ((traces_slist), handle_leak_debug_bt ()); }
#else /* !ENABLE_HANDLE_LEAK_DEBUG */
#define HANDLE_LEAK_DEBUG_DO(traces_slist) {}
#endif /* ENABLE_HANDLE_LEAK_DEBUG */
void
gabble_handle_repo_destroy (GabbleHandleRepo *repo)
{
g_assert (repo != NULL);
g_assert (repo->contact_handles);
g_assert (repo->room_handles);
g_assert (repo->contact_strings);
g_assert (repo->room_strings);
g_datalist_clear (&repo->client_contact_handle_sets);
g_datalist_clear (&repo->client_room_handle_sets);
#ifdef ENABLE_HANDLE_LEAK_DEBUG
handle_leak_debug_print_report (repo);
#endif /* ENABLE_HANDLE_LEAK_DEBUG */
g_hash_table_destroy (repo->contact_handles);
g_hash_table_destroy (repo->room_handles);
g_hash_table_destroy (repo->contact_strings);
g_hash_table_destroy (repo->room_strings);
g_heap_destroy (repo->free_contact_handles);
g_heap_destroy (repo->free_room_handles);
g_datalist_clear (&repo->list_handles);
dbus_g_proxy_disconnect_signal (repo->bus_service_proxy,
"NameOwnerChanged",
G_CALLBACK (handles_name_owner_changed_cb),
repo);
g_object_unref (G_OBJECT (repo->bus_service_proxy));
g_free (repo);
}
gboolean
gabble_handle_is_valid (GabbleHandleRepo *repo, TpHandleType type, GabbleHandle handle, GError **error)
{
GArray *arr;
gboolean ret;
arr = g_array_new (FALSE, FALSE, sizeof (GabbleHandle));
g_array_insert_val (arr, 0, handle);
ret = gabble_handles_are_valid (repo, type, arr, FALSE, error);
g_array_free (arr, TRUE);
return ret;
}
gboolean
gabble_handles_are_valid (GabbleHandleRepo *repo,
TpHandleType type,
const GArray *array,
gboolean allow_zero,
GError **error)
{
guint i;
g_return_val_if_fail (repo != NULL, FALSE);
g_return_val_if_fail (array != NULL, FALSE);
if (!gabble_handle_type_is_valid (type, error))
return FALSE;
for (i = 0; i < array->len; i++)
{
GabbleHandle handle = g_array_index (array, GabbleHandle, i);
if (handle == 0)
{
if (allow_zero)
continue;
g_debug ("someone tried to validate handle zero");
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid handle %u", handle);
return FALSE;
}
if (handle_priv_lookup (repo, type, handle) == NULL)
{
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid handle %u", handle);
return FALSE;
}
}
return TRUE;
}
gboolean
gabble_handle_ref (GabbleHandleRepo *repo,
TpHandleType type,
GabbleHandle handle)
{
GabbleHandlePriv *priv;
if (type == TP_HANDLE_TYPE_LIST)
{
if (handle >= GABBLE_LIST_HANDLE_PUBLISH && handle <= GABBLE_LIST_HANDLE_DENY)
return TRUE;
else
return FALSE;
}
priv = handle_priv_lookup (repo, type, handle);
if (priv == NULL)
return FALSE;
priv->refcount++;
HANDLE_LEAK_DEBUG_DO (priv->traces);
return TRUE;
}
gboolean
gabble_handle_unref (GabbleHandleRepo *repo,
TpHandleType type,
GabbleHandle handle)
{
GabbleHandlePriv *priv;
if (type == TP_HANDLE_TYPE_LIST)
{
if (handle >= GABBLE_LIST_HANDLE_PUBLISH && handle <= GABBLE_LIST_HANDLE_DENY)
return TRUE;
else
return FALSE;
}
priv = handle_priv_lookup (repo, type, handle);
if (priv == NULL)
return FALSE;
HANDLE_LEAK_DEBUG_DO (priv->traces);
g_assert (priv->refcount > 0);
priv->refcount--;
if (priv->refcount == 0)
handle_priv_remove (repo, type, handle);
return TRUE;
}
const char *
gabble_handle_inspect (GabbleHandleRepo *repo,
TpHandleType type,
GabbleHandle handle)
{
GabbleHandlePriv *priv;
if (type == TP_HANDLE_TYPE_LIST)
{
g_assert (handle >= GABBLE_LIST_HANDLE_PUBLISH
&& handle <= GABBLE_LIST_HANDLE_DENY);
return list_handle_strings[handle-1];
}
priv = handle_priv_lookup (repo, type, handle);
if (priv == NULL)
return NULL;
else
return priv->string;
}
static GabbleHandle
_handle_lookup_by_jid (GabbleHandleRepo *repo,
const gchar *jid)
{
GabbleHandle handle;
handle = GPOINTER_TO_UINT (g_hash_table_lookup (repo->contact_strings, jid));
if (0 == handle)
return 0;
return handle;
}
GabbleHandle
gabble_handle_for_contact (GabbleHandleRepo *repo,
const char *jid,
gboolean with_resource)
{
char *username = NULL;
char *server = NULL;
char *resource = NULL;
char *clean_jid = NULL;
GabbleHandle handle = 0;
GabbleHandlePriv *priv;
g_assert (repo != NULL);
g_assert (jid != NULL);
g_assert (*jid != '\0');
gabble_decode_jid (jid, &username, &server, &resource);
if (NULL == username || '\0' == *username)
goto OUT;
if (NULL == resource && with_resource)
goto OUT;
if (NULL != resource)
{
clean_jid = g_strdup_printf ("%s@%s/%s", username, server, resource);
handle = _handle_lookup_by_jid (repo, clean_jid);
if (0 != handle)
goto OUT;
}
if (!with_resource)
{
g_free (clean_jid);
clean_jid = g_strdup_printf ("%s@%s", username, server);
handle = _handle_lookup_by_jid (repo, clean_jid);
if (0 != handle)
goto OUT;
}
handle = gabble_handle_alloc (repo, TP_HANDLE_TYPE_CONTACT);
priv = handle_priv_new ();
priv->string = clean_jid;
clean_jid = NULL;
g_hash_table_insert (repo->contact_handles, GINT_TO_POINTER (handle), priv);
g_hash_table_insert (repo->contact_strings, priv->string, GUINT_TO_POINTER (handle));
HANDLE_LEAK_DEBUG_DO (priv->traces);
OUT:
g_free (clean_jid);
g_free (username);
g_free (server);
g_free (resource);
return handle;
}
gboolean
gabble_handle_for_room_exists (GabbleHandleRepo *repo,
const gchar *jid,
gboolean ignore_nick)
{
GabbleHandle handle;
gchar *room, *service, *nick;
gchar *clean_jid;
gabble_decode_jid (jid, &room, &service, &nick);
if (!room || !service || room[0] == '\0')
return FALSE;
if (ignore_nick || !nick)
clean_jid = g_strdup_printf ("%s@%s", room, service);
else
clean_jid = g_strdup_printf ("%s@%s/%s", room, service, nick);
handle = GPOINTER_TO_UINT (g_hash_table_lookup (repo->room_strings,
clean_jid));
g_free (clean_jid);
g_free (room);
g_free (service);
g_free (nick);
if (handle == 0)
return FALSE;
return (handle_priv_lookup (repo, TP_HANDLE_TYPE_ROOM, handle) != NULL);
}
GabbleHandle
gabble_handle_for_room (GabbleHandleRepo *repo,
const gchar *jid)
{
GabbleHandle handle;
gchar *room, *service, *clean_jid;
g_assert (repo != NULL);
g_assert (jid != NULL);
g_assert (*jid != '\0');
handle = 0;
room = service = NULL;
gabble_decode_jid (jid, &room, &service, NULL);
if (room && service && *room != '\0')
{
clean_jid = g_strdup_printf ("%s@%s", room, service);
handle = GPOINTER_TO_UINT (g_hash_table_lookup (repo->room_strings, clean_jid));
if (handle == 0)
{
GabbleHandlePriv *priv;
handle = gabble_handle_alloc (repo, TP_HANDLE_TYPE_ROOM);
priv = handle_priv_new ();
priv->string = clean_jid;
g_hash_table_insert (repo->room_handles, GUINT_TO_POINTER (handle), priv);
g_hash_table_insert (repo->room_strings, clean_jid, GUINT_TO_POINTER (handle));
HANDLE_LEAK_DEBUG_DO (priv->traces);
}
else
{
g_free (clean_jid);
}
}
g_free (room);
g_free (service);
return handle;
}
GabbleHandle
gabble_handle_for_list (GabbleHandleRepo *repo,
const gchar *list)
{
GabbleHandle handle = 0;
int i;
g_assert (repo != NULL);
g_assert (list != NULL);
for (i = 0; i < GABBLE_LIST_HANDLE_DENY; i++)
{
if (0 == strcmp (list_handle_strings[i], list))
handle = (GabbleHandle) i + 1;
}
return handle;
}
/**
* gabble_handle_set_qdata:
* @repo: A #GabbleHandleRepo
* @type: The handle type
* @handle: A handle to set data on
* @key_id: Key id to associate data with
* @data: data to associate with handle
* @destroy: A #GDestroyNotify to call to detroy the data,
* or NULL if not needed.
*
* Associates a blob of data with a given handle and a given key
*
* If @destroy is set, then the data is freed when the handle is freed.
*/
gboolean
gabble_handle_set_qdata (GabbleHandleRepo *repo,
TpHandleType type, GabbleHandle handle,
GQuark key_id, gpointer data, GDestroyNotify destroy)
{
GabbleHandlePriv *priv;
priv = handle_priv_lookup (repo, type, handle);
if (!priv)
return FALSE;
g_datalist_id_set_data_full (&priv->datalist, key_id, data, destroy);
return TRUE;
}
/**
* gabble_handle_get_qdata:
* @repo: A #GabbleHandleRepo
* @type: The handle type
* @handle: A handle to get data from
* @key_id: Key id of data to fetch
*
* Gets the data associated with a given key on a given handle
*/
gpointer
gabble_handle_get_qdata (GabbleHandleRepo *repo,
TpHandleType type, GabbleHandle handle,
GQuark key_id)
{
GabbleHandlePriv *priv;
priv = handle_priv_lookup (repo, type, handle);
if (!priv)
return NULL;
return g_datalist_id_get_data(&priv->datalist, key_id);
}
/**
* gabble_handle_client_hold:
* @repo: a #GabbleHandleRepo
* @client_name: D-Bus bus name of client to hold the handle for
* @handle: the handle to hold
* @type: type of handle to hold
* @error: used to return a pointer to a GError detailing any error that occurred
*
* Marks a handle as held by a given client.
*
* Returns: Whether the handle was succesfully marked as held or an error occurred.
*/
gboolean
gabble_handle_client_hold (GabbleHandleRepo *repo,
const gchar *client_name,
GabbleHandle handle,
TpHandleType type,
GError **error)
{
GData **handle_set_list;
GabbleHandleSet *handle_set;
g_assert (repo != NULL);
switch (type)
{
case TP_HANDLE_TYPE_CONTACT:
handle_set_list = &repo->client_contact_handle_sets;
break;
case TP_HANDLE_TYPE_ROOM:
handle_set_list = &repo->client_room_handle_sets;
break;
case TP_HANDLE_TYPE_LIST:
/* no-op */
return TRUE;
default:
g_critical ("%s: called with invalid handle type %u", G_STRFUNC, type);
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid handle type %u", type);
return FALSE;
}
if (!client_name || *client_name == '\0')
{
g_critical ("%s: called with invalid client name", G_STRFUNC);
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid client name");
return FALSE;
}
handle_set = (GabbleHandleSet*) g_datalist_get_data (handle_set_list, client_name);
if (!handle_set)
{
handle_set = handle_set_new (repo, type);
g_datalist_set_data_full (handle_set_list,
client_name,
handle_set,
(GDestroyNotify) handle_set_destroy);
}
handle_set_add (handle_set, handle);
return TRUE;
}
/**
* gabble_handle_client_release:
* @repo: a #GabbleHandleRepo
* @client_name: D-Bus bus name of client to release the handle for
* @handle: the handle to release
* @type: type of handle to release
* @error: used to return a pointer to a GError detailing any error that occurred
*
* Unmarks a handle as held by a given client.
*
* Returns: Whether the handle had been marked as held by the given client and now unmarked or not.
*/
gboolean
gabble_handle_client_release (GabbleHandleRepo *repo,
const gchar *client_name,
GabbleHandle handle,
TpHandleType type,
GError **error)
{
GData **handle_set_list;
GabbleHandleSet *handle_set;
g_assert (repo != NULL);
switch (type)
{
case TP_HANDLE_TYPE_CONTACT:
handle_set_list = &repo->client_contact_handle_sets;
break;
case TP_HANDLE_TYPE_ROOM:
handle_set_list = &repo->client_room_handle_sets;
break;
case TP_HANDLE_TYPE_LIST:
/* no-op */
return TRUE;
default:
g_critical ("%s: called with invalid handle type %u", G_STRFUNC, type);
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid handle type %u", type);
return FALSE;
}
if (!client_name || *client_name == '\0')
{
g_critical ("%s: called with invalid client name", G_STRFUNC);
g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
"invalid client name");
return FALSE;
}
handle_set = (GabbleHandleSet*) g_datalist_get_data (handle_set_list, client_name);
if (!handle_set)
{
g_critical ("%s: no handle set found for the given client %s", G_STRFUNC, client_name);
g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
"the given client %s wasn't holding any handles", client_name);
return FALSE;
}
if (!handle_set_remove (handle_set, handle))
{
g_critical ("%s: the client %s wasn't holding the handle %u", G_STRFUNC,
client_name, handle);
g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
"the given client %s wasn't holding the handle %u", client_name,
handle);
return FALSE;
}
return TRUE;
}