X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-contact-list.c;h=6868e84a66d8911603f978a05912813b942bbe13;hp=b89384487faa9d22833dcc411d09e66ff7544354;hb=aa098bf904f8e85fa6aa44ffea99e1e027775d26;hpb=9064f1dfd44ba08a60385ef021b58011264956a1 diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c index b8938448..6868e84a 100644 --- a/libempathy/empathy-tp-contact-list.c +++ b/libempathy/empathy-tp-contact-list.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2007 Xavier Claessens - * Copyright (C) 2007-2008 Collabora Ltd. + * Copyright (C) 2007-2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,39 +23,33 @@ #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 DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CONTACT +#include "empathy-debug.h" -struct _EmpathyTpContactListPriv { - TpConn *tp_conn; - McAccount *account; - MissionControl *mc; +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpContactList) +typedef struct { + EmpathyTpContactFactory *factory; + TpConnection *connection; const gchar *protocol_group; - EmpathyTpGroup *publish; - EmpathyTpGroup *subscribe; - GList *members; - GList *pendings; - - GList *groups; - GHashTable *contacts_groups; -}; + TpChannel *publish; + TpChannel *subscribe; + GHashTable *members; + GHashTable *pendings; + GHashTable *groups; +} EmpathyTpContactListPriv; typedef enum { TP_CONTACT_LIST_TYPE_PUBLISH, @@ -63,687 +57,809 @@ typedef enum { TP_CONTACT_LIST_TYPE_UNKNOWN } TpContactListType; -static void empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass); -static void empathy_tp_contact_list_init (EmpathyTpContactList *list); static void tp_contact_list_iface_init (EmpathyContactListIface *iface); enum { - DESTROY, - LAST_SIGNAL + PROP_0, + PROP_CONNECTION, }; -static guint signals[LAST_SIGNAL]; - 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 -tp_contact_list_group_destroy_cb (EmpathyTpGroup *group, - EmpathyTpContactList *list) +tp_contact_list_group_invalidated_cb (TpChannel *channel, + guint domain, + gint code, + gchar *message, + EmpathyTpContactList *list) { EmpathyTpContactListPriv *priv = GET_PRIV (list); + const TpIntSet *members; + TpIntSetIter iter; + const gchar *group_name; + + group_name = tp_channel_get_identifier (channel); + DEBUG ("Group %s invalidated. Message: %s", group_name, message); + + /* 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; + + contact = g_hash_table_lookup (priv->members, + GUINT_TO_POINTER (iter.element)); + if (contact == NULL) { + continue; + } - empathy_debug (DEBUG_DOMAIN, "Group destroyed: %s", - empathy_tp_group_get_name (group)); + 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); + } - priv->groups = g_list_remove (priv->groups, group); - g_object_unref (group); + g_hash_table_remove (priv->groups, group_name); } static void -tp_contact_list_group_member_added_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_group_ready_cb (TpChannel *channel, + const GError *error, + gpointer list) { - EmpathyTpContactListPriv *priv = GET_PRIV (list); - const gchar *group_name; - GList **groups; + EmpathyTpContactListPriv *priv = GET_PRIV (list); - if (!g_list_find (priv->members, contact)) { + if (error) { + DEBUG ("Error: %s", error->message); + g_object_unref (channel); return; } + + DEBUG ("Add group %s", tp_channel_get_identifier (channel)); + g_hash_table_insert (priv->groups, + (gpointer) tp_channel_get_identifier (channel), + channel); - groups = g_hash_table_lookup (priv->contacts_groups, contact); - if (!groups) { - groups = g_slice_new0 (GList*); - g_hash_table_insert (priv->contacts_groups, - g_object_ref (contact), - groups); - } - - group_name = empathy_tp_group_get_name (group); - if (!g_list_find_custom (*groups, group_name, (GCompareFunc) strcmp)) { - empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) added to group %s", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - group_name); - *groups = g_list_prepend (*groups, g_strdup (group_name)); - g_signal_emit_by_name (list, "groups-changed", contact, - group_name, - TRUE); - } + g_signal_connect (channel, "invalidated", + G_CALLBACK (tp_contact_list_group_invalidated_cb), + list); } static void -tp_contact_list_group_member_removed_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +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 = GET_PRIV (list); - const gchar *group_name; - GList **groups, *l; + const gchar *group_name; + gint i; - if (!g_list_find (priv->members, contact)) { - return; - } + group_name = tp_channel_get_identifier (channel); - groups = g_hash_table_lookup (priv->contacts_groups, contact); - if (!groups) { - return; - } + for (i = 0; i < added->len; i++) { + EmpathyContact *contact; + TpHandle handle; + + handle = g_array_index (added, TpHandle, i); + contact = g_hash_table_lookup (priv->members, + GUINT_TO_POINTER (handle)); + if (contact == NULL) { + continue; + } + + 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); + } + + for (i = 0; i < removed->len; i++) { + EmpathyContact *contact; + TpHandle handle; + + handle = g_array_index (removed, TpHandle, i); + contact = g_hash_table_lookup (priv->members, + GUINT_TO_POINTER (handle)); + if (contact == NULL) { + continue; + } + + DEBUG ("Contact %s (%d) removed from group %s", + empathy_contact_get_id (contact), handle, group_name); - group_name = empathy_tp_group_get_name (group); - if ((l = g_list_find_custom (*groups, group_name, (GCompareFunc) strcmp))) { - empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) removed from group %s", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - group_name); - *groups = g_list_delete_link (*groups, l); g_signal_emit_by_name (list, "groups-changed", contact, group_name, FALSE); - } + } } -static EmpathyTpGroup * -tp_contact_list_find_group (EmpathyTpContactList *list, - const gchar *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 = GET_PRIV (list); - GList *l; + TpChannel *channel; - for (l = priv->groups; l; l = l->next) { - if (!tp_strdiff (group, empathy_tp_group_get_name (l->data))) { - return l->data; - } + /* 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; } - return NULL; + + channel = tp_channel_new (priv->connection, + object_path, channel_type, + handle_type, handle, NULL); + + /* 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); + + /* Give the ref to the callback */ + tp_channel_call_when_ready (channel, + tp_contact_list_group_ready_cb, + list); + + return channel; } -static TpContactListType -tp_contact_list_get_type (EmpathyTpContactList *list, - EmpathyTpGroup *group) +typedef struct { + GArray *handles; + TpHandle channel_handle; + guint ref_count; +} GroupAddData; + +static void +tp_contact_list_group_add_data_unref (gpointer user_data) { - const gchar *name; + GroupAddData *data = user_data; - name = empathy_tp_group_get_name (group); - if (!tp_strdiff (name, "subscribe")) { - return TP_CONTACT_LIST_TYPE_SUBSCRIBE; - } else if (!tp_strdiff (name, "publish")) { - return TP_CONTACT_LIST_TYPE_PUBLISH; + data->ref_count--; + if (data->ref_count == 0) { + g_array_free (data->handles, TRUE); + g_slice_free (GroupAddData, data); } - - return TP_CONTACT_LIST_TYPE_UNKNOWN; } static void -tp_contact_list_add_member (EmpathyTpContactList *list, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message) +tp_contact_list_group_add_ready_cb (TpChannel *channel, + const GError *error, + gpointer user_data) { - EmpathyTpContactListPriv *priv = GET_PRIV (list); - GList *l; - - /* Add to the list and emit signal */ - priv->members = g_list_prepend (priv->members, g_object_ref (contact)); - g_signal_emit_by_name (list, "members-changed", - contact, actor, reason, message, - TRUE); + GroupAddData *data = user_data; - /* This contact is now member, implicitly accept pending. */ - if (g_list_find (priv->pendings, contact)) { - empathy_tp_group_add_member (priv->publish, contact, ""); + if (error) { + tp_contact_list_group_add_data_unref (data); + return; } - /* Update groups of the contact */ - for (l = priv->groups; l; l = l->next) { - if (empathy_tp_group_is_member (l->data, contact)) { - tp_contact_list_group_member_added_cb (l->data, contact, - NULL, 0, NULL, - 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_added_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_group_request_channel_cb (TpConnection *connection, + const gchar *object_path, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv = GET_PRIV (list); - TpContactListType list_type; - - list_type = tp_contact_list_get_type (list, group); - empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) added to list type %d", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - list_type); + GroupAddData *data = user_data; + TpChannel *channel; - /* We now get the presence of that contact, add it to members */ - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && - !g_list_find (priv->members, contact)) { - tp_contact_list_add_member (list, contact, actor, reason, message); + if (error) { + DEBUG ("Error: %s", error->message); + return; } - /* We now send our presence to that contact, remove it from pendings */ - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && - g_list_find (priv->pendings, contact)) { - g_signal_emit_by_name (list, "pendings-changed", - contact, actor, reason, message, - FALSE); - priv->pendings = g_list_remove (priv->pendings, contact); - g_object_unref (contact); + 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_group_request_handles_cb (TpConnection *connection, + const GArray *handles, + const GError *error, + gpointer user_data, + GObject *list) +{ + GroupAddData *data = user_data; + + if (error) { + DEBUG ("Error: %s", error->message); + return; } + + 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); } +/* This function takes ownership of handles array */ static void -tp_contact_list_removed_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - guint reason, - const gchar *message, - EmpathyTpContactList *list) +tp_contact_list_group_add (EmpathyTpContactList *list, + const gchar *group_name, + GArray *handles) { EmpathyTpContactListPriv *priv = GET_PRIV (list); - TpContactListType list_type; - - list_type = tp_contact_list_get_type (list, group); - empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) removed from list type %d", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - list_type); - - /* This contact refuses to send us his presence, remove from members. */ - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && - g_list_find (priv->members, contact)) { - g_signal_emit_by_name (list, "members-changed", - contact, actor, reason, message, - FALSE); - priv->members = g_list_remove (priv->members, contact); - g_object_unref (contact); + 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; } - /* We refuse to send our presence to that contact, remove from pendings */ - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && - g_list_find (priv->pendings, contact)) { - g_signal_emit_by_name (list, "pendings-changed", - contact, actor, reason, message, - FALSE); - priv->pendings = g_list_remove (priv->pendings, contact); - g_object_unref (contact); - } + /* 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_pending_cb (EmpathyTpGroup *group, - EmpathyContact *contact, - EmpathyContact *actor, - 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 = GET_PRIV (list); - TpContactListType list_type; + guint i; + + if (error) { + DEBUG ("Error: %s", error->message); + return; + } - list_type = tp_contact_list_get_type (list, group); - empathy_debug (DEBUG_DOMAIN, "Contact %s (%d) pending in list type %d", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - list_type); + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact = contacts[i]; + TpHandle handle; - /* We want this contact in our contact list but we don't get its - * presence yet. Add to members anyway. */ - if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && - !g_list_find (priv->members, contact)) { - tp_contact_list_add_member (list, contact, actor, reason, message); - } - - /* This contact wants our presence, auto accept if he is member, - * otherwise he is pending. */ - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && - !g_list_find (priv->pendings, contact)) { - if (g_list_find (priv->members, contact)) { - empathy_tp_group_add_member (priv->publish, contact, ""); - } else { - priv->pendings = g_list_prepend (priv->pendings, - g_object_ref (contact)); - g_signal_emit_by_name (list, "pendings-changed", - contact, actor, reason, message, - TRUE); + handle = empathy_contact_get_handle (contact); + if (g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle))) + continue; + + /* 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); + + /* This contact is now member, implicitly accept pending. */ + if (g_hash_table_lookup (priv->pendings, GUINT_TO_POINTER (handle))) { + GArray handles = {(gchar*) &handle, 1}; + + tp_cli_channel_interface_group_call_add_members (priv->publish, + -1, &handles, NULL, NULL, NULL, NULL, NULL); } } } static void -tp_contact_list_newchannel_cb (DBusGProxy *proxy, - const gchar *object_path, - const gchar *channel_type, - TpHandleType handle_type, - guint channel_handle, - gboolean suppress_handler, - 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 = GET_PRIV (list); - EmpathyTpGroup *group; - TpChan *new_chan; - const gchar *bus_name; + guint i; - if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) != 0 || - suppress_handler) { + 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); - g_return_if_fail (TELEPATHY_IS_CHAN (new_chan)); - - group = empathy_tp_group_new (priv->account, new_chan); - g_object_unref (new_chan); - - if (handle_type == TP_HANDLE_TYPE_LIST) { - TpContactListType list_type; - GList *contacts, *l; - - list_type = tp_contact_list_get_type (list, group); - if (list_type == TP_CONTACT_LIST_TYPE_PUBLISH && !priv->publish) { - priv->publish = group; - - /* Publish is the list of contacts to who we send our - * presence. Makes no sense to be in remote-pending */ - g_signal_connect (group, "local-pending", - G_CALLBACK (tp_contact_list_pending_cb), - list); - - contacts = empathy_tp_group_get_local_pendings (group); - for (l = contacts; l; l = l->next) { - EmpathyPendingInfo *info = l->data; - - tp_contact_list_pending_cb (group, - info->member, - info->actor, - 0, - info->message, - list); - empathy_pending_info_free (info); - } - g_list_free (contacts); - } - else if (list_type == TP_CONTACT_LIST_TYPE_SUBSCRIBE && !priv->subscribe) { - priv->subscribe = group; - - /* Subscribe is the list of contacts from who we - * receive presence. Makes no sense to be in - * local-pending */ - g_signal_connect (group, "remote-pending", - G_CALLBACK (tp_contact_list_pending_cb), - list); - - contacts = empathy_tp_group_get_remote_pendings (group); - for (l = contacts; l; l = l->next) { - tp_contact_list_pending_cb (group, - l->data, - NULL, 0, - NULL, list); - g_object_unref (l->data); - } - g_list_free (contacts); - } else { - empathy_debug (DEBUG_DOMAIN, - "Type of contact list channel unknown " - "or aleady have that list: %s", - empathy_tp_group_get_name (group)); - g_object_unref (group); - return; - } - empathy_debug (DEBUG_DOMAIN, - "New contact list channel of type: %d", - list_type); - - g_signal_connect (group, "member-added", - G_CALLBACK (tp_contact_list_added_cb), - list); - g_signal_connect (group, "member-removed", - G_CALLBACK (tp_contact_list_removed_cb), - list); - - contacts = empathy_tp_group_get_members (group); - for (l = contacts; l; l = l->next) { - tp_contact_list_added_cb (group, - l->data, - NULL, 0, NULL, - list); - g_object_unref (l->data); - } - g_list_free (contacts); - } - else if (handle_type == TP_HANDLE_TYPE_GROUP) { - const gchar *group_name; - GList *contacts, *l; + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact = contacts[i]; + TpHandle handle; + const gchar *message; + TpChannelGroupChangeReason reason; - /* Check if already exists */ - group_name = empathy_tp_group_get_name (group); - if (tp_contact_list_find_group (list, group_name)) { - g_object_unref (group); - return; - } + handle = empathy_contact_get_handle (contact); + if (g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle))) { + GArray handles = {(gchar*) &handle, 1}; - empathy_debug (DEBUG_DOMAIN, "New server-side group channel: %s", - group_name); - - priv->groups = g_list_prepend (priv->groups, group); - - g_signal_connect (group, "member-added", - G_CALLBACK (tp_contact_list_group_member_added_cb), - list); - g_signal_connect (group, "member-removed", - G_CALLBACK (tp_contact_list_group_member_removed_cb), - list); - g_signal_connect (group, "destroy", - G_CALLBACK (tp_contact_list_group_destroy_cb), - list); - - contacts = empathy_tp_group_get_members (group); - for (l = contacts; l; l = l->next) { - tp_contact_list_group_member_added_cb (group, l->data, - NULL, 0, NULL, - list); - g_object_unref (l->data); + /* This contact is already member, auto accept. */ + tp_cli_channel_interface_group_call_add_members (priv->publish, + -1, &handles, NULL, NULL, NULL, NULL, NULL); + } + 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_list_free (contacts); - } else { - empathy_debug (DEBUG_DOMAIN, - "Unknown handle type (%d) for contact list channel", - handle_type); - g_object_unref (group); } } static void -tp_contact_list_destroy_cb (TpConn *tp_conn, - EmpathyTpContactList *list) +tp_contact_list_remove_handle (EmpathyTpContactList *list, + GHashTable *table, + TpHandle handle) { EmpathyTpContactListPriv *priv = GET_PRIV (list); - GList *l; - - empathy_debug (DEBUG_DOMAIN, "Account disconnected or CM crashed"); - - /* DBus proxie should NOT be used anymore */ - g_object_unref (priv->tp_conn); - priv->tp_conn = NULL; + EmpathyContact *contact; + const gchar *signal; + + if (table == priv->pendings) + signal = "pendings-changed"; + else if (table == priv->members) + signal = "members-changed"; + else + return; - /* Remove all contacts */ - for (l = priv->members; l; l = l->next) { - g_signal_emit_by_name (list, "members-changed", l->data, - NULL, 0, NULL, - FALSE); - g_object_unref (l->data); - } - for (l = priv->pendings; l; l = l->next) { - g_signal_emit_by_name (list, "pendings-changed", l->data, - NULL, 0, NULL, + 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 (l->data); + g_object_unref (contact); } - g_list_free (priv->members); - g_list_free (priv->pendings); - priv->members = NULL; - priv->pendings = NULL; - - /* Tell the world to not use us anymore */ - g_signal_emit (list, signals[DESTROY], 0); } static void -tp_contact_list_disconnect (EmpathyTpContactList *list) +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 = GET_PRIV (list); + guint i; - 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); + /* 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)); + } + + /* 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)); + } + + /* 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_status_changed_cb (MissionControl *mc, - TpConnectionStatus status, - McPresence presence, - TpConnectionStatusReason reason, - const gchar *unique_name, - 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 = GET_PRIV (list); - McAccount *account; - account = mc_account_lookup (unique_name); - if (status != TP_CONNECTION_STATUS_CONNECTED && - empathy_account_equal (account, priv->account)) { - /* We are disconnected */ - tp_contact_list_disconnect (list); - tp_contact_list_destroy_cb (priv->tp_conn, list); + if (error) { + DEBUG ("Error: %s", error->message); + return; } - g_object_unref (account); -} + priv->publish = tp_channel_new (connection, object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_LIST, + GPOINTER_TO_UINT (user_data), + NULL); -static void -tp_contact_list_group_list_free (GList **groups) -{ - g_list_foreach (*groups, (GFunc) g_free, NULL); - g_list_free (*groups); - g_slice_free (GList*, groups); + /* 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_finalize (GObject *object) +tp_contact_list_publish_request_handle_cb (TpConnection *connection, + const GArray *handles, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv; - EmpathyTpContactList *list; - - list = EMPATHY_TP_CONTACT_LIST (object); - priv = GET_PRIV (list); + TpHandle handle; - empathy_debug (DEBUG_DOMAIN, "finalize: %p", object); - - tp_contact_list_disconnect (list); + if (error) { + DEBUG ("Error: %s", error->message); + return; + } - if (priv->mc) { - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_list_status_changed_cb), + 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); - g_object_unref (priv->mc); - } +} - if (priv->subscribe) { - g_object_unref (priv->subscribe); +static void +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) +{ + 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 (priv->publish) { - g_object_unref (priv->publish); + + /* 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 (priv->account) { - g_object_unref (priv->account); + + /* 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)); } - if (priv->tp_conn) { - g_object_unref (priv->tp_conn); +} + +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 = GET_PRIV (list); + + if (error) { + DEBUG ("Error: %s", error->message); + return; } - g_hash_table_destroy (priv->contacts_groups); - g_list_foreach (priv->groups, (GFunc) g_object_unref, NULL); - g_list_free (priv->groups); - g_list_foreach (priv->members, (GFunc) g_object_unref, NULL); - g_list_free (priv->members); - g_list_foreach (priv->pendings, (GFunc) g_object_unref, NULL); - g_list_free (priv->pendings); + priv->subscribe = tp_channel_new (connection, object_path, + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_HANDLE_TYPE_LIST, + GPOINTER_TO_UINT (user_data), + NULL); - G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); + /* 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); } static void -empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) +tp_contact_list_subscribe_request_handle_cb (TpConnection *connection, + const GArray *handles, + const GError *error, + gpointer user_data, + GObject *list) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = tp_contact_list_finalize; + TpHandle handle; - 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); + if (error) { + DEBUG ("Error: %s", error->message); + return; + } - g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); + 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 -empathy_tp_contact_list_init (EmpathyTpContactList *list) +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) { + 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_setup (EmpathyTpContactList *list) +tp_contact_list_list_channels_cb (TpConnection *connection, + const GPtrArray *channels, + const GError *error, + gpointer user_data, + GObject *list) { - EmpathyTpContactListPriv *priv = GET_PRIV (list); - GPtrArray *channels; - guint i; - GError *error = NULL; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + guint i; - /* 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); return; } for (i = 0; i < channels->len; i++) { GValueArray *chan_struct; const gchar *object_path; - const gchar *chan_iface; + 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)); - chan_iface = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); + 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)); - tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), - object_path, chan_iface, - handle_type, handle, - FALSE, - list); - - g_value_array_free (chan_struct); + tp_contact_list_group_add_channel (EMPATHY_TP_CONTACT_LIST (list), + object_path, channel_type, + handle_type, handle); } - g_ptr_array_free (channels, TRUE); } -EmpathyTpContactList * -empathy_tp_contact_list_new (McAccount *account) +static void +tp_contact_list_finalize (GObject *object) { EmpathyTpContactListPriv *priv; EmpathyTpContactList *list; - MissionControl *mc; - TpConn *tp_conn = NULL; - McProfile *profile; - const gchar *protocol_name; + GHashTableIter iter; + gpointer channel; - g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + list = EMPATHY_TP_CONTACT_LIST (object); + priv = GET_PRIV (list); - mc = empathy_mission_control_new (); + DEBUG ("finalize: %p", object); - /* status==0 means CONNECTED */ - if (mission_control_get_connection_status (mc, account, NULL) == 0) { - tp_conn = mission_control_get_connection (mc, account, NULL); + if (priv->subscribe) { + g_object_unref (priv->subscribe); } - if (!tp_conn) { - /* The account is not connected, nothing to do. */ - g_object_unref (mc); - return NULL; + if (priv->publish) { + g_object_unref (priv->publish); } - list = g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, NULL); - priv = GET_PRIV (list); + if (priv->connection) { + g_object_unref (priv->connection); + } - priv->tp_conn = tp_conn; - priv->account = g_object_ref (account); - priv->mc = mc; - priv->contacts_groups = g_hash_table_new_full (empathy_contact_hash, - empathy_contact_equal, - (GDestroyNotify) g_object_unref, - (GDestroyNotify) tp_contact_list_group_list_free); + if (priv->factory) { + g_object_unref (priv->factory); + } + + 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_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_constructed (GObject *list) +{ + + 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); + + 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 */ - profile = mc_account_get_profile (account); - protocol_name = mc_profile_get_protocol_name (profile); - if (strcmp (protocol_name, "local-xmpp") == 0) { + tp_connection_parse_object_path (priv->connection, &protocol_name, NULL); + if (!tp_strdiff (protocol_name, "local-xmpp")) { priv->protocol_group = _("People nearby"); } - g_object_unref (profile); - - /* Connect signals */ - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_list_status_changed_cb), - list, NULL); - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_conn), "NewChannel", - G_CALLBACK (tp_contact_list_newchannel_cb), - list, NULL); - g_signal_connect (priv->tp_conn, "destroy", - G_CALLBACK (tp_contact_list_destroy_cb), - list); + g_free (protocol_name); +} - tp_contact_list_setup (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; + }; +} - return list; +static void +tp_contact_list_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + 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; + }; } -McAccount * -empathy_tp_contact_list_get_account (EmpathyTpContactList *list) +static void +empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + 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)); +} + +static void +empathy_tp_contact_list_init (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (list, + EMPATHY_TYPE_TP_CONTACT_LIST, EmpathyTpContactListPriv); + + list->priv = priv; + + /* 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); + + /* 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); + + /* 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); +} + +EmpathyTpContactList * +empathy_tp_contact_list_new (TpConnection *connection) +{ + return g_object_new (EMPATHY_TYPE_TP_CONTACT_LIST, + "connection", connection, + NULL); +} + +TpConnection * +empathy_tp_contact_list_get_connection (EmpathyTpContactList *list) { EmpathyTpContactListPriv *priv; @@ -751,7 +867,7 @@ empathy_tp_contact_list_get_account (EmpathyTpContactList *list) priv = GET_PRIV (list); - return priv->account; + return priv->connection; } static void @@ -760,12 +876,18 @@ tp_contact_list_add (EmpathyContactList *list, const gchar *message) { EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - empathy_tp_group_add_member (priv->subscribe, contact, message); - if (g_list_find (priv->pendings, contact)) { - empathy_tp_group_add_member (priv->publish, contact, message); + 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); } } @@ -775,55 +897,58 @@ tp_contact_list_remove (EmpathyContactList *list, const gchar *message) { EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - empathy_tp_group_remove_member (priv->subscribe, contact, message); - empathy_tp_group_remove_member (priv->publish, contact, message); + 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); + } } static GList * tp_contact_list_get_members (EmpathyContactList *list) { EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *ret; - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - g_list_foreach (priv->members, (GFunc) g_object_ref, NULL); - return g_list_copy (priv->members); + ret = g_hash_table_get_values (priv->members); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; } static GList * tp_contact_list_get_pendings (EmpathyContactList *list) { EmpathyTpContactListPriv *priv = GET_PRIV (list); + GList *ret; - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - g_list_foreach (priv->pendings, (GFunc) g_object_ref, NULL); - return g_list_copy (priv->pendings); + ret = g_hash_table_get_values (priv->pendings); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; } static GList * tp_contact_list_get_all_groups (EmpathyContactList *list) { EmpathyTpContactListPriv *priv = GET_PRIV (list); - GList *groups = NULL, *l; + GList *ret, *l; - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); - - if (priv->protocol_group) { - groups = g_list_prepend (groups, g_strdup (priv->protocol_group)); + ret = g_hash_table_get_keys (priv->groups); + for (l = ret; l; l = l->next) { + l->data = g_strdup (l->data); } - for (l = priv->groups; l; l = l->next) { - const gchar *name; - - name = empathy_tp_group_get_name (l->data); - groups = g_list_prepend (groups, g_strdup (name)); + if (priv->protocol_group) { + ret = g_list_prepend (ret, g_strdup (priv->protocol_group)); } - return groups; + return ret; } static GList * @@ -831,175 +956,119 @@ tp_contact_list_get_groups (EmpathyContactList *list, EmpathyContact *contact) { EmpathyTpContactListPriv *priv = GET_PRIV (list); - GList **groups; - GList *ret = NULL, *l; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), NULL); + GList *ret = NULL; + GHashTableIter iter; + gpointer group_name; + gpointer channel; + TpHandle handle; + + 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; + + members = tp_channel_group_get_members (channel); + if (tp_intset_is_member (members, handle)) { + ret = g_list_prepend (ret, g_strdup (group_name)); + } + } if (priv->protocol_group) { ret = g_list_prepend (ret, g_strdup (priv->protocol_group)); } - groups = g_hash_table_lookup (priv->contacts_groups, contact); - if (!groups) { - return ret; - } - - for (l = *groups; l; l = l->next) { - ret = g_list_prepend (ret, g_strdup (l->data)); - } - - return ret; } -static EmpathyTpGroup * -tp_contact_list_get_group (EmpathyTpContactList *list, - const gchar *group) -{ - EmpathyTpContactListPriv *priv = GET_PRIV (list); - EmpathyTpGroup *tp_group; - gchar *object_path; - guint handle; - GArray *handles; - const char *names[2] = {group, NULL}; - GError *error = NULL; - - tp_group = tp_contact_list_find_group (list, group); - if (tp_group) { - return tp_group; - } - - empathy_debug (DEBUG_DOMAIN, "creating new group: %s", group); - - if (!tp_conn_request_handles (DBUS_G_PROXY (priv->tp_conn), - TP_HANDLE_TYPE_GROUP, - names, - &handles, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to RequestHandles: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - 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, - handle, - TRUE, - &object_path, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Failed to RequestChannel: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - return NULL; - } - - tp_contact_list_newchannel_cb (DBUS_G_PROXY (priv->tp_conn), - object_path, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_GROUP, - handle, FALSE, - list); - g_free (object_path); - - return tp_contact_list_find_group (list, group); -} - static void tp_contact_list_add_to_group (EmpathyContactList *list, EmpathyContact *contact, - const gchar *group) + const gchar *group_name) { - EmpathyTpGroup *tp_group; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); - - tp_group = tp_contact_list_get_group (EMPATHY_TP_CONTACT_LIST (list), - group); - - empathy_tp_group_add_member (tp_group, contact, ""); + TpHandle handle; + GArray *handles; + + 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); } static void tp_contact_list_remove_from_group (EmpathyContactList *list, EmpathyContact *contact, - const gchar *group) + const gchar *group_name) { - EmpathyTpGroup *tp_group; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + TpHandle handle; + GArray handles = {(gchar *) &handle, 1}; - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + channel = g_hash_table_lookup (priv->groups, group_name); + if (channel == NULL) { + return; + } - tp_group = tp_contact_list_find_group (EMPATHY_TP_CONTACT_LIST (list), - group); + handle = empathy_contact_get_handle (contact); + DEBUG ("remove contact %s (%d) from group %s", + empathy_contact_get_id (contact), handle, group_name); - if (tp_group) { - empathy_tp_group_remove_member (tp_group, contact, ""); - } + tp_cli_channel_interface_group_call_remove_members (channel, -1, + &handles, NULL, NULL, NULL, NULL, NULL); } static void tp_contact_list_rename_group (EmpathyContactList *list, - const gchar *old_group, - const gchar *new_group) + const gchar *old_group_name, + const gchar *new_group_name) { - EmpathyTpGroup *tp_group; - GList *members; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + const TpIntSet *members; + GArray *handles; - tp_group = tp_contact_list_find_group (EMPATHY_TP_CONTACT_LIST (list), - old_group); - if (!tp_group) { + channel = g_hash_table_lookup (priv->groups, old_group_name); + if (channel == NULL) { return; } - empathy_debug (DEBUG_DOMAIN, "rename group %s to %s", old_group, new_group); - - /* Remove all members from the old group */ - members = empathy_tp_group_get_members (tp_group); - empathy_tp_group_remove_members (tp_group, members, ""); - empathy_tp_group_close (tp_group); + DEBUG ("rename group %s to %s", old_group_name, new_group_name); - /* Add all members to the new group */ - tp_group = tp_contact_list_get_group (EMPATHY_TP_CONTACT_LIST (list), - new_group); - empathy_tp_group_add_members (tp_group, members, ""); + /* 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); - g_list_foreach (members, (GFunc) g_object_unref, NULL); - g_list_free (members); + tp_contact_list_group_add (EMPATHY_TP_CONTACT_LIST (list), + new_group_name, handles); } static void tp_contact_list_remove_group (EmpathyContactList *list, - const gchar *group) + const gchar *group_name) { - EmpathyTpGroup *tp_group; - GList *members; - - g_return_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list)); + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *channel; + const TpIntSet *members; + GArray *handles; - tp_group = tp_contact_list_find_group (EMPATHY_TP_CONTACT_LIST (list), - group); - - if (!tp_group) { + channel = g_hash_table_lookup (priv->groups, group_name); + if (channel == NULL) { return; } - empathy_debug (DEBUG_DOMAIN, "remove group %s", group); - - /* Remove all members of the group */ - members = empathy_tp_group_get_members (tp_group); - empathy_tp_group_remove_members (tp_group, members, ""); - empathy_tp_group_close (tp_group); + DEBUG ("remove group %s", group_name); - g_list_foreach (members, (GFunc) g_object_unref, NULL); - g_list_free (members); + /* 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 @@ -1017,3 +1086,47 @@ tp_contact_list_iface_init (EmpathyContactListIface *iface) iface->remove_group = tp_contact_list_remove_group; } +gboolean +empathy_tp_contact_list_can_add (EmpathyTpContactList *list) +{ + EmpathyTpContactListPriv *priv; + TpChannelGroupFlags flags; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), FALSE); + + priv = GET_PRIV (list); + + if (priv->subscribe == NULL) + return FALSE; + + 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)); + + /* 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); +} +