X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-contact-list.c;h=6868e84a66d8911603f978a05912813b942bbe13;hp=dd04fac53d565bfac37fee26b54680ca68845db9;hb=aa098bf904f8e85fa6aa44ffea99e1e027775d26;hpb=58505161bcc63ba91190f6b38a3c52c0c564ca32 diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c index dd04fac5..6868e84a 100644 --- a/libempathy/empathy-tp-contact-list.c +++ b/libempathy/empathy-tp-contact-list.c @@ -1,22 +1,21 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2007 Xavier Claessens - * Copyright (C) 2007 Collabora Ltd. + * Copyright (C) 2007-2009 Collabora Ltd. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU 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 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 program is distributed in the hope that it will be useful, + * 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 - * General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * 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 * * Authors: Xavier Claessens */ @@ -24,1873 +23,1110 @@ #include #include +#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include "empathy-tp-contact-list.h" +#include "empathy-tp-contact-factory.h" #include "empathy-contact-list.h" -#include "empathy-tp-group.h" -#include "empathy-debug.h" #include "empathy-utils.h" -#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - EMPATHY_TYPE_TP_CONTACT_LIST, EmpathyTpContactListPriv)) - -#define DEBUG_DOMAIN "TpContactList" -#define MAX_AVATAR_REQUESTS 10 - -struct _EmpathyTpContactListPriv { - TpConn *tp_conn; - McAccount *account; - MissionControl *mc; - EmpathyContact *user_contact; - gboolean setup; - - EmpathyTpGroup *publish; - EmpathyTpGroup *subscribe; +#define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CONTACT +#include "empathy-debug.h" +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpContactList) +typedef struct { + EmpathyTpContactFactory *factory; + TpConnection *connection; + const gchar *protocol_group; + + TpChannel *publish; + TpChannel *subscribe; + GHashTable *members; + GHashTable *pendings; GHashTable *groups; - GHashTable *contacts; - GList *members; - GList *local_pending; - - DBusGProxy *aliasing_iface; - DBusGProxy *avatars_iface; - DBusGProxy *presence_iface; - - GList *avatar_requests_queue; -}; +} EmpathyTpContactListPriv; typedef enum { TP_CONTACT_LIST_TYPE_PUBLISH, TP_CONTACT_LIST_TYPE_SUBSCRIBE, - TP_CONTACT_LIST_TYPE_UNKNOWN, - TP_CONTACT_LIST_TYPE_COUNT + TP_CONTACT_LIST_TYPE_UNKNOWN } TpContactListType; -typedef struct { - guint handle; - GList *new_groups; -} TpContactListData; - -typedef struct { - EmpathyTpContactList *list; - guint handle; -} TpContactListAvatarRequestData; - -typedef struct { - EmpathyTpContactList *list; - guint *handles; -} TpContactListAliasesRequestData; - -static void empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass); -static void tp_contact_list_iface_init (EmpathyContactListIface *iface); -static void empathy_tp_contact_list_init (EmpathyTpContactList *list); -static void tp_contact_list_finalize (GObject *object); -static void tp_contact_list_finalize_proxies (EmpathyTpContactList *list); -static void tp_contact_list_setup (EmpathyContactList *list); -static EmpathyContact * tp_contact_list_find (EmpathyContactList *list, - const gchar *id); -static void tp_contact_list_add (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); -static void tp_contact_list_remove (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message); -static GList * tp_contact_list_get_members (EmpathyContactList *list); -static GList * tp_contact_list_get_local_pending (EmpathyContactList *list); -static void tp_contact_list_process_pending (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept); -static void tp_contact_list_remove_local_pending (EmpathyTpContactList *list, - EmpathyContact *contact); -static void tp_contact_list_contact_removed_foreach (guint handle, - EmpathyContact *contact, - EmpathyTpContactList *list); -static void tp_contact_list_destroy_cb (DBusGProxy *proxy, - EmpathyTpContactList *list); -static gboolean tp_contact_list_find_foreach (guint handle, - EmpathyContact *contact, - gchar *id); -static void tp_contact_list_newchannel_cb (DBusGProxy *proxy, - const gchar *object_path, - const gchar *channel_type, - TelepathyHandleType handle_type, - guint channel_handle, - gboolean suppress_handle, - EmpathyTpContactList *list); -static TpContactListType tp_contact_list_get_type (EmpathyTpContactList *list, - EmpathyTpGroup *group); -static void tp_contact_list_added_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_removed_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_pending_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_groups_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list); -static void tp_contact_list_name_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list); -static void tp_contact_list_update_groups_foreach (gchar *object_path, - EmpathyTpGroup *group, - TpContactListData *data); -static EmpathyTpGroup * tp_contact_list_get_group (EmpathyTpContactList *list, - const gchar *name); -static gboolean tp_contact_list_find_group (gchar *key, - EmpathyTpGroup *group, - gchar *group_name); -static void tp_contact_list_get_groups_foreach (gchar *key, - EmpathyTpGroup *group, - GList **groups); -static void tp_contact_list_group_channel_closed_cb (TpChan *channel, - EmpathyTpContactList *list); -static void tp_contact_list_group_members_added_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_group_members_removed_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list); -static void tp_contact_list_get_info (EmpathyTpContactList *list, - GArray *handles); -static void tp_contact_list_request_avatar (EmpathyTpContactList *list, - guint handle); -static void tp_contact_list_start_avatar_requests (EmpathyTpContactList *list); -static void tp_contact_list_avatar_update_cb (DBusGProxy *proxy, - guint handle, - gchar *new_token, - EmpathyTpContactList *list); -static void tp_contact_list_request_avatar_cb (DBusGProxy *proxy, - GArray *avatar_data, - gchar *mime_type, - GError *error, - TpContactListAvatarRequestData *data); -static void tp_contact_list_aliases_update_cb (DBusGProxy *proxy, - GPtrArray *handlers, - EmpathyTpContactList *list); -static void tp_contact_list_request_aliases_cb (DBusGProxy *proxy, - gchar **contact_names, - GError *error, - TpContactListAliasesRequestData *data); -static void tp_contact_list_presence_update_cb (DBusGProxy *proxy, - GHashTable *handle_table, - EmpathyTpContactList *list); -static void tp_contact_list_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyTpContactList *list); -static void tp_contact_list_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - EmpathyPresence **presence); -static void tp_contact_list_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyTpContactList *list); +static void tp_contact_list_iface_init (EmpathyContactListIface *iface); enum { - DESTROY, - LAST_SIGNAL + PROP_0, + PROP_CONNECTION, }; -static guint signals[LAST_SIGNAL]; -static guint n_avatar_requests = 0; - G_DEFINE_TYPE_WITH_CODE (EmpathyTpContactList, empathy_tp_contact_list, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST, tp_contact_list_iface_init)); static void -empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = tp_contact_list_finalize; - - signals[DESTROY] = - g_signal_new ("destroy", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); -} - -static void -tp_contact_list_iface_init (EmpathyContactListIface *iface) -{ - iface->setup = tp_contact_list_setup; - iface->find = tp_contact_list_find; - iface->add = tp_contact_list_add; - iface->remove = tp_contact_list_remove; - iface->get_members = tp_contact_list_get_members; - iface->get_local_pending = tp_contact_list_get_local_pending; - iface->process_pending = tp_contact_list_process_pending; -} - -static void -empathy_tp_contact_list_init (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - - priv = GET_PRIV (list); - - priv->groups = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_object_unref); - priv->contacts = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_object_unref); -} - -static void -tp_contact_list_finalize (GObject *object) -{ - EmpathyTpContactListPriv *priv; - EmpathyTpContactList *list; - - list = EMPATHY_TP_CONTACT_LIST (object); - priv = GET_PRIV (list); - - empathy_debug (DEBUG_DOMAIN, "finalize: %p", object); - - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_list_status_changed_cb), - list); - - tp_contact_list_finalize_proxies (list); - - if (priv->tp_conn) { - g_object_unref (priv->tp_conn); - } - if (priv->subscribe) { - g_object_unref (priv->subscribe); - } - if (priv->publish) { - g_object_unref (priv->publish); - } - - g_object_unref (priv->account); - g_object_unref (priv->user_contact); - g_object_unref (priv->mc); - g_hash_table_destroy (priv->groups); - g_hash_table_destroy (priv->contacts); - - g_list_foreach (priv->local_pending, (GFunc) empathy_contact_list_info_free, NULL); - g_list_free (priv->local_pending); - - g_list_foreach (priv->members, (GFunc) g_object_unref, NULL); - g_list_free (priv->members); - - G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); -} - -EmpathyTpContactList * -empathy_tp_contact_list_new (McAccount *account) +tp_contact_list_group_invalidated_cb (TpChannel *channel, + guint domain, + gint code, + gchar *message, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - EmpathyTpContactList *list; - MissionControl *mc; - guint handle; - GError *error = NULL; - - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); - - mc = empathy_mission_control_new (); - - if (mission_control_get_connection_status (mc, account, NULL) != 0) { - /* The account is not connected, nothing to do. */ - return NULL; - } - - list = g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, NULL); - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + const TpIntSet *members; + TpIntSetIter iter; + const gchar *group_name; - priv->tp_conn = mission_control_get_connection (mc, account, NULL); - priv->account = g_object_ref (account); - priv->mc = mc; + group_name = tp_channel_get_identifier (channel); + DEBUG ("Group %s invalidated. Message: %s", group_name, message); - g_signal_connect (priv->tp_conn, "destroy", - G_CALLBACK (tp_contact_list_destroy_cb), - list); - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_list_status_changed_cb), - list, NULL); - - priv->aliasing_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_ALIASING_QUARK); - priv->avatars_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_AVATARS_QUARK); - priv->presence_iface = tp_conn_get_interface (priv->tp_conn, - TELEPATHY_CONN_IFACE_PRESENCE_QUARK); - - if (priv->aliasing_iface) { - dbus_g_proxy_connect_signal (priv->aliasing_iface, - "AliasesChanged", - G_CALLBACK (tp_contact_list_aliases_update_cb), - list, NULL); - } - - if (priv->avatars_iface) { - dbus_g_proxy_connect_signal (priv->avatars_iface, - "AvatarUpdated", - G_CALLBACK (tp_contact_list_avatar_update_cb), - list, NULL); - } + /* Signal that all members are not in that group anymore */ + members = tp_channel_group_get_members (channel); + tp_intset_iter_init (&iter, members); + while (tp_intset_iter_next (&iter)) { + EmpathyContact *contact; - if (priv->presence_iface) { - dbus_g_proxy_connect_signal (priv->presence_iface, - "PresenceUpdate", - G_CALLBACK (tp_contact_list_presence_update_cb), - list, NULL); - } + contact = g_hash_table_lookup (priv->members, + GUINT_TO_POINTER (iter.element)); + if (contact == NULL) { + continue; + } - /* Get our own handle and contact */ - if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn), - &handle, &error)) { - empathy_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } else { - priv->user_contact = empathy_tp_contact_list_get_from_handle (list, handle); - empathy_contact_set_is_user (priv->user_contact, TRUE); + DEBUG ("Contact %s (%d) removed from group %s", + empathy_contact_get_id (contact), iter.element, + group_name); + g_signal_emit_by_name (list, "groups-changed", contact, + group_name, + FALSE); } - return list; + g_hash_table_remove (priv->groups, group_name); } static void -tp_contact_list_setup (EmpathyContactList *list) +tp_contact_list_group_ready_cb (TpChannel *channel, + const GError *error, + gpointer list) { - EmpathyTpContactListPriv *priv; - GPtrArray *channels; - GError *error = NULL; - guint i; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - empathy_debug (DEBUG_DOMAIN, "setup contact list: %p", list); - - priv->setup = TRUE; - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (tp_contact_list_newchannel_cb), - list, NULL); - - /* Get existing channels */ - if (!tp_conn_list_channels (DBUS_G_PROXY (priv->tp_conn), - &channels, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to get list of open channels: %s", - error ? error->message : "No error given"); - g_clear_error (&error); + if (error) { + DEBUG ("Error: %s", error->message); + g_object_unref (channel); return; } - - for (i = 0; channels->len > i; i++) { - GValueArray *chan_struct; - const gchar *object_path; - const gchar *chan_iface; - TelepathyHandleType handle_type; - guint handle; - - chan_struct = g_ptr_array_index (channels, i); - object_path = g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); - chan_iface = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); - handle_type = g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); - handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); - - tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), - object_path, chan_iface, - handle_type, handle, - FALSE, - EMPATHY_TP_CONTACT_LIST (list)); - - g_value_array_free (chan_struct); - } - - g_ptr_array_free (channels, TRUE); -} - -static EmpathyContact * -tp_contact_list_find (EmpathyContactList *list, - const gchar *id) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return g_hash_table_find (priv->contacts, - (GHRFunc) tp_contact_list_find_foreach, - (gchar*) id); -} - -static void -tp_contact_list_add (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message) -{ - EmpathyTpContactListPriv *priv; - guint handle; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - handle = empathy_contact_get_handle (contact); - empathy_tp_group_add_member (priv->subscribe, handle, message); -} - -static void -tp_contact_list_remove (EmpathyContactList *list, - EmpathyContact *contact, - const gchar *message) -{ - EmpathyTpContactListPriv *priv; - guint handle; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - priv = GET_PRIV (list); - - handle = empathy_contact_get_handle (contact); - empathy_tp_group_remove_member (priv->subscribe, handle, message); -} - -static GList * -tp_contact_list_get_members (EmpathyContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - g_list_foreach (priv->members, (GFunc) g_object_ref, NULL); - return g_list_copy (priv->members); -} - -static GList * -tp_contact_list_get_local_pending (EmpathyContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return g_list_copy (priv->local_pending); -} - -static void -tp_contact_list_process_pending (EmpathyContactList *list, - EmpathyContact *contact, - gboolean accept) -{ - EmpathyTpContactListPriv *priv; - guint handle; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - - priv = GET_PRIV (list); - - handle = empathy_contact_get_handle (contact); - if (accept) { - empathy_tp_group_add_member (priv->publish, handle, NULL); - } else { - empathy_tp_group_remove_member (priv->publish, handle, NULL); - } -} - -McAccount * -empathy_tp_contact_list_get_account (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - - return priv->account; -} - -EmpathyContact * -empathy_tp_contact_list_get_user (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); - return priv->user_contact; -} - -EmpathyContact * -empathy_tp_contact_list_get_from_id (EmpathyTpContactList *list, - const gchar *id) -{ - EmpathyTpContactListPriv *priv; - EmpathyContact *contact; - const gchar *contact_ids[] = {id, NULL}; - GArray *handles; - guint handle; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - g_return_val_if_fail (id != NULL, NULL); - - priv = GET_PRIV (list); - - contact = tp_contact_list_find (EMPATHY_CONTACT_LIST (list), id); - if (contact) { - return g_object_ref (contact); - } - - /* The id is unknown, requests a new handle */ - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - contact_ids, - &handles, &error)) { - empathy_debug (DEBUG_DOMAIN, - "RequestHandle for %s failed: %s", id, - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - - handle = g_array_index(handles, guint, 0); - g_array_free (handles, TRUE); + DEBUG ("Add group %s", tp_channel_get_identifier (channel)); + g_hash_table_insert (priv->groups, + (gpointer) tp_channel_get_identifier (channel), + channel); - return empathy_tp_contact_list_get_from_handle (list, handle); -} - -EmpathyContact * -empathy_tp_contact_list_get_from_handle (EmpathyTpContactList *list, - guint handle) -{ - EmpathyContact *contact; - GArray *handles; - GList *contacts; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - handles = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (handles, handle); - - contacts = empathy_tp_contact_list_get_from_handles (list, handles); - g_array_free (handles, TRUE); - - if (!contacts) { - return NULL; - } - - contact = contacts->data; - g_list_free (contacts); - - return contact; + g_signal_connect (channel, "invalidated", + G_CALLBACK (tp_contact_list_group_invalidated_cb), + list); } -GList * -empathy_tp_contact_list_get_from_handles (EmpathyTpContactList *list, - GArray *handles) +static void +tp_contact_list_group_members_changed_cb (TpChannel *channel, + gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + guint actor, + guint reason, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - gchar **handles_names; - gchar **id; - GArray *new_handles; - GList *contacts = NULL; - guint i; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - g_return_val_if_fail (handles != NULL, NULL); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + const gchar *group_name; + gint i; - priv = GET_PRIV (list); + group_name = tp_channel_get_identifier (channel); - /* Search all handles we already have */ - new_handles = g_array_new (FALSE, FALSE, sizeof (guint)); - for (i = 0; i < handles->len; i++) { + for (i = 0; i < added->len; i++) { EmpathyContact *contact; - guint handle; - - handle = g_array_index (handles, guint, i); - - if (handle == 0) { - continue; - } + TpHandle handle; - contact = g_hash_table_lookup (priv->contacts, + handle = g_array_index (added, TpHandle, i); + contact = g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle)); - - if (contact) { - contacts = g_list_prepend (contacts, - g_object_ref (contact)); - } else { - g_array_append_val (new_handles, handle); + if (contact == NULL) { + continue; } - } - if (new_handles->len == 0) { - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Holds all handles we don't have yet. - * FIXME: We should release them at some point. */ - if (!tp_conn_hold_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - new_handles, &error)) { - empathy_debug (DEBUG_DOMAIN, - "HoldHandles Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; - } + DEBUG ("Contact %s (%d) added to group %s", + empathy_contact_get_id (contact), handle, group_name); + g_signal_emit_by_name (list, "groups-changed", contact, + group_name, + TRUE); + } - /* Get the IDs of all new handles */ - if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_CONTACT, - new_handles, - &handles_names, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "InspectHandle Error: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Create contact objects */ - for (i = 0, id = handles_names; *id && i < new_handles->len; id++, i++) { + for (i = 0; i < removed->len; i++) { EmpathyContact *contact; - guint handle; - - handle = g_array_index (new_handles, guint, i); - contact = g_object_new (EMPATHY_TYPE_CONTACT, - "account", priv->account, - "id", *id, - "handle", handle, - NULL); - - if (!priv->presence_iface) { - EmpathyPresence *presence; + TpHandle handle; - /* We have no presence iface, set default presence - * to available */ - presence = empathy_presence_new_full (MC_PRESENCE_AVAILABLE, - NULL); - - empathy_contact_set_presence (contact, presence); - g_object_unref (presence); + handle = g_array_index (removed, TpHandle, i); + contact = g_hash_table_lookup (priv->members, + GUINT_TO_POINTER (handle)); + if (contact == NULL) { + continue; } - g_signal_connect (contact, "notify::groups", - G_CALLBACK (tp_contact_list_groups_updated_cb), - list); - g_signal_connect (contact, "notify::name", - G_CALLBACK (tp_contact_list_name_updated_cb), - list); - - empathy_debug (DEBUG_DOMAIN, "new contact created: %s (%d)", - *id, handle); - - g_hash_table_insert (priv->contacts, - GUINT_TO_POINTER (handle), - contact); + DEBUG ("Contact %s (%d) removed from group %s", + empathy_contact_get_id (contact), handle, group_name); - contacts = g_list_prepend (contacts, g_object_ref (contact)); - } - - tp_contact_list_get_info (list, new_handles); - - g_array_free (new_handles, TRUE); - g_strfreev (handles_names); - - return contacts; + g_signal_emit_by_name (list, "groups-changed", contact, + group_name, + FALSE); + } } -void -empathy_tp_contact_list_rename_group (EmpathyTpContactList *list, - const gchar *old_group, - const gchar *new_group) +static TpChannel * +tp_contact_list_group_add_channel (EmpathyTpContactList *list, + const gchar *object_path, + const gchar *channel_type, + TpHandleType handle_type, + guint handle) { - EmpathyTpContactListPriv *priv; - EmpathyTpGroup *group; - GArray *members; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_return_if_fail (old_group != NULL); - g_return_if_fail (new_group != NULL); - - priv = GET_PRIV (list); - - group = g_hash_table_find (priv->groups, - (GHRFunc) tp_contact_list_find_group, - (gchar*) old_group); - if (!group) { - /* The group doesn't exists on this account */ - return; - } - - empathy_debug (DEBUG_DOMAIN, "rename group %s to %s", group, new_group); - - /* Remove all members from the old group */ - members = empathy_tp_group_get_members (group); - empathy_tp_group_remove_members (group, members, ""); - tp_contact_list_group_members_removed_cb (group, members, - 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_hash_table_remove (priv->groups, - empathy_tp_group_get_object_path (group)); - - /* Add all members to the new group */ - group = tp_contact_list_get_group (list, new_group); - if (group) { - empathy_tp_group_add_members (group, members, ""); + /* Only accept server-side contact groups */ + if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) || + handle_type != TP_HANDLE_TYPE_GROUP) { + return NULL; } -} - -GList * -empathy_tp_contact_list_get_groups (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - GList *groups = NULL; - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - priv = GET_PRIV (list); + channel = tp_channel_new (priv->connection, + object_path, channel_type, + handle_type, handle, NULL); - g_hash_table_foreach (priv->groups, - (GHFunc) tp_contact_list_get_groups_foreach, - &groups); + /* TpChannel emits initial set of members just before being ready */ + g_signal_connect (channel, "group-members-changed", + G_CALLBACK (tp_contact_list_group_members_changed_cb), + list); - groups = g_list_sort (groups, (GCompareFunc) strcmp); + /* Give the ref to the callback */ + tp_channel_call_when_ready (channel, + tp_contact_list_group_ready_cb, + list); - return groups; + return channel; } -static void -tp_contact_list_finalize_proxies (EmpathyTpContactList *list) -{ - EmpathyTpContactListPriv *priv; - - priv = GET_PRIV (list); - - if (priv->tp_conn) { - g_signal_handlers_disconnect_by_func (priv->tp_conn, - tp_contact_list_destroy_cb, - list); - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (tp_contact_list_newchannel_cb), - list); - } - - if (priv->aliasing_iface) { - dbus_g_proxy_disconnect_signal (priv->aliasing_iface, - "AliasesChanged", - G_CALLBACK (tp_contact_list_aliases_update_cb), - list); - } - - if (priv->avatars_iface) { - dbus_g_proxy_disconnect_signal (priv->avatars_iface, - "AvatarUpdated", - G_CALLBACK (tp_contact_list_avatar_update_cb), - list); - } - - if (priv->presence_iface) { - dbus_g_proxy_disconnect_signal (priv->presence_iface, - "PresenceUpdate", - G_CALLBACK (tp_contact_list_presence_update_cb), - list); - } -} +typedef struct { + GArray *handles; + TpHandle channel_handle; + guint ref_count; +} GroupAddData; static void -tp_contact_list_destroy_cb (DBusGProxy *proxy, - EmpathyTpContactList *list) +tp_contact_list_group_add_data_unref (gpointer user_data) { - EmpathyTpContactListPriv *priv; + GroupAddData *data = user_data; - priv = GET_PRIV (list); - - empathy_debug (DEBUG_DOMAIN, "Connection destroyed... " - "Account disconnected or CM crashed"); - - /* DBus proxies should NOT be used anymore */ - g_object_unref (priv->tp_conn); - priv->tp_conn = NULL; - priv->aliasing_iface = NULL; - priv->avatars_iface = NULL; - priv->presence_iface = NULL; - - /* Remove all contacts */ - g_hash_table_foreach (priv->contacts, - (GHFunc) tp_contact_list_contact_removed_foreach, - list); - g_hash_table_remove_all (priv->contacts); - - /* Tell the world to not use us anymore */ - g_signal_emit (list, signals[DESTROY], 0); + data->ref_count--; + if (data->ref_count == 0) { + g_array_free (data->handles, TRUE); + g_slice_free (GroupAddData, data); + } } static void -tp_contact_list_contact_removed_foreach (guint handle, - EmpathyContact *contact, - EmpathyTpContactList *list) +tp_contact_list_group_add_ready_cb (TpChannel *channel, + const GError *error, + gpointer user_data) { - g_signal_handlers_disconnect_by_func (contact, - tp_contact_list_groups_updated_cb, - list); - g_signal_handlers_disconnect_by_func (contact, - tp_contact_list_name_updated_cb, - list); + GroupAddData *data = user_data; - g_signal_emit_by_name (list, "contact-removed", contact); -} + if (error) { + tp_contact_list_group_add_data_unref (data); + return; + } -static void -tp_contact_list_block_contact (EmpathyTpContactList *list, - EmpathyContact *contact) -{ - g_signal_handlers_block_by_func (contact, - tp_contact_list_groups_updated_cb, - list); - g_signal_handlers_block_by_func (contact, - tp_contact_list_name_updated_cb, - list); + tp_cli_channel_interface_group_call_add_members (channel, -1, + data->handles, NULL, NULL, NULL, NULL, NULL); + tp_contact_list_group_add_data_unref (data); } static void -tp_contact_list_unblock_contact (EmpathyTpContactList *list, - EmpathyContact *contact) +tp_contact_list_group_request_channel_cb (TpConnection *connection, + const gchar *object_path, + const GError *error, + gpointer user_data, + GObject *list) { - g_signal_handlers_unblock_by_func (contact, - tp_contact_list_groups_updated_cb, - list); - g_signal_handlers_unblock_by_func (contact, - tp_contact_list_name_updated_cb, - list); -} + GroupAddData *data = user_data; + TpChannel *channel; -static gboolean -tp_contact_list_find_foreach (guint handle, - EmpathyContact *contact, - gchar *id) -{ - if (strcmp (empathy_contact_get_id (contact), id) == 0) { - return TRUE; + if (error) { + DEBUG ("Error: %s", error->message); + return; } - return FALSE; + channel = tp_contact_list_group_add_channel (EMPATHY_TP_CONTACT_LIST (list), + object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_GROUP, + data->channel_handle); + + data->ref_count++; + tp_channel_call_when_ready (channel, + tp_contact_list_group_add_ready_cb, + data); } static void -tp_contact_list_newchannel_cb (DBusGProxy *proxy, - const gchar *object_path, - const gchar *channel_type, - TelepathyHandleType handle_type, - guint channel_handle, - gboolean suppress_handle, - EmpathyTpContactList *list) +tp_contact_list_group_request_handles_cb (TpConnection *connection, + const GArray *handles, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - EmpathyTpGroup *group; - TpChan *new_chan; - const gchar *bus_name; - GArray *members; + GroupAddData *data = user_data; - priv = GET_PRIV (list); - - if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) != 0 || - suppress_handle || - !priv->setup) { + if (error) { + DEBUG ("Error: %s", error->message); return; } - bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)); - new_chan = tp_chan_new (tp_get_bus (), - bus_name, - object_path, - channel_type, handle_type, channel_handle); - - if (handle_type == TP_HANDLE_TYPE_LIST) { - TpContactListType list_type; - - group = empathy_tp_group_new (new_chan, priv->tp_conn); - - list_type = tp_contact_list_get_type (list, group); - if (list_type == TP_CONTACT_LIST_TYPE_UNKNOWN) { - empathy_debug (DEBUG_DOMAIN, - "Type of contact list channel unknown: %s", - empathy_tp_group_get_name (group)); - - g_object_unref (new_chan); - g_object_unref (group); - return; - } else { - empathy_debug (DEBUG_DOMAIN, - "New contact list channel of type: %d", - list_type); - } - - g_signal_connect (group, "members-added", - G_CALLBACK (tp_contact_list_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (tp_contact_list_removed_cb), - list); - - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - GList *pendings, *l; - - if (priv->publish) { - g_object_unref (priv->publish); - } - priv->publish = group; - - /* Makes no sense to be in remote-pending */ - g_signal_connect (group, "local-pending", - G_CALLBACK (tp_contact_list_pending_cb), - list); - - pendings = empathy_tp_group_get_local_pending_members_with_info (group); - if (pendings) { - GArray *pending; - - pending = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); - for (l = pendings; l; l = l->next) { - EmpathyTpGroupInfo *info; - - info = l->data; - - g_array_insert_val (pending, 0, info->member); - tp_contact_list_pending_cb (group, pending, - info->actor, - info->reason, - info->message, - list); - } - g_array_free (pending, TRUE); - empathy_tp_group_info_list_free (pendings); - } - } - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - GArray *remote_pendings = NULL; - - if (priv->subscribe) { - g_object_unref (priv->subscribe); - } - priv->subscribe = group; - - /* Makes no sense to be in local-pending */ - g_signal_connect (group, "remote-pending", - G_CALLBACK (tp_contact_list_pending_cb), - list); - empathy_tp_group_get_all_members (group, - &members, - NULL, - &remote_pendings); - - tp_contact_list_pending_cb (group, remote_pendings, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, - list); - g_array_free (remote_pendings, TRUE); - } else { - members = empathy_tp_group_get_members (group); - } - - tp_contact_list_added_cb (group, members, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_array_free (members, TRUE); - } - else if (handle_type == TP_HANDLE_TYPE_GROUP) { - const gchar *object_path; - - object_path = dbus_g_proxy_get_path (DBUS_G_PROXY (new_chan)); - if (g_hash_table_lookup (priv->groups, object_path)) { - g_object_unref (new_chan); - return; - } - - group = empathy_tp_group_new (new_chan, priv->tp_conn); - - empathy_debug (DEBUG_DOMAIN, "New server-side group channel: %s", - empathy_tp_group_get_name (group)); - - dbus_g_proxy_connect_signal (DBUS_G_PROXY (new_chan), "Closed", - G_CALLBACK - (tp_contact_list_group_channel_closed_cb), - list, NULL); - - g_hash_table_insert (priv->groups, g_strdup (object_path), group); - g_signal_connect (group, "members-added", - G_CALLBACK (tp_contact_list_group_members_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (tp_contact_list_group_members_removed_cb), - list); - - members = empathy_tp_group_get_members (group); - tp_contact_list_group_members_added_cb (group, members, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE, - NULL, list); - g_array_free (members, TRUE); - } - - g_object_unref (new_chan); + data->channel_handle = g_array_index (handles, TpHandle, 0); + data->ref_count++; + tp_cli_connection_call_request_channel (connection, -1, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_GROUP, + data->channel_handle, + TRUE, + tp_contact_list_group_request_channel_cb, + data, tp_contact_list_group_add_data_unref, + list); } -static TpContactListType -tp_contact_list_get_type (EmpathyTpContactList *list, - EmpathyTpGroup *group) -{ - EmpathyTpContactListPriv *priv; - TpContactListType list_type; - const gchar *name; - - priv = GET_PRIV (list); - - name = empathy_tp_group_get_name (group); - if (strcmp (name, "subscribe") == 0) { - list_type = TP_CONTACT_LIST_TYPE_SUBSCRIBE; - } else if (strcmp (name, "publish") == 0) { - list_type = TP_CONTACT_LIST_TYPE_PUBLISH; - } else { - list_type = TP_CONTACT_LIST_TYPE_UNKNOWN; +/* This function takes ownership of handles array */ +static void +tp_contact_list_group_add (EmpathyTpContactList *list, + const gchar *group_name, + GArray *handles) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + const gchar *names[] = {group_name, NULL}; + GroupAddData *data; + + /* Search the channel for that group name */ + channel = g_hash_table_lookup (priv->groups, group_name); + if (channel) { + tp_cli_channel_interface_group_call_add_members (channel, -1, + handles, NULL, NULL, NULL, NULL, NULL); + g_array_free (handles, TRUE); + return; } - return list_type; + /* That group does not exist yet, we have to: + * 1) Request an handle for the group name + * 2) Request a channel + * 3) Add handles in members of the new channel */ + data = g_slice_new0 (GroupAddData); + data->handles = handles; + data->ref_count = 1; + tp_cli_connection_call_request_handles (priv->connection, -1, + TP_HANDLE_TYPE_GROUP, names, + tp_contact_list_group_request_handles_cb, + data, tp_contact_list_group_add_data_unref, + G_OBJECT (list)); } static void -tp_contact_list_added_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_got_added_members_cb (EmpathyTpContactFactory *factory, + guint n_contacts, + EmpathyContact * const * contacts, + guint n_failed, + const TpHandle *failed, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - GList *added_list, *l; - TpContactListType list_type; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + guint i; - priv = GET_PRIV (list); - - list_type = tp_contact_list_get_type (list, group); - - added_list = empathy_tp_contact_list_get_from_handles (list, handles); - for (l = added_list; l; l = l->next) { - EmpathyContact *contact; - EmpathySubscription subscription; + if (error) { + DEBUG ("Error: %s", error->message); + return; + } - contact = EMPATHY_CONTACT (l->data); + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact = contacts[i]; + TpHandle handle; - empathy_debug (DEBUG_DOMAIN, "Contact '%s' added to list type %d", - empathy_contact_get_name (contact), - list_type); + handle = empathy_contact_get_handle (contact); + if (g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle))) + continue; - subscription = empathy_contact_get_subscription (contact); - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - subscription |= EMPATHY_SUBSCRIPTION_FROM; - } - else if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - subscription |= EMPATHY_SUBSCRIPTION_TO; - tp_contact_list_remove_local_pending (list, contact); - } + /* Add to the list and emit signal */ + g_hash_table_insert (priv->members, GUINT_TO_POINTER (handle), + g_object_ref (contact)); + g_signal_emit_by_name (list, "members-changed", contact, + 0, 0, NULL, TRUE); - tp_contact_list_block_contact (list, contact); - empathy_contact_set_subscription (contact, subscription); - tp_contact_list_unblock_contact (list, contact); + /* This contact is now member, implicitly accept pending. */ + if (g_hash_table_lookup (priv->pendings, GUINT_TO_POINTER (handle))) { + GArray handles = {(gchar*) &handle, 1}; - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - if (!g_list_find (priv->members, contact)) { - priv->members = g_list_prepend (priv->members, - g_object_ref (contact)); - g_signal_emit_by_name (list, "contact-added", contact); - } + tp_cli_channel_interface_group_call_add_members (priv->publish, + -1, &handles, NULL, NULL, NULL, NULL, NULL); } - - g_object_unref (contact); } - - g_list_free (added_list); } static void -tp_contact_list_removed_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_got_local_pending_cb (EmpathyTpContactFactory *factory, + guint n_contacts, + EmpathyContact * const * contacts, + guint n_failed, + const TpHandle *failed, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - GList *removed_list, *l; - TpContactListType list_type; - - priv = GET_PRIV (list); - - list_type = tp_contact_list_get_type (list, group); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + guint i; - removed_list = empathy_tp_contact_list_get_from_handles (list, handles); - for (l = removed_list; l; l = l->next) { - EmpathyContact *contact; - EmpathySubscription subscription; + if (error) { + DEBUG ("Error: %s", error->message); + return; + } - contact = EMPATHY_CONTACT (l->data); + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact = contacts[i]; + TpHandle handle; + const gchar *message; + TpChannelGroupChangeReason reason; - empathy_debug (DEBUG_DOMAIN, "Contact '%s' removed from list type %d", - empathy_contact_get_name (contact), - list_type); + handle = empathy_contact_get_handle (contact); + if (g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle))) { + GArray handles = {(gchar*) &handle, 1}; - subscription = empathy_contact_get_subscription (contact); - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - subscription &= !EMPATHY_SUBSCRIPTION_FROM; - } - else if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - subscription &= !EMPATHY_SUBSCRIPTION_TO; - tp_contact_list_remove_local_pending (list, contact); + /* This contact is already member, auto accept. */ + tp_cli_channel_interface_group_call_add_members (priv->publish, + -1, &handles, NULL, NULL, NULL, NULL, NULL); } - - tp_contact_list_block_contact (list, contact); - empathy_contact_set_subscription (contact, subscription); - tp_contact_list_unblock_contact (list, contact); - - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - GList *l; - - if ((l = g_list_find (priv->members, contact))) { - g_signal_emit_by_name (list, "contact-removed", contact); - priv->members = g_list_delete_link (priv->members, l); - g_object_unref (contact); - } + else if (tp_channel_group_get_local_pending_info (priv->publish, + handle, + NULL, + &reason, + &message)) { + /* Add contact to pendings */ + g_hash_table_insert (priv->pendings, GUINT_TO_POINTER (handle), + g_object_ref (contact)); + g_signal_emit_by_name (list, "pendings-changed", contact, + contact, reason, message, TRUE); } - g_object_unref (contact); } - - g_list_free (removed_list); } static void -tp_contact_list_pending_cb (EmpathyTpGroup *group, - GArray *handles, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_remove_handle (EmpathyTpContactList *list, + GHashTable *table, + TpHandle handle) { - EmpathyTpContactListPriv *priv; - GList *pending_list, *l; - TpContactListType list_type; - - priv = GET_PRIV (list); - - list_type = tp_contact_list_get_type (list, group); - - pending_list = empathy_tp_contact_list_get_from_handles (list, handles); - for (l = pending_list; l; l = l->next) { - EmpathyContact *contact; - - contact = EMPATHY_CONTACT (l->data); - - empathy_debug (DEBUG_DOMAIN, "Contact '%s' pending in list type %d", - empathy_contact_get_name (contact), - list_type); - - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH) { - EmpathyContactListInfo *info; - - info = empathy_contact_list_info_new (contact, message); - priv->local_pending = g_list_prepend (priv->local_pending, - info); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + EmpathyContact *contact; + const gchar *signal; - g_signal_emit_by_name (list, "local-pending", - contact, message); - } - else if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE) { - if (!g_list_find (priv->members, contact)) { - priv->members = g_list_prepend (priv->members, - g_object_ref (contact)); - g_signal_emit_by_name (list, "contact-added", contact); - } - } + if (table == priv->pendings) + signal = "pendings-changed"; + else if (table == priv->members) + signal = "members-changed"; + else + return; + contact = g_hash_table_lookup (table, GUINT_TO_POINTER (handle)); + if (contact) { + g_object_ref (contact); + g_hash_table_remove (table, GUINT_TO_POINTER (handle)); + g_signal_emit_by_name (list, signal, contact, 0, 0, NULL, + FALSE); g_object_unref (contact); } - - g_list_free (pending_list); } static void -tp_contact_list_remove_local_pending (EmpathyTpContactList *list, - EmpathyContact *contact) +tp_contact_list_publish_group_members_changed_cb (TpChannel *channel, + gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + TpHandle actor, + TpChannelGroupChangeReason reason, + EmpathyTpContactList *list) { - EmpathyTpContactListPriv *priv; - GList *l; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + guint i; - for (l = priv->local_pending; l; l = l->next) { - EmpathyContactListInfo *info; + /* We now send our presence to those contacts, remove them from pendings */ + for (i = 0; i < added->len; i++) { + tp_contact_list_remove_handle (list, priv->pendings, + g_array_index (added, TpHandle, i)); + } - info = l->data; - if (empathy_contact_equal (contact, info->contact)) { - empathy_debug (DEBUG_DOMAIN, "Contact no more local-pending: %s", - empathy_contact_get_name (contact)); + /* We refuse to send our presence to those contacts, remove from pendings */ + for (i = 0; i < removed->len; i++) { + tp_contact_list_remove_handle (list, priv->pendings, + g_array_index (added, TpHandle, i)); + } - priv->local_pending = g_list_delete_link (priv->local_pending, l); - empathy_contact_list_info_free (info); - break; - } + /* Those contacts want our presence, auto accept those that are already + * member, otherwise add in pendings. */ + if (local_pending->len > 0) { + empathy_tp_contact_factory_get_from_handles (priv->factory, + local_pending->len, (TpHandle*) local_pending->data, + tp_contact_list_got_local_pending_cb, NULL, NULL, + G_OBJECT (list)); } } static void -tp_contact_list_groups_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list) +tp_contact_list_publish_request_channel_cb (TpConnection *connection, + const gchar *object_path, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - TpContactListData data; - GList *groups, *l; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - priv = GET_PRIV (list); - - /* Make sure all groups are created */ - groups = empathy_contact_get_groups (contact); - for (l = groups; l; l = l->next) { - tp_contact_list_get_group (list, l->data); + if (error) { + DEBUG ("Error: %s", error->message); + return; } - data.handle = empathy_contact_get_handle (contact); - data.new_groups = groups; + priv->publish = tp_channel_new (connection, object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_LIST, + GPOINTER_TO_UINT (user_data), + NULL); - g_hash_table_foreach (priv->groups, - (GHFunc) tp_contact_list_update_groups_foreach, - &data); + /* TpChannel emits initial set of members just before being ready */ + g_signal_connect (priv->publish, "group-members-changed", + G_CALLBACK (tp_contact_list_publish_group_members_changed_cb), + list); } static void -tp_contact_list_name_updated_cb (EmpathyContact *contact, - GParamSpec *param, - EmpathyTpContactList *list) +tp_contact_list_publish_request_handle_cb (TpConnection *connection, + const GArray *handles, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - GHashTable *new_alias; - const gchar *new_name; - guint handle; - GError *error = NULL; + TpHandle handle; - priv = GET_PRIV (list); - - if (!priv->aliasing_iface) { + if (error) { + DEBUG ("Error: %s", error->message); return; } - handle = empathy_contact_get_handle (contact); - new_name = empathy_contact_get_name (contact); - - empathy_debug (DEBUG_DOMAIN, "renaming handle %d to %s", - handle, new_name); - - new_alias = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - g_free); - - g_hash_table_insert (new_alias, - GUINT_TO_POINTER (handle), - g_strdup (new_name)); - - if (!tp_conn_iface_aliasing_set_aliases (priv->aliasing_iface, - new_alias, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't rename contact: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } - - g_hash_table_destroy (new_alias); + handle = g_array_index (handles, TpHandle, 0); + tp_cli_connection_call_request_channel (connection, -1, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_LIST, + handle, + TRUE, + tp_contact_list_publish_request_channel_cb, + GUINT_TO_POINTER (handle), NULL, + list); } static void -tp_contact_list_update_groups_foreach (gchar *object_path, - EmpathyTpGroup *group, - TpContactListData *data) +tp_contact_list_subscribe_group_members_changed_cb (TpChannel *channel, + gchar *message, + GArray *added, + GArray *removed, + GArray *local_pending, + GArray *remote_pending, + guint actor, + guint reason, + EmpathyTpContactList *list) { - gboolean is_member; - gboolean found = FALSE; - const gchar *group_name; - GList *l; - - is_member = empathy_tp_group_is_member (group, data->handle); - group_name = empathy_tp_group_get_name (group); - - for (l = data->new_groups; l; l = l->next) { - if (strcmp (group_name, l->data) == 0) { - found = TRUE; - break; - } + EmpathyTpContactListPriv *priv = GET_PRIV (list); + guint i; + + /* We now get the presence of those contacts, add them to members */ + if (added->len > 0) { + empathy_tp_contact_factory_get_from_handles (priv->factory, + added->len, (TpHandle*) added->data, + tp_contact_list_got_added_members_cb, NULL, NULL, + G_OBJECT (list)); } - if (is_member && !found) { - /* We are no longer member of this group */ - empathy_debug (DEBUG_DOMAIN, "Contact %d removed from group '%s'", - data->handle, group_name); - empathy_tp_group_remove_member (group, data->handle, ""); + /* Those contacts refuse to send us their presence, remove from members. */ + for (i = 0; i < removed->len; i++) { + tp_contact_list_remove_handle (list, priv->members, + g_array_index (added, TpHandle, i)); } - if (!is_member && found) { - /* We are now member of this group */ - empathy_debug (DEBUG_DOMAIN, "Contact %d added to group '%s'", - data->handle, group_name); - empathy_tp_group_add_member (group, data->handle, ""); + /* We want those contacts in our contact list but we don't get their + * presence yet. Add to members anyway. */ + if (remote_pending->len > 0) { + empathy_tp_contact_factory_get_from_handles (priv->factory, + remote_pending->len, (TpHandle*) remote_pending->data, + tp_contact_list_got_added_members_cb, NULL, NULL, + G_OBJECT (list)); } } -static EmpathyTpGroup * -tp_contact_list_get_group (EmpathyTpContactList *list, - const gchar *name) +static void +tp_contact_list_subscribe_request_channel_cb (TpConnection *connection, + const gchar *object_path, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - EmpathyTpGroup *group; - TpChan *group_channel; - GArray *handles; - guint group_handle; - char *group_object_path; - const char *names[2] = {name, NULL}; - GError *error = NULL; - - priv = GET_PRIV (list); - - group = g_hash_table_find (priv->groups, - (GHRFunc) tp_contact_list_find_group, - (gchar*) name); - if (group) { - return group; - } + EmpathyTpContactListPriv *priv = GET_PRIV (list); - empathy_debug (DEBUG_DOMAIN, "creating new group: %s", name); - - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_GROUP, - names, - &handles, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't request the creation of a new handle for group: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; + if (error) { + DEBUG ("Error: %s", error->message); + return; } - group_handle = g_array_index (handles, guint, 0); - g_array_free (handles, TRUE); - if (!tp_conn_request_channel (DBUS_G_PROXY (priv->tp_conn), - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - group_handle, - FALSE, - &group_object_path, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't request the creation of a new group channel: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } + priv->subscribe = tp_channel_new (connection, object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_LIST, + GPOINTER_TO_UINT (user_data), + NULL); - group_channel = tp_chan_new (tp_get_bus (), - dbus_g_proxy_get_bus_name (DBUS_G_PROXY (priv->tp_conn)), - group_object_path, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - group_handle); - - dbus_g_proxy_connect_signal (DBUS_G_PROXY (group_channel), - "Closed", - G_CALLBACK - (tp_contact_list_group_channel_closed_cb), - list, - NULL); - - group = empathy_tp_group_new (group_channel, priv->tp_conn); - g_hash_table_insert (priv->groups, group_object_path, group); - g_signal_connect (group, "members-added", - G_CALLBACK (tp_contact_list_group_members_added_cb), - list); - g_signal_connect (group, "members-removed", - G_CALLBACK (tp_contact_list_group_members_removed_cb), + /* TpChannel emits initial set of members just before being ready */ + g_signal_connect (priv->subscribe, "group-members-changed", + G_CALLBACK (tp_contact_list_subscribe_group_members_changed_cb), list); - - return group; } -static gboolean -tp_contact_list_find_group (gchar *key, - EmpathyTpGroup *group, - gchar *group_name) +static void +tp_contact_list_subscribe_request_handle_cb (TpConnection *connection, + const GArray *handles, + const GError *error, + gpointer user_data, + GObject *list) { - if (strcmp (group_name, empathy_tp_group_get_name (group)) == 0) { - return TRUE; + TpHandle handle; + + if (error) { + DEBUG ("Error: %s", error->message); + return; } - return FALSE; + handle = g_array_index (handles, TpHandle, 0); + tp_cli_connection_call_request_channel (connection, -1, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_LIST, + handle, + TRUE, + tp_contact_list_subscribe_request_channel_cb, + GUINT_TO_POINTER (handle), NULL, + list); } static void -tp_contact_list_get_groups_foreach (gchar *key, - EmpathyTpGroup *group, - GList **groups) +tp_contact_list_new_channel_cb (TpConnection *proxy, + const gchar *object_path, + const gchar *channel_type, + guint handle_type, + guint handle, + gboolean suppress_handler, + gpointer user_data, + GObject *list) { - const gchar *name; - - name = empathy_tp_group_get_name (group); - *groups = g_list_append (*groups, g_strdup (name)); + if (!suppress_handler) { + tp_contact_list_group_add_channel (EMPATHY_TP_CONTACT_LIST (list), + object_path, channel_type, + handle_type, handle); + } } static void -tp_contact_list_group_channel_closed_cb (TpChan *channel, - EmpathyTpContactList *list) +tp_contact_list_list_channels_cb (TpConnection *connection, + const GPtrArray *channels, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; + guint i; - priv = GET_PRIV (list); + if (error) { + DEBUG ("Error: %s", error->message); + return; + } + + for (i = 0; i < channels->len; i++) { + GValueArray *chan_struct; + const gchar *object_path; + const gchar *channel_type; + TpHandleType handle_type; + guint handle; + + chan_struct = g_ptr_array_index (channels, i); + object_path = g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); + channel_type = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); + handle_type = g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); + handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); - g_hash_table_remove (priv->groups, - dbus_g_proxy_get_path (DBUS_G_PROXY (channel))); + tp_contact_list_group_add_channel (EMPATHY_TP_CONTACT_LIST (list), + object_path, channel_type, + handle_type, handle); + } } static void -tp_contact_list_group_members_added_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_finalize (GObject *object) { EmpathyTpContactListPriv *priv; - GList *added_list, *l; - const gchar *group_name; + EmpathyTpContactList *list; + GHashTableIter iter; + gpointer channel; + list = EMPATHY_TP_CONTACT_LIST (object); priv = GET_PRIV (list); - group_name = empathy_tp_group_get_name (group); - added_list = empathy_tp_contact_list_get_from_handles (list, members); + DEBUG ("finalize: %p", object); - for (l = added_list; l; l = l->next) { - EmpathyContact *contact; + if (priv->subscribe) { + g_object_unref (priv->subscribe); + } + if (priv->publish) { + g_object_unref (priv->publish); + } - contact = EMPATHY_CONTACT (l->data); + if (priv->connection) { + g_object_unref (priv->connection); + } - tp_contact_list_block_contact (list, contact); - empathy_contact_add_group (contact, group_name); - tp_contact_list_unblock_contact (list, contact); + if (priv->factory) { + g_object_unref (priv->factory); + } - g_object_unref (contact); + g_hash_table_iter_init (&iter, priv->groups); + while (g_hash_table_iter_next (&iter, NULL, &channel)) { + g_signal_handlers_disconnect_by_func (channel, + tp_contact_list_group_invalidated_cb, list); } - g_list_free (added_list); + g_hash_table_destroy (priv->groups); + g_hash_table_destroy (priv->members); + g_hash_table_destroy (priv->pendings); + + G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); } static void -tp_contact_list_group_members_removed_cb (EmpathyTpGroup *group, - GArray *members, - guint actor_handle, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_constructed (GObject *list) { - EmpathyTpContactListPriv *priv; - GList *removed_list, *l; - const gchar *group_name; - - priv = GET_PRIV (list); - - group_name = empathy_tp_group_get_name (group); - removed_list = empathy_tp_contact_list_get_from_handles (list, members); - - for (l = removed_list; l; l = l->next) { - EmpathyContact *contact; - - contact = l->data; - tp_contact_list_block_contact (list, contact); - empathy_contact_remove_group (contact, group_name); - tp_contact_list_unblock_contact (list, contact); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + gchar *protocol_name = NULL; + const gchar *names[] = {NULL, NULL}; + + priv->factory = empathy_tp_contact_factory_dup_singleton (priv->connection); + + names[0] = "publish"; + tp_cli_connection_call_request_handles (priv->connection, + -1, + TP_HANDLE_TYPE_LIST, + names, + tp_contact_list_publish_request_handle_cb, + NULL, NULL, + G_OBJECT (list)); + names[0] = "subscribe"; + tp_cli_connection_call_request_handles (priv->connection, + -1, + TP_HANDLE_TYPE_LIST, + names, + tp_contact_list_subscribe_request_handle_cb, + NULL, NULL, + G_OBJECT (list)); + + tp_cli_connection_call_list_channels (priv->connection, -1, + tp_contact_list_list_channels_cb, + NULL, NULL, + list); - g_object_unref (contact); + tp_cli_connection_connect_to_new_channel (priv->connection, + tp_contact_list_new_channel_cb, + NULL, NULL, + list, NULL); + + /* Check for protocols that does not support contact groups. We can + * put all contacts into a special group in that case. + * FIXME: Default group should be an information in the profile */ + tp_connection_parse_object_path (priv->connection, &protocol_name, NULL); + if (!tp_strdiff (protocol_name, "local-xmpp")) { + priv->protocol_group = _("People nearby"); } + g_free (protocol_name); +} - g_list_free (removed_list); +static void +tp_contact_list_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (object); + + switch (param_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; } static void -tp_contact_list_get_info (EmpathyTpContactList *list, - GArray *handles) +tp_contact_list_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) { - EmpathyTpContactListPriv *priv; - GError *error = NULL; + EmpathyTpContactListPriv *priv = GET_PRIV (object); + + switch (param_id) { + case PROP_CONNECTION: + priv->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} - priv = GET_PRIV (list); +static void +empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); - if (priv->presence_iface) { - /* FIXME: We should use GetPresence instead */ - if (!tp_conn_iface_presence_request_presence (priv->presence_iface, - handles, &error)) { - empathy_debug (DEBUG_DOMAIN, - "Could not request presences: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } - } + object_class->finalize = tp_contact_list_finalize; + object_class->constructed = tp_contact_list_constructed; + object_class->get_property = tp_contact_list_get_property; + object_class->set_property = tp_contact_list_set_property; + + g_object_class_install_property (object_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "The Connection", + "The connection associated with the contact list", + TP_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); +} - if (priv->aliasing_iface) { - TpContactListAliasesRequestData *data; +static void +empathy_tp_contact_list_init (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (list, + EMPATHY_TYPE_TP_CONTACT_LIST, EmpathyTpContactListPriv); - data = g_slice_new (TpContactListAliasesRequestData); - data->list = list; - data->handles = g_memdup (handles->data, handles->len * sizeof (guint)); + list->priv = priv; - tp_conn_iface_aliasing_request_aliases_async (priv->aliasing_iface, - handles, - (tp_conn_iface_aliasing_request_aliases_reply) - tp_contact_list_request_aliases_cb, - data); - } + /* Map group's name to group's TpChannel. The group name string is owned + * by the TpChannel object */ + priv->groups = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, + (GDestroyNotify) g_object_unref); - if (priv->avatars_iface) { - guint i; + /* Map contact's handle to EmpathyContact object */ + priv->members = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) g_object_unref); - for (i = 0; i < handles->len; i++) { - guint handle; + /* Map contact's handle to EmpathyContact object */ + priv->pendings = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) g_object_unref); +} - handle = g_array_index (handles, gint, i); - tp_contact_list_request_avatar (list, handle); - } - } +EmpathyTpContactList * +empathy_tp_contact_list_new (TpConnection *connection) +{ + return g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, + "connection", connection, + NULL); } -static void -tp_contact_list_request_avatar (EmpathyTpContactList *list, - guint handle) +TpConnection * +empathy_tp_contact_list_get_connection (EmpathyTpContactList *list) { EmpathyTpContactListPriv *priv; + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + priv = GET_PRIV (list); - - /* We queue avatar requests to not send too many dbus async - * calls at once. If we don't we reach the dbus's limit of - * pending calls */ - priv->avatar_requests_queue = g_list_append (priv->avatar_requests_queue, - GUINT_TO_POINTER (handle)); - tp_contact_list_start_avatar_requests (list); + + return priv->connection; } static void -tp_contact_list_start_avatar_requests (EmpathyTpContactList *list) +tp_contact_list_add (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message) { - EmpathyTpContactListPriv *priv; - TpContactListAvatarRequestData *data; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; - while (n_avatar_requests < MAX_AVATAR_REQUESTS && - priv->avatar_requests_queue) { - data = g_slice_new (TpContactListAvatarRequestData); - data->list = list; - data->handle = GPOINTER_TO_UINT (priv->avatar_requests_queue->data); - - n_avatar_requests++; - priv->avatar_requests_queue = g_list_remove (priv->avatar_requests_queue, - priv->avatar_requests_queue->data); - - tp_conn_iface_avatars_request_avatar_async (priv->avatars_iface, - data->handle, - (tp_conn_iface_avatars_request_avatar_reply) - tp_contact_list_request_avatar_cb, - data); + handle = empathy_contact_get_handle (contact); + if (priv->subscribe) { + tp_cli_channel_interface_group_call_add_members (priv->subscribe, + -1, &handles, message, NULL, NULL, NULL, NULL); + } + if (priv->publish && + g_hash_table_lookup (priv->pendings, GUINT_TO_POINTER (handle))) { + tp_cli_channel_interface_group_call_add_members (priv->publish, + -1, &handles, message, NULL, NULL, NULL, NULL); } } static void -tp_contact_list_avatar_update_cb (DBusGProxy *proxy, - guint handle, - gchar *new_token, - EmpathyTpContactList *list) +tp_contact_list_remove (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *message) { - EmpathyTpContactListPriv *priv; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; - if (!g_hash_table_lookup (priv->contacts, GUINT_TO_POINTER (handle))) { - /* We don't know this contact, skip */ - return; + handle = empathy_contact_get_handle (contact); + if (priv->subscribe) { + tp_cli_channel_interface_group_call_remove_members (priv->subscribe, + -1, &handles, message, NULL, NULL, NULL, NULL); + } + if (priv->publish) { + tp_cli_channel_interface_group_call_remove_members (priv->publish, + -1, &handles, message, NULL, NULL, NULL, NULL); } +} - empathy_debug (DEBUG_DOMAIN, "Changing avatar for %d to %s", - handle, new_token); +static GList * +tp_contact_list_get_members (EmpathyContactList *list) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *ret; - tp_contact_list_request_avatar (list, handle); + ret = g_hash_table_get_values (priv->members); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; } -static void -tp_contact_list_request_avatar_cb (DBusGProxy *proxy, - GArray *avatar_data, - gchar *mime_type, - GError *error, - TpContactListAvatarRequestData *data) +static GList * +tp_contact_list_get_pendings (EmpathyContactList *list) { - EmpathyContact *contact; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *ret; + + ret = g_hash_table_get_values (priv->pendings); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; +} - contact = empathy_tp_contact_list_get_from_handle (data->list, data->handle); +static GList * +tp_contact_list_get_all_groups (EmpathyContactList *list) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *ret, *l; - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error requesting avatar for %s: %s", - empathy_contact_get_name (contact), - error ? error->message : "No error given"); - } else { - EmpathyAvatar *avatar; - - avatar = empathy_avatar_new (avatar_data->data, - avatar_data->len, - mime_type); - tp_contact_list_block_contact (data->list, contact); - empathy_contact_set_avatar (contact, avatar); - tp_contact_list_unblock_contact (data->list, contact); - empathy_avatar_unref (avatar); + ret = g_hash_table_get_keys (priv->groups); + for (l = ret; l; l = l->next) { + l->data = g_strdup (l->data); } - n_avatar_requests--; - tp_contact_list_start_avatar_requests (data->list); + if (priv->protocol_group) { + ret = g_list_prepend (ret, g_strdup (priv->protocol_group)); + } - g_object_unref (contact); - g_slice_free (TpContactListAvatarRequestData, data); + return ret; } -static void -tp_contact_list_aliases_update_cb (DBusGProxy *proxy, - GPtrArray *renamed_handlers, - EmpathyTpContactList *list) +static GList * +tp_contact_list_get_groups (EmpathyContactList *list, + EmpathyContact *contact) { - EmpathyTpContactListPriv *priv; - guint i; - - priv = GET_PRIV (list); - - for (i = 0; renamed_handlers->len > i; i++) { - guint handle; - const gchar *alias; - GValueArray *renamed_struct; - EmpathyContact *contact; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *ret = NULL; + GHashTableIter iter; + gpointer group_name; + gpointer channel; + TpHandle handle; - renamed_struct = g_ptr_array_index (renamed_handlers, i); - handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0)); - alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1)); - - if (!g_hash_table_lookup (priv->contacts, GUINT_TO_POINTER (handle))) { - /* We don't know this contact, skip */ - continue; - } + handle = empathy_contact_get_handle (contact); + g_hash_table_iter_init (&iter, priv->groups); + while (g_hash_table_iter_next (&iter, &group_name, &channel)) { + const TpIntSet *members; - if (G_STR_EMPTY (alias)) { - alias = NULL; + members = tp_channel_group_get_members (channel); + if (tp_intset_is_member (members, handle)) { + ret = g_list_prepend (ret, g_strdup (group_name)); } + } - contact = empathy_tp_contact_list_get_from_handle (list, handle); - tp_contact_list_block_contact (list, contact); - empathy_contact_set_name (contact, alias); - tp_contact_list_unblock_contact (list, contact); - g_object_unref (contact); - - empathy_debug (DEBUG_DOMAIN, "contact %d renamed to %s (update cb)", - handle, alias); + if (priv->protocol_group) { + ret = g_list_prepend (ret, g_strdup (priv->protocol_group)); } + + return ret; } static void -tp_contact_list_request_aliases_cb (DBusGProxy *proxy, - gchar **contact_names, - GError *error, - TpContactListAliasesRequestData *data) +tp_contact_list_add_to_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group_name) { - guint i = 0; - gchar **name; + TpHandle handle; + GArray *handles; - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error requesting aliases: %s", - error->message); - } - - for (name = contact_names; *name && !error; name++) { - EmpathyContact *contact; - - contact = empathy_tp_contact_list_get_from_handle (data->list, - data->handles[i]); - tp_contact_list_block_contact (data->list, contact); - empathy_contact_set_name (contact, *name); - tp_contact_list_unblock_contact (data->list, contact); - g_object_unref (contact); + handle = empathy_contact_get_handle (contact); + handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); + g_array_append_val (handles, handle); + tp_contact_list_group_add (EMPATHY_TP_CONTACT_LIST (list), + group_name, handles); +} - empathy_debug (DEBUG_DOMAIN, "contact %d renamed to %s (request cb)", - data->handles[i], *name); +static void +tp_contact_list_remove_from_group (EmpathyContactList *list, + EmpathyContact *contact, + const gchar *group_name) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; - i++; + channel = g_hash_table_lookup (priv->groups, group_name); + if (channel == NULL) { + return; } - g_free (data->handles); - g_slice_free (TpContactListAliasesRequestData, data); -} + handle = empathy_contact_get_handle (contact); + DEBUG ("remove contact %s (%d) from group %s", + empathy_contact_get_id (contact), handle, group_name); -static void -tp_contact_list_presence_update_cb (DBusGProxy *proxy, - GHashTable *handle_table, - EmpathyTpContactList *list) -{ - g_hash_table_foreach (handle_table, - (GHFunc) tp_contact_list_parse_presence_foreach, - list); + tp_cli_channel_interface_group_call_remove_members (channel, -1, + &handles, NULL, NULL, NULL, NULL, NULL); } static void -tp_contact_list_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyTpContactList *list) +tp_contact_list_rename_group (EmpathyContactList *list, + const gchar *old_group_name, + const gchar *new_group_name) { - EmpathyTpContactListPriv *priv; - GHashTable *presences_table; - EmpathyContact *contact; - EmpathyPresence *presence = NULL; - - priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + const TpIntSet *members; + GArray *handles; - if (!g_hash_table_lookup (priv->contacts, GUINT_TO_POINTER (handle))) { - /* We don't know this contact, skip */ + channel = g_hash_table_lookup (priv->groups, old_group_name); + if (channel == NULL) { return; } - contact = empathy_tp_contact_list_get_from_handle (list, handle); - presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1)); + DEBUG ("rename group %s to %s", old_group_name, new_group_name); - g_hash_table_foreach (presences_table, - (GHFunc) tp_contact_list_presences_table_foreach, - &presence); + /* Remove all members and close the old channel */ + members = tp_channel_group_get_members (channel); + handles = tp_intset_to_array (members); + tp_cli_channel_interface_group_call_remove_members (channel, -1, + handles, NULL, NULL, NULL, NULL, NULL); + tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL); - empathy_debug (DEBUG_DOMAIN, "Presence changed for %s (%d) to %s (%d)", - empathy_contact_get_name (contact), - handle, - presence ? empathy_presence_get_status (presence) : "unset", - presence ? empathy_presence_get_state (presence) : MC_PRESENCE_UNSET); - - tp_contact_list_block_contact (list, contact); - empathy_contact_set_presence (contact, presence); - tp_contact_list_unblock_contact (list, contact); - - g_object_unref (contact); + tp_contact_list_group_add (EMPATHY_TP_CONTACT_LIST (list), + new_group_name, handles); } static void -tp_contact_list_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - EmpathyPresence **presence) +tp_contact_list_remove_group (EmpathyContactList *list, + const gchar *group_name) { - McPresence state; - const GValue *message; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + const TpIntSet *members; + GArray *handles; - state = empathy_presence_state_from_str (state_str); - if ((state == MC_PRESENCE_UNSET) || (state == MC_PRESENCE_OFFLINE)) { + channel = g_hash_table_lookup (priv->groups, group_name); + if (channel == NULL) { return; } - if (*presence) { - g_object_unref (*presence); - *presence = NULL; - } - - *presence = empathy_presence_new (); - empathy_presence_set_state (*presence, state); + DEBUG ("remove group %s", group_name); - message = g_hash_table_lookup (presences_table, "message"); - if (message != NULL) { - empathy_presence_set_status (*presence, - g_value_get_string (message)); - } + /* Remove all members and close the channel */ + members = tp_channel_group_get_members (channel); + handles = tp_intset_to_array (members); + tp_cli_channel_interface_group_call_remove_members (channel, -1, + handles, NULL, NULL, NULL, NULL, NULL); + tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL); + g_array_free (handles, TRUE); } static void -tp_contact_list_status_changed_cb (MissionControl *mc, - TelepathyConnectionStatus status, - McPresence presence, - TelepathyConnectionStatusReason reason, - const gchar *unique_name, - EmpathyTpContactList *list) +tp_contact_list_iface_init (EmpathyContactListIface *iface) +{ + iface->add = tp_contact_list_add; + iface->remove = tp_contact_list_remove; + iface->get_members = tp_contact_list_get_members; + iface->get_pendings = tp_contact_list_get_pendings; + iface->get_all_groups = tp_contact_list_get_all_groups; + iface->get_groups = tp_contact_list_get_groups; + iface->add_to_group = tp_contact_list_add_to_group; + iface->remove_from_group = tp_contact_list_remove_from_group; + iface->rename_group = tp_contact_list_rename_group; + iface->remove_group = tp_contact_list_remove_group; +} + +gboolean +empathy_tp_contact_list_can_add (EmpathyTpContactList *list) { EmpathyTpContactListPriv *priv; - McAccount *account; + TpChannelGroupFlags flags; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), FALSE); priv = GET_PRIV (list); - account = mc_account_lookup (unique_name); - if (status != TP_CONN_STATUS_DISCONNECTED || - !empathy_account_equal (account, priv->account) || - !priv->tp_conn) { - g_object_unref (account); - return; - } + if (priv->subscribe == NULL) + return FALSE; - /* We are disconnected, do just like if the connection was destroyed */ - g_signal_handlers_disconnect_by_func (priv->tp_conn, - tp_contact_list_destroy_cb, - list); - tp_contact_list_destroy_cb (DBUS_G_PROXY (priv->tp_conn), list); + flags = tp_channel_group_get_flags (priv->subscribe); + return (flags & TP_CHANNEL_GROUP_FLAG_CAN_ADD) != 0; +} + +void +empathy_tp_contact_list_remove_all (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GHashTableIter iter; + gpointer contact; + + g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - g_object_unref (account); + /* Remove all contacts */ + g_hash_table_iter_init (&iter, priv->members); + while (g_hash_table_iter_next (&iter, NULL, &contact)) { + g_signal_emit_by_name (list, "members-changed", contact, + NULL, 0, NULL, + FALSE); + } + g_hash_table_remove_all (priv->members); + + g_hash_table_iter_init (&iter, priv->pendings); + while (g_hash_table_iter_next (&iter, NULL, &contact)) { + g_signal_emit_by_name (list, "pendings-changed", contact, + NULL, 0, NULL, + FALSE); + } + g_hash_table_remove_all (priv->pendings); }