X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-contact-list.c;h=95ec3bea2109631ee34d9ef2d22b599996792b68;hp=876e356f4ec54a8b6e270fdab30d625d44efdd68;hb=940d0e9778828657a6ffbcadd35a8a84d706ac70;hpb=e4d54d6de30699fca0374e8b99f31c5ba1f70e78 diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c index 876e356f..95ec3bea 100644 --- a/libempathy/empathy-tp-contact-list.c +++ b/libempathy/empathy-tp-contact-list.c @@ -16,7 +16,7 @@ * 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 */ @@ -29,6 +29,7 @@ #include #include #include +#include #include "empathy-tp-contact-list.h" #include "empathy-tp-contact-factory.h" @@ -42,13 +43,18 @@ typedef struct { EmpathyTpContactFactory *factory; TpConnection *connection; - const gchar *protocol_group; TpChannel *publish; TpChannel *subscribe; - GHashTable *members; - GHashTable *pendings; - GHashTable *groups; + TpChannel *stored; + GHashTable *members; /* handle -> EmpathyContact */ + GHashTable *pendings; /* handle -> EmpathyContact */ + GHashTable *groups; /* group name -> TpChannel */ + GHashTable *add_to_group; /* group name -> GArray of handles */ + + EmpathyContactListFlags flags; + + TpProxySignalConnection *new_channels_sig; } EmpathyTpContactListPriv; typedef enum { @@ -69,11 +75,8 @@ G_DEFINE_TYPE_WITH_CODE (EmpathyTpContactList, empathy_tp_contact_list, G_TYPE_O tp_contact_list_iface_init)); static void -tp_contact_list_group_invalidated_cb (TpChannel *channel, - guint domain, - gint code, - gchar *message, - EmpathyTpContactList *list) +tp_contact_list_forget_group (EmpathyTpContactList *list, + TpChannel *channel) { EmpathyTpContactListPriv *priv = GET_PRIV (list); const TpIntSet *members; @@ -81,7 +84,6 @@ tp_contact_list_group_invalidated_cb (TpChannel *channel, 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); @@ -102,47 +104,34 @@ tp_contact_list_group_invalidated_cb (TpChannel *channel, group_name, FALSE); } - - g_hash_table_remove (priv->groups, group_name); } static void -tp_contact_list_group_ready_cb (TpChannel *channel, - const GError *error, - gpointer list) +tp_contact_list_group_invalidated_cb (TpChannel *channel, + guint domain, + gint code, + gchar *message, + EmpathyTpContactList *list) { EmpathyTpContactListPriv *priv = GET_PRIV (list); + const gchar *group_name; - 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); + group_name = tp_channel_get_identifier (channel); + DEBUG ("Group %s invalidated. Message: %s", group_name, message); - g_signal_connect (channel, "invalidated", - G_CALLBACK (tp_contact_list_group_invalidated_cb), - list); + tp_contact_list_forget_group (list, channel); + + g_hash_table_remove (priv->groups, group_name); } 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) +contacts_added_to_group (EmpathyTpContactList *list, + TpChannel *channel, + GArray *added) { - EmpathyTpContactListPriv *priv = GET_PRIV (list); + EmpathyTpContactListPriv *priv = GET_PRIV (list); const gchar *group_name; - gint i; + guint i; group_name = tp_channel_get_identifier (channel); @@ -162,7 +151,27 @@ tp_contact_list_group_members_changed_cb (TpChannel *channel, g_signal_emit_by_name (list, "groups-changed", contact, group_name, TRUE); - } + } +} + +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 = GET_PRIV (list); + const gchar *group_name; + guint i; + + contacts_added_to_group (list, channel, added); + + group_name = tp_channel_get_identifier (channel); for (i = 0; i < removed->len; i++) { EmpathyContact *contact; @@ -181,10 +190,76 @@ tp_contact_list_group_members_changed_cb (TpChannel *channel, g_signal_emit_by_name (list, "groups-changed", contact, group_name, FALSE); - } + } } -static TpChannel * +static void +tp_contact_list_group_ready_cb (TpChannel *channel, + const GError *error, + gpointer list) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + TpChannel *old_group; + const gchar *group_name; + const TpIntSet *members; + GArray *arr; + + if (error) { + DEBUG ("Error: %s", error->message); + g_object_unref (channel); + return; + } + + group_name = tp_channel_get_identifier (channel); + + /* If there's already a group with this name in the table, we can't + * just let it be replaced. Replacing it causes it to be unreffed, + * which causes it to be invalidated (see + * ), which causes + * it to be removed from the hash table again, which causes it to be + * unreffed again. + */ + old_group = g_hash_table_lookup (priv->groups, group_name); + + if (old_group != NULL) { + DEBUG ("Discarding old group %s (%p)", group_name, old_group); + g_hash_table_steal (priv->groups, group_name); + tp_contact_list_forget_group (list, old_group); + g_object_unref (old_group); + } + + g_hash_table_insert (priv->groups, (gpointer) group_name, channel); + DEBUG ("Group %s added", group_name); + + g_signal_connect (channel, "group-members-changed", + G_CALLBACK (tp_contact_list_group_members_changed_cb), + list); + + g_signal_connect (channel, "invalidated", + G_CALLBACK (tp_contact_list_group_invalidated_cb), + list); + + if (priv->add_to_group) { + GArray *handles; + + handles = g_hash_table_lookup (priv->add_to_group, group_name); + if (handles) { + DEBUG ("Adding initial members to group %s", group_name); + tp_cli_channel_interface_group_call_add_members (channel, + -1, handles, NULL, NULL, NULL, NULL, NULL); + g_hash_table_remove (priv->add_to_group, group_name); + } + } + + /* Get initial members of the group */ + members = tp_channel_group_get_members (channel); + g_assert (members != NULL); + arr = tp_intset_to_array (members); + contacts_added_to_group (list, channel, arr); + g_array_free (arr, TRUE); +} + +static void tp_contact_list_group_add_channel (EmpathyTpContactList *list, const gchar *object_path, const gchar *channel_type, @@ -197,58 +272,17 @@ tp_contact_list_group_add_channel (EmpathyTpContactList *list, /* 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; } 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; -} - -typedef struct { - GArray *handles; - TpHandle channel_handle; - guint ref_count; -} GroupAddData; - -static void -tp_contact_list_group_add_data_unref (gpointer user_data) -{ - GroupAddData *data = user_data; - - if (--data->ref_count == 0) { - g_array_free (data->handles, TRUE); - g_slice_free (GroupAddData, data); - } -} - -static void -tp_contact_list_group_add_ready_cb (TpChannel *channel, - const GError *error, - gpointer user_data) -{ - GroupAddData *data = user_data; - - if (error) { - tp_contact_list_group_add_data_unref (data); - return; - } - - 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 @@ -258,24 +292,12 @@ tp_contact_list_group_request_channel_cb (TpConnection *connection, gpointer user_data, GObject *list) { - GroupAddData *data = user_data; - TpChannel *channel; - + /* The new channel will be handled in NewChannel cb. Here we only + * handle the error if RequestChannel failed */ if (error) { DEBUG ("Error: %s", error->message); return; } - - 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 @@ -285,25 +307,25 @@ tp_contact_list_group_request_handles_cb (TpConnection *connection, gpointer user_data, GObject *list) { - GroupAddData *data = user_data; + TpHandle channel_handle; if (error) { DEBUG ("Error: %s", error->message); return; } - data->channel_handle = g_array_index (handles, TpHandle, 1); - data->ref_count++; + channel_handle = g_array_index (handles, TpHandle, 0); tp_cli_connection_call_request_channel (connection, -1, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, TP_HANDLE_TYPE_GROUP, - data->channel_handle, + channel_handle, TRUE, tp_contact_list_group_request_channel_cb, - data, tp_contact_list_group_add_data_unref, + NULL, NULL, list); } +/* This function takes ownership of handles array */ static void tp_contact_list_group_add (EmpathyTpContactList *list, const gchar *group_name, @@ -312,8 +334,8 @@ tp_contact_list_group_add (EmpathyTpContactList *list, 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, @@ -322,27 +344,41 @@ tp_contact_list_group_add (EmpathyTpContactList *list, return; } - data = g_slice_new0 (GroupAddData); - data->handles = handles; - data->ref_count = 1; + /* That group does not exist yet, we have to: + * 1) Request an handle for the group name + * 2) Request a channel + * 3) When NewChannel is emitted, add handles in members + */ + g_hash_table_insert (priv->add_to_group, + g_strdup (group_name), + handles); 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, + NULL, NULL, G_OBJECT (list)); } static void tp_contact_list_got_added_members_cb (EmpathyTpContactFactory *factory, - GList *contacts, + 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); - GList *l; + guint i; - for (l = contacts; l; l = l->next) { - EmpathyContact *contact = l->data; + if (error) { + DEBUG ("Error: %s", error->message); + return; + } + + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact = contacts[i]; TpHandle handle; handle = empathy_contact_get_handle (contact); @@ -357,7 +393,7 @@ tp_contact_list_got_added_members_cb (EmpathyTpContactFactory *factory, /* This contact is now member, implicitly accept pending. */ if (g_hash_table_lookup (priv->pendings, GUINT_TO_POINTER (handle))) { - GArray handles = {(gchar*) &handle, 1}; + GArray handles = {(gchar *) &handle, 1}; tp_cli_channel_interface_group_call_add_members (priv->publish, -1, &handles, NULL, NULL, NULL, NULL, NULL); @@ -367,22 +403,31 @@ tp_contact_list_got_added_members_cb (EmpathyTpContactFactory *factory, static void tp_contact_list_got_local_pending_cb (EmpathyTpContactFactory *factory, - GList *contacts, - gpointer info, + 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); - GList *l; + guint i; + + if (error) { + DEBUG ("Error: %s", error->message); + return; + } - for (l = contacts; l; l = l->next) { - EmpathyContact *contact = l->data; + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact = contacts[i]; TpHandle handle; const gchar *message; TpChannelGroupChangeReason reason; handle = empathy_contact_get_handle (contact); if (g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle))) { - GArray handles = {(gchar*) &handle, 1}; + GArray handles = {(gchar *) &handle, 1}; /* This contact is already member, auto accept. */ tp_cli_channel_interface_group_call_add_members (priv->publish, @@ -409,12 +454,12 @@ tp_contact_list_remove_handle (EmpathyTpContactList *list, { EmpathyTpContactListPriv *priv = GET_PRIV (list); EmpathyContact *contact; - const gchar *signal; + const gchar *sig; if (table == priv->pendings) - signal = "pendings-changed"; + sig = "pendings-changed"; else if (table == priv->members) - signal = "members-changed"; + sig = "members-changed"; else return; @@ -422,7 +467,7 @@ tp_contact_list_remove_handle (EmpathyTpContactList *list, 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, + g_signal_emit_by_name (list, sig, contact, 0, 0, NULL, FALSE); g_object_unref (contact); } @@ -451,25 +496,25 @@ tp_contact_list_publish_group_members_changed_cb (TpChannel *channel, /* 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)); + g_array_index (removed, 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, + local_pending->len, (TpHandle *) local_pending->data, tp_contact_list_got_local_pending_cb, NULL, NULL, G_OBJECT (list)); } } static void -tp_contact_list_publish_request_channel_cb (TpConnection *connection, - const gchar *object_path, - const GError *error, - gpointer user_data, - GObject *list) +tp_contact_list_get_alias_flags_cb (TpConnection *connection, + guint flags, + const GError *error, + gpointer user_data, + GObject *list) { EmpathyTpContactListPriv *priv = GET_PRIV (list); @@ -478,41 +523,48 @@ tp_contact_list_publish_request_channel_cb (TpConnection *connection, return; } - priv->publish = tp_channel_new (connection, object_path, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_LIST, - GPOINTER_TO_UINT (user_data), - NULL); - - /* 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); + if (flags & TP_CONNECTION_ALIAS_FLAG_USER_SET) { + priv->flags |= EMPATHY_CONTACT_LIST_CAN_ALIAS; + } } static void -tp_contact_list_publish_request_handle_cb (TpConnection *connection, - const GArray *handles, - const GError *error, - gpointer user_data, - GObject *list) +tp_contact_list_get_requestablechannelclasses_cb (TpProxy *connection, + const GValue *value, + const GError *error, + gpointer user_data, + GObject *list) { - TpHandle handle; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GPtrArray *classes; + guint i; if (error) { DEBUG ("Error: %s", error->message); return; } - 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); + classes = g_value_get_boxed (value); + for (i = 0; i < classes->len; i++) { + GValueArray *class = g_ptr_array_index (classes, i); + GHashTable *props; + const char *channel_type; + guint handle_type; + + props = g_value_get_boxed (g_value_array_get_nth (class, 0)); + + channel_type = tp_asv_get_string (props, + TP_IFACE_CHANNEL ".ChannelType"); + handle_type = tp_asv_get_uint32 (props, + TP_IFACE_CHANNEL ".TargetHandleType", NULL); + + if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST) && + handle_type == TP_HANDLE_TYPE_GROUP) { + DEBUG ("Got channel class for a contact group"); + priv->flags |= EMPATHY_CONTACT_LIST_CAN_GROUP; + break; + } + } } static void @@ -532,7 +584,7 @@ tp_contact_list_subscribe_group_members_changed_cb (TpChannel *channel, /* 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, + added->len, (TpHandle *) added->data, tp_contact_list_got_added_members_cb, NULL, NULL, G_OBJECT (list)); } @@ -540,70 +592,19 @@ tp_contact_list_subscribe_group_members_changed_cb (TpChannel *channel, /* 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)); + g_array_index (removed, TpHandle, i)); } - /* We want those contacts in our contact list but we don't get their + /* 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, + remote_pending->len, (TpHandle *) remote_pending->data, tp_contact_list_got_added_members_cb, NULL, NULL, G_OBJECT (list)); } } -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; - } - - priv->subscribe = tp_channel_new (connection, object_path, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, - TP_HANDLE_TYPE_LIST, - GPOINTER_TO_UINT (user_data), - NULL); - - /* 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 -tp_contact_list_subscribe_request_handle_cb (TpConnection *connection, - const GArray *handles, - const GError *error, - gpointer user_data, - GObject *list) -{ - TpHandle handle; - - if (error) { - DEBUG ("Error: %s", error->message); - return; - } - - 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_new_channel_cb (TpConnection *proxy, const gchar *object_path, @@ -671,6 +672,9 @@ tp_contact_list_finalize (GObject *object) if (priv->publish) { g_object_unref (priv->publish); } + if (priv->stored) { + g_object_unref (priv->stored); + } if (priv->connection) { g_object_unref (priv->connection); @@ -689,36 +693,203 @@ tp_contact_list_finalize (GObject *object) g_hash_table_destroy (priv->groups); g_hash_table_destroy (priv->members); g_hash_table_destroy (priv->pendings); + g_hash_table_destroy (priv->add_to_group); G_OBJECT_CLASS (empathy_tp_contact_list_parent_class)->finalize (object); } +static gboolean +received_all_list_channels (EmpathyTpContactList *self) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (self); + + return (priv->stored != NULL && priv->publish != NULL && + priv->subscribe != NULL); +} + static void -tp_contact_list_constructed (GObject *list) +got_list_channel (EmpathyTpContactList *list, + TpChannel *channel) { + EmpathyTpContactListPriv *priv = GET_PRIV (list); + const gchar *id; + + /* We requested that channel by providing TargetID property, so it's + * guaranteed that tp_channel_get_identifier will return it. */ + id = tp_channel_get_identifier (channel); + + /* TpChannel emits initial set of members just before being ready */ + if (!tp_strdiff (id, "stored")) { + if (priv->stored != NULL) + return; + priv->stored = g_object_ref (channel); + } else if (!tp_strdiff (id, "publish")) { + if (priv->publish != NULL) + return; + priv->publish = g_object_ref (channel); + g_signal_connect (priv->publish, "group-members-changed", + G_CALLBACK (tp_contact_list_publish_group_members_changed_cb), + list); + } else if (!tp_strdiff (id, "subscribe")) { + if (priv->subscribe != NULL) + return; + priv->subscribe = g_object_ref (channel); + g_signal_connect (priv->subscribe, "group-members-changed", + G_CALLBACK (tp_contact_list_subscribe_group_members_changed_cb), + list); + } + + if (received_all_list_channels (list) && priv->new_channels_sig != NULL) { + /* We don't need to watch NewChannels anymore */ + tp_proxy_signal_connection_disconnect (priv->new_channels_sig); + priv->new_channels_sig = NULL; + } +} + +static void +list_ensure_channel_cb (TpConnection *conn, + gboolean yours, + const gchar *path, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + EmpathyTpContactList *list = user_data; + TpChannel *channel; + if (error != NULL) { + DEBUG ("failed: %s\n", error->message); + return; + } + + channel = tp_channel_new_from_properties (conn, path, properties, NULL); + got_list_channel (list, channel); + g_object_unref (channel); +} + +static void +new_channels_cb (TpConnection *conn, + const GPtrArray *channels, + gpointer user_data, + GObject *weak_object) +{ + EmpathyTpContactList *list = EMPATHY_TP_CONTACT_LIST (weak_object); + guint i; + + for (i = 0; i < channels->len ; i++) { + GValueArray *arr = g_ptr_array_index (channels, i); + const gchar *path; + GHashTable *properties; + const gchar *id; + TpChannel *channel; + + path = g_value_get_boxed (g_value_array_get_nth (arr, 0)); + properties = g_value_get_boxed (g_value_array_get_nth (arr, 1)); + + if (tp_strdiff (tp_asv_get_string (properties, + TP_IFACE_CHANNEL ".ChannelType"), + TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) + return; + + if (tp_asv_get_uint32 (properties, + TP_IFACE_CHANNEL ".TargetHandleType", NULL) + != TP_HANDLE_TYPE_LIST) + return; + + id = tp_asv_get_string (properties, + TP_IFACE_CHANNEL ".TargetID"); + if (id == NULL) + return; + + channel = tp_channel_new_from_properties (conn, path, + properties, NULL); + got_list_channel (list, channel); + g_object_unref (channel); + } +} + +static void +conn_ready_cb (TpConnection *connection, + const GError *error, + gpointer data) +{ + EmpathyTpContactList *list = data; + EmpathyTpContactListPriv *priv = GET_PRIV (list); + GHashTable *request; + + if (error != NULL) { + DEBUG ("failed: %s", error->message); + goto out; + } + + request = tp_asv_new ( + TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, + TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_UINT, TP_HANDLE_TYPE_LIST, + NULL); + + /* Watch the NewChannels signal so if ensuring list channels fails (for + * example because the server is slow and the D-Bus call timeouts before CM + * fetches the roster), we have a chance to get them later. */ + priv->new_channels_sig = + tp_cli_connection_interface_requests_connect_to_new_channels ( + priv->connection, new_channels_cb, NULL, NULL, G_OBJECT (list), NULL); + + /* Request the 'stored' list. */ + tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "stored"); + tp_cli_connection_interface_requests_call_ensure_channel (priv->connection, + -1, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list)); + + /* Request the 'publish' list. */ + tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "publish"); + tp_cli_connection_interface_requests_call_ensure_channel (priv->connection, + -1, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list)); + + /* Request the 'subscribe' list. */ + tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "subscribe"); + tp_cli_connection_interface_requests_call_ensure_channel (priv->connection, + -1, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list)); + + g_hash_table_unref (request); +out: + g_object_unref (list); +} + +static void +tp_contact_list_constructed (GObject *list) +{ EmpathyTpContactListPriv *priv = GET_PRIV (list); - const 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)); + /* call GetAliasFlags */ + if (tp_proxy_has_interface_by_id (priv->connection, + TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING)) { + tp_cli_connection_interface_aliasing_call_get_alias_flags ( + priv->connection, + -1, + tp_contact_list_get_alias_flags_cb, + NULL, NULL, + G_OBJECT (list)); + } + + /* lookup RequestableChannelClasses */ + if (tp_proxy_has_interface_by_id (priv->connection, + TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS)) { + tp_cli_dbus_properties_call_get (priv->connection, + -1, + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + "RequestableChannelClasses", + tp_contact_list_get_requestablechannelclasses_cb, + NULL, NULL, + G_OBJECT (list)); + } else { + /* we just don't know... better mark the flag just in case */ + priv->flags |= EMPATHY_CONTACT_LIST_CAN_GROUP; + } + + tp_connection_call_when_ready (priv->connection, conn_ready_cb, + g_object_ref (list)); tp_cli_connection_call_list_channels (priv->connection, -1, tp_contact_list_list_channels_cb, @@ -729,14 +900,6 @@ tp_contact_list_constructed (GObject *list) 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 */ - //protocol_name = tp_connection_get_protocol (priv->connection); - if (!tp_strdiff (protocol_name, "local-xmpp")) { - priv->protocol_group = _("People nearby"); - } } static void @@ -797,6 +960,12 @@ empathy_tp_contact_list_class_init (EmpathyTpContactListClass *klass) g_type_class_add_private (object_class, sizeof (EmpathyTpContactListPriv)); } +static void +tp_contact_list_array_free (gpointer handles) +{ + g_array_free (handles, TRUE); +} + static void empathy_tp_contact_list_init (EmpathyTpContactList *list) { @@ -805,7 +974,8 @@ empathy_tp_contact_list_init (EmpathyTpContactList *list) list->priv = priv; - /* Map group's name to group's channel */ + /* 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); @@ -819,6 +989,11 @@ empathy_tp_contact_list_init (EmpathyTpContactList *list) priv->pendings = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); + + /* Map group's name to GArray of handle */ + priv->add_to_group = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + tp_contact_list_array_free); } EmpathyTpContactList * @@ -855,6 +1030,14 @@ tp_contact_list_add (EmpathyContactList *list, tp_cli_channel_interface_group_call_add_members (priv->subscribe, -1, &handles, message, NULL, NULL, NULL, NULL); } + if (priv->publish) { + TpChannelGroupFlags flags = tp_channel_group_get_flags (priv->subscribe); + if (flags & TP_CHANNEL_GROUP_FLAG_CAN_ADD || + 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 @@ -867,6 +1050,14 @@ tp_contact_list_remove (EmpathyContactList *list, GArray handles = {(gchar *) &handle, 1}; handle = empathy_contact_get_handle (contact); + + /* FIXME: this is racy if tp_contact_list_remove is called before the + * 'stored' list has been retrieved. */ + if (priv->stored != NULL) { + tp_cli_channel_interface_group_call_remove_members (priv->stored, + -1, &handles, message, NULL, NULL, NULL, NULL); + } + if (priv->subscribe) { tp_cli_channel_interface_group_call_remove_members (priv->subscribe, -1, &handles, message, NULL, NULL, NULL, NULL); @@ -910,10 +1101,6 @@ tp_contact_list_get_all_groups (EmpathyContactList *list) l->data = g_strdup (l->data); } - if (priv->protocol_group) { - ret = g_list_prepend (ret, g_strdup (priv->protocol_group)); - } - return ret; } @@ -939,10 +1126,6 @@ tp_contact_list_get_groups (EmpathyContactList *list, } } - if (priv->protocol_group) { - ret = g_list_prepend (ret, g_strdup (priv->protocol_group)); - } - return ret; } @@ -1037,6 +1220,34 @@ tp_contact_list_remove_group (EmpathyContactList *list, g_array_free (handles, TRUE); } +static EmpathyContactListFlags +tp_contact_list_get_flags (EmpathyContactList *list) +{ + EmpathyTpContactListPriv *priv; + EmpathyContactListFlags flags; + + g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_LIST (list), FALSE); + + priv = GET_PRIV (list); + flags = priv->flags; + + if (priv->subscribe != NULL) { + TpChannelGroupFlags group_flags; + + group_flags = tp_channel_group_get_flags (priv->subscribe); + + if (group_flags & TP_CHANNEL_GROUP_FLAG_CAN_ADD) { + flags |= EMPATHY_CONTACT_LIST_CAN_ADD; + } + + if (group_flags & TP_CHANNEL_GROUP_FLAG_CAN_REMOVE) { + flags |= EMPATHY_CONTACT_LIST_CAN_REMOVE; + } + } + + return flags; +} + static void tp_contact_list_iface_init (EmpathyContactListIface *iface) { @@ -1050,23 +1261,7 @@ tp_contact_list_iface_init (EmpathyContactListIface *iface) 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; - 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; + iface->get_flags = tp_contact_list_get_flags; } void