diff -r 000000000000 -r d0f3a028347a telepathygabble/src/group-mixin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telepathygabble/src/group-mixin.c Tue Feb 02 01:10:06 2010 +0200 @@ -0,0 +1,785 @@ +/* + * group-mixin.c - Source for GabbleGroupMixin + * Copyright (C) 2006 Collabora Ltd. + * + * @author Ole Andre Vadla Ravnaas + * @author Robert McQueen + * + * 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 +#include + +#include "ansi.h" +#include "debug.h" +#include "telepathy-errors.h" + + +#include "group-mixin.h" +#include "group-mixin-signals-marshal.h" + +#define DEBUG_FLAG GABBLE_DEBUG_GROUPS +#ifdef DEBUG_FLAG +//#define DEBUG(format, ...) +#define DEBUGGING 0 +#define NODE_DEBUG(n, s) +#endif /* DEBUG_FLAG */ + +#ifdef EMULATOR +#include "libgabble_wsd_solution.h" + + GET_STATIC_VAR_FROM_TLS(offset_quark1,gabble_grp_mixin,GQuark) + #define offset_quark1 (*GET_WSD_VAR_NAME(offset_quark1,gabble_grp_mixin, s)()) + + GET_STATIC_VAR_FROM_TLS(offset_quark,gabble_grp_mixin,GQuark) + #define offset_quark (*GET_WSD_VAR_NAME(offset_quark,gabble_grp_mixin, s)()) + + +#endif + +static const char *group_change_reason_str(guint reason) +{ + switch (reason) + { + case TP_CHANNEL_GROUP_CHANGE_REASON_NONE: + return "unspecified reason"; + case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE: + return "offline"; + case TP_CHANNEL_GROUP_CHANGE_REASON_KICKED: + return "kicked"; + case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY: + return "busy"; + case TP_CHANNEL_GROUP_CHANGE_REASON_INVITED: + return "invited"; + case TP_CHANNEL_GROUP_CHANGE_REASON_BANNED: + return "banned"; + default: + return "(unknown reason code)"; + } +} + +struct _GabbleGroupMixinPrivate { + GabbleHandleSet *actors; + GHashTable *handle_owners; +}; + + +/** + * gabble_group_mixin_class_get_offset_quark: + * + * Returns: the quark used for storing mixin offset on a GObjectClass + */ +GQuark +gabble_group_mixin_class_get_offset_quark () +{ +#ifndef EMULATOR + static GQuark offset_quark1 = 0; +#endif + + if (!offset_quark1) + offset_quark1 = g_quark_from_static_string("GroupMixinClassOffsetQuark"); + return offset_quark1; +} + +/** + * gabble_group_mixin_get_offset_quark: + * + * Returns: the quark used for storing mixin offset on a GObject + */ +GQuark +gabble_group_mixin_get_offset_quark () +{ +#ifndef EMULATOR + static GQuark offset_quark = 0; +#endif + + if (!offset_quark) + offset_quark = g_quark_from_static_string("GroupMixinOffsetQuark"); + return offset_quark; +} + +void gabble_group_mixin_class_init (GObjectClass *obj_cls, + glong offset, + GabbleGroupMixinAddMemberFunc add_func, + GabbleGroupMixinRemMemberFunc rem_func) +{ + GabbleGroupMixinClass *mixin_cls; + + g_assert (G_IS_OBJECT_CLASS (obj_cls)); + + g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls), + GABBLE_GROUP_MIXIN_CLASS_OFFSET_QUARK, + GINT_TO_POINTER (offset)); + + mixin_cls = GABBLE_GROUP_MIXIN_CLASS (obj_cls); + + mixin_cls->add_member = add_func; + mixin_cls->remove_member = rem_func; + + mixin_cls->group_flags_changed_signal_id = + g_signal_new ("group-flags-changed", + G_OBJECT_CLASS_TYPE (obj_cls), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + group_mixin_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + mixin_cls->members_changed_signal_id = + g_signal_new ("members-changed", + G_OBJECT_CLASS_TYPE (obj_cls), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + group_mixin_marshal_VOID__STRING_BOXED_BOXED_BOXED_BOXED_UINT_UINT, + G_TYPE_NONE, 7, G_TYPE_STRING, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, G_TYPE_UINT, G_TYPE_UINT); +} + +void gabble_group_mixin_init (GObject *obj, + glong offset, + GabbleHandleRepo *handle_repo, + GabbleHandle self_handle) +{ + GabbleGroupMixin *mixin; + + g_assert (G_IS_OBJECT (obj)); + + g_type_set_qdata (G_OBJECT_TYPE (obj), + GABBLE_GROUP_MIXIN_OFFSET_QUARK, + GINT_TO_POINTER (offset)); + + mixin = GABBLE_GROUP_MIXIN (obj); + + mixin->handle_repo = handle_repo; + mixin->self_handle = self_handle; + + mixin->group_flags = 0; + + mixin->members = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT); + mixin->local_pending = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT); + mixin->remote_pending = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT); + + mixin->priv = g_new0 (GabbleGroupMixinPrivate, 1); + mixin->priv->handle_owners = g_hash_table_new (g_direct_hash, g_direct_equal); + mixin->priv->actors = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT); +} + +static void +handle_owners_foreach_unref (gpointer key, + gpointer value, + gpointer user_data) +{ + GabbleGroupMixin *mixin = user_data; + + gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + GPOINTER_TO_UINT (key)); + gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + GPOINTER_TO_UINT (value)); +} + +void gabble_group_mixin_finalize (GObject *obj) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + handle_set_destroy (mixin->priv->actors); + + g_hash_table_foreach (mixin->priv->handle_owners, + handle_owners_foreach_unref, + mixin); + + g_hash_table_destroy (mixin->priv->handle_owners); + + g_free (mixin->priv); + + handle_set_destroy (mixin->members); + handle_set_destroy (mixin->local_pending); + handle_set_destroy (mixin->remote_pending); +} + +gboolean +gabble_group_mixin_get_self_handle (GObject *obj, guint *ret, GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + if (handle_set_is_member (mixin->members, mixin->self_handle) || + handle_set_is_member (mixin->local_pending, mixin->self_handle) || + handle_set_is_member (mixin->remote_pending, mixin->self_handle)) + { + *ret = mixin->self_handle; + } + else + { + *ret = 0; + } + + return TRUE; +} + +gboolean +gabble_group_mixin_get_group_flags (GObject *obj, guint *ret, GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + *ret = mixin->group_flags; + + return TRUE; +} + +gboolean +gabble_group_mixin_add_members (GObject *obj, const GArray *contacts, const gchar *message, GError **error) +{ + GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj)); + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + guint i; + GabbleHandle handle; + + /* reject invalid handles */ + if (!gabble_handles_are_valid (mixin->handle_repo, + TP_HANDLE_TYPE_CONTACT, + contacts, + FALSE, + error)) + return FALSE; + + /* check that adding is allowed by flags */ + for (i = 0; i < contacts->len; i++) + { + handle = g_array_index (contacts, GabbleHandle, i); + + if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_ADD) == 0 && + !handle_set_is_member (mixin->local_pending, handle)) + { + gabble_debug (DEBUG_FLAG, "handle %u cannot be added to members without GROUP_FLAG_CAN_ADD", + handle); + + g_set_error (error, TELEPATHY_ERRORS, PermissionDenied, + "handle %u cannot be added to members without GROUP_FLAG_CAN_ADD", + handle); + + return FALSE; + } + } + + /* add handle by handle */ + for (i = 0; i < contacts->len; i++) + { + handle = g_array_index (contacts, GabbleHandle, i); + + if (handle_set_is_member (mixin->members, handle)) + { + gabble_debug (DEBUG_FLAG, "handle %u is already a member, skipping", handle); + + continue; + } + + if (!mixin_cls->add_member (obj, handle, message, error)) + { + return FALSE; + } + } + + return TRUE; +} + +gboolean +gabble_group_mixin_remove_members (GObject *obj, const GArray *contacts, const gchar *message, GError **error) +{ + GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj)); + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + guint i; + GabbleHandle handle; + + /* reject invalid handles */ + if (!gabble_handles_are_valid (mixin->handle_repo, + TP_HANDLE_TYPE_CONTACT, + contacts, + FALSE, + error)) + return FALSE; + + /* check removing is allowed by flags */ + for (i = 0; i < contacts->len; i++) + { + handle = g_array_index (contacts, GabbleHandle, i); + + if (handle_set_is_member (mixin->members, handle)) + { + if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_REMOVE) == 0) + { + gabble_debug (DEBUG_FLAG, "handle %u cannot be removed from members without GROUP_FLAG_CAN_REMOVE", + handle); + + g_set_error (error, TELEPATHY_ERRORS, PermissionDenied, + "handle %u cannot be removed from members without GROUP_FLAG_CAN_REMOVE", + handle); + + return FALSE; + } + } + else if (handle_set_is_member (mixin->remote_pending, handle)) + { + if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_RESCIND) == 0) + { + gabble_debug (DEBUG_FLAG, "handle %u cannot be removed from remote pending without GROUP_FLAG_CAN_RESCIND", + handle); + + g_set_error (error, TELEPATHY_ERRORS, PermissionDenied, + "handle %u cannot be removed from remote pending without GROUP_FLAG_CAN_RESCIND", + handle); + + return FALSE; + } + } + else if (!handle_set_is_member (mixin->local_pending, handle)) + { + gabble_debug (DEBUG_FLAG, "handle %u is not a current or pending member", + handle); + + g_set_error (error, TELEPATHY_ERRORS, NotAvailable, + "handle %u is not a current or pending member", handle); + + return FALSE; + } + } + + /* remove handle by handle */ + for (i = 0; i < contacts->len; i++) + { + handle = g_array_index (contacts, GabbleHandle, i); + + if (!mixin_cls->remove_member (obj, handle, message, error)) + { + return FALSE; + } + } + + return TRUE; +} + +gboolean +gabble_group_mixin_get_members (GObject *obj, GArray **ret, GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + *ret = handle_set_to_array (mixin->members); + + return TRUE; +} + +gboolean +gabble_group_mixin_get_local_pending_members (GObject *obj, GArray **ret, GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + *ret = handle_set_to_array (mixin->local_pending); + + return TRUE; +} + +gboolean +gabble_group_mixin_get_remote_pending_members (GObject *obj, GArray **ret, GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + *ret = handle_set_to_array (mixin->remote_pending); + + return TRUE; +} + +gboolean +gabble_group_mixin_get_all_members (GObject *obj, GArray **ret, GArray **ret1, GArray **ret2, GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + + *ret = handle_set_to_array (mixin->members); + *ret1 = handle_set_to_array (mixin->local_pending); + *ret2 = handle_set_to_array (mixin->remote_pending); + + return TRUE; +} + +gboolean +gabble_group_mixin_get_handle_owners (GObject *obj, + const GArray *handles, + GArray **ret, + GError **error) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + GabbleGroupMixinPrivate *priv = mixin->priv; + guint i; + + if ((mixin->group_flags & + TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES) == 0) + { + g_set_error (error, TELEPATHY_ERRORS, NotAvailable, + "channel doesn't have channel specific handles"); + + return FALSE; + } + + if (!gabble_handles_are_valid (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + handles, FALSE, error)) + { + return FALSE; + } + + *ret = g_array_sized_new (FALSE, FALSE, sizeof (GabbleHandle), handles->len); + + for (i = 0; i < handles->len; i++) + { + GabbleHandle local_handle = g_array_index (handles, GabbleHandle, i); + GabbleHandle owner_handle; + + if (!handle_set_is_member (mixin->members, local_handle)) + { + g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, + "handle %u is not a member", local_handle); + + g_array_free (*ret, TRUE); + *ret = NULL; + + return FALSE; + } + + owner_handle = GPOINTER_TO_UINT ( + g_hash_table_lookup (priv->handle_owners, + GUINT_TO_POINTER (local_handle))); + + g_array_append_val (*ret, owner_handle); + } + + return TRUE; +} + +#define GFTS_APPEND_FLAG_IF_SET(flag) \ + if (flags & flag) \ + { \ + if (i++ > 0) \ + g_string_append (str, "|"); \ + g_string_append (str, #flag + 22); \ + } + +static gchar * +group_flags_to_string (TpChannelGroupFlags flags) +{ + gint i = 0; + GString *str; + + str = g_string_new ("[" ANSI_BOLD_OFF); + + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_ADD); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_REMOVE); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_ACCEPT); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_REJECT); + GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND); + + g_string_append (str, ANSI_BOLD_ON "]"); + + return g_string_free (str, FALSE); +} + +/** + * gabble_group_mixin_change_flags: + * + * Request a change to be made to the flags. Emits the + * signal with the changes which were made. + */ +void +gabble_group_mixin_change_flags (GObject *obj, + TpChannelGroupFlags add, + TpChannelGroupFlags remove) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj)); + TpChannelGroupFlags added, removed; + + added = add & ~mixin->group_flags; + mixin->group_flags |= added; + + removed = remove & mixin->group_flags; + mixin->group_flags &= ~removed; + + if (add != 0 || remove != 0) + { + gchar *str_added, *str_removed, *str_flags; + + if (DEBUGGING) + { + str_added = group_flags_to_string (added); + str_removed = group_flags_to_string (removed); + str_flags = group_flags_to_string (mixin->group_flags); + + g_message (ANSI_BOLD_ON ANSI_FG_WHITE + "%s: emitting group flags changed\n" + " added : %s\n" + " removed : %s\n" + " flags now: %s\n" ANSI_RESET, + G_STRFUNC, str_added, str_removed, str_flags); + + fflush (stdout); + + g_free (str_added); + g_free (str_removed); + g_free (str_flags); + } + + g_signal_emit(obj, mixin_cls->group_flags_changed_signal_id, 0, added, removed); + } +} + +static gchar * +member_array_to_string (GabbleHandleRepo *repo, const GArray *array) +{ + GString *str; + guint i; + + str = g_string_new ("[" ANSI_BOLD_OFF); + + for (i = 0; i < array->len; i++) + { + GabbleHandle handle; + const gchar *handle_str; + + handle = g_array_index (array, guint32, i); + handle_str = gabble_handle_inspect (repo, TP_HANDLE_TYPE_CONTACT, handle); + + g_string_append_printf (str, "%s%u (%s)", + (i > 0) ? "\n " : "", + handle, handle_str); + } + + g_string_append (str, ANSI_BOLD_ON "]"); + + return g_string_free (str, FALSE); +} + +static void remove_handle_owners_if_exist (GObject *obj, GArray *array); + +/** + * gabble_group_mixin_change_members: + * + * Request members to be added, removed or marked as local or remote pending. + * Changes member sets, references, and emits the MembersChanged signal. + */ +gboolean +gabble_group_mixin_change_members (GObject *obj, + const gchar *message, + GIntSet *add, + GIntSet *remove, + GIntSet *local_pending, + GIntSet *remote_pending, + GabbleHandle actor, + guint reason) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj)); + GIntSet *new_add, *new_remove, *new_local_pending, + *new_remote_pending, *tmp, *tmp2, *empty; + gboolean ret; + + empty = g_intset_new (); + + if (add == NULL) + add = empty; + + if (remove == NULL) + remove = empty; + + if (local_pending == NULL) + local_pending = empty; + + if (remote_pending == NULL) + remote_pending = empty; + + /* members + add */ + new_add = handle_set_update (mixin->members, add); + + /* members - remove */ + new_remove = handle_set_difference_update (mixin->members, remove); + + /* members - local_pending */ + tmp = handle_set_difference_update (mixin->members, local_pending); + g_intset_destroy (tmp); + + /* members - remote_pending */ + tmp = handle_set_difference_update (mixin->members, remote_pending); + g_intset_destroy (tmp); + + + /* local pending + local_pending */ + new_local_pending = handle_set_update (mixin->local_pending, local_pending); + + /* local pending - add */ + tmp = handle_set_difference_update (mixin->local_pending, add); + g_intset_destroy (tmp); + + /* local pending - remove */ + tmp = handle_set_difference_update (mixin->local_pending, remove); + tmp2 = g_intset_union (new_remove, tmp); + g_intset_destroy (new_remove); + g_intset_destroy (tmp); + new_remove = tmp2; + + /* local pending - remote_pending */ + tmp = handle_set_difference_update (mixin->local_pending, remote_pending); + g_intset_destroy (tmp); + + + /* remote pending + remote_pending */ + new_remote_pending = handle_set_update (mixin->remote_pending, remote_pending); + + /* remote pending - add */ + tmp = handle_set_difference_update (mixin->remote_pending, add); + g_intset_destroy (tmp); + + /* remote pending - remove */ + tmp = handle_set_difference_update (mixin->remote_pending, remove); + tmp2 = g_intset_union (new_remove, tmp); + g_intset_destroy (new_remove); + g_intset_destroy (tmp); + new_remove = tmp2; + + /* remote pending - local_pending */ + tmp = handle_set_difference_update (mixin->remote_pending, local_pending); + g_intset_destroy (tmp); + + if (g_intset_size (new_add) > 0 || + g_intset_size (new_remove) > 0 || + g_intset_size (new_local_pending) > 0 || + g_intset_size (new_remote_pending) > 0) + { + GArray *arr_add, *arr_remove, *arr_local, *arr_remote; + gchar *add_str, *rem_str, *local_str, *remote_str; + + /* translate intsets to arrays */ + arr_add = g_intset_to_array (new_add); + arr_remove = g_intset_to_array (new_remove); + arr_local = g_intset_to_array (new_local_pending); + arr_remote = g_intset_to_array (new_remote_pending); + + /* remove any handle owner mappings */ + remove_handle_owners_if_exist (obj, arr_remove); + + if (DEBUGGING) + { + add_str = member_array_to_string (mixin->handle_repo, arr_add); + rem_str = member_array_to_string (mixin->handle_repo, arr_remove); + local_str = member_array_to_string (mixin->handle_repo, arr_local); + remote_str = member_array_to_string (mixin->handle_repo, arr_remote); + + g_message (ANSI_BOLD_ON ANSI_FG_CYAN + "%s: emitting members changed\n" + " message : \"%s\"\n" + " added : %s\n" + " removed : %s\n" + " local_pending : %s\n" + " remote_pending: %s\n" + " actor : %u\n" + " reason : %u: %s\n" ANSI_RESET, + G_STRFUNC, message, add_str, rem_str, local_str, remote_str, + actor, reason, group_change_reason_str(reason)); + + fflush (stdout); + + g_free (add_str); + g_free (rem_str); + g_free (local_str); + g_free (remote_str); + } + + if (actor) + { + handle_set_add (mixin->priv->actors, actor); + } + /* emit signal */ + g_signal_emit(obj, mixin_cls->members_changed_signal_id, 0, + message, + arr_add, arr_remove, + arr_local, arr_remote, + actor, reason); + + /* free arrays */ + g_array_free (arr_add, TRUE); + g_array_free (arr_remove, TRUE); + g_array_free (arr_local, TRUE); + g_array_free (arr_remote, TRUE); + + ret = TRUE; + } + else + { + gabble_debug (DEBUG_FLAG, "not emitting signal, nothing changed"); + + ret = FALSE; + } + + /* free intsets */ + g_intset_destroy (new_add); + g_intset_destroy (new_remove); + g_intset_destroy (new_local_pending); + g_intset_destroy (new_remote_pending); + g_intset_destroy (empty); + + return ret; +} + +void +gabble_group_mixin_add_handle_owner (GObject *obj, + GabbleHandle local_handle, + GabbleHandle owner_handle) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + GabbleGroupMixinPrivate *priv = mixin->priv; + + g_hash_table_insert (priv->handle_owners, GUINT_TO_POINTER (local_handle), + GUINT_TO_POINTER (owner_handle)); + + gabble_handle_ref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + local_handle); + gabble_handle_ref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + owner_handle); +} + +static void +remove_handle_owners_if_exist (GObject *obj, GArray *array) +{ + GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj); + GabbleGroupMixinPrivate *priv = mixin->priv; + guint i; + + for (i = 0; i < array->len; i++) + { + GabbleHandle handle = g_array_index (array, guint32, i); + gpointer local_handle, owner_handle; + + if (g_hash_table_lookup_extended (priv->handle_owners, + GUINT_TO_POINTER (handle), + &local_handle, + &owner_handle)) + { + gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + GPOINTER_TO_UINT (local_handle)); + gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT, + GPOINTER_TO_UINT (owner_handle)); + + g_hash_table_remove (priv->handle_owners, GUINT_TO_POINTER (handle)); + } + } +} +