X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-contact-list.c;h=95ec3bea2109631ee34d9ef2d22b599996792b68;hp=9ef71b4f9b9e4a165b20bfb7441b1d9760109e59;hb=940d0e9778828657a6ffbcadd35a8a84d706ac70;hpb=7b4335bd88919c0d377ee0f67f1158d4f6b7dc2d diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c index 9ef71b4f..95ec3bea 100644 --- a/libempathy/empathy-tp-contact-list.c +++ b/libempathy/empathy-tp-contact-list.c @@ -43,7 +43,6 @@ typedef struct { EmpathyTpContactFactory *factory; TpConnection *connection; - const gchar *protocol_group; TpChannel *publish; TpChannel *subscribe; @@ -54,6 +53,8 @@ typedef struct { GHashTable *add_to_group; /* group name -> GArray of handles */ EmpathyContactListFlags flags; + + TpProxySignalConnection *new_channels_sig; } EmpathyTpContactListPriv; typedef enum { @@ -123,6 +124,75 @@ tp_contact_list_group_invalidated_cb (TpChannel *channel, g_hash_table_remove (priv->groups, group_name); } +static void +contacts_added_to_group (EmpathyTpContactList *list, + TpChannel *channel, + GArray *added) +{ + EmpathyTpContactListPriv *priv = GET_PRIV (list); + const gchar *group_name; + guint i; + + group_name = tp_channel_get_identifier (channel); + + 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); + } +} + +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; + 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); + + g_signal_emit_by_name (list, "groups-changed", contact, + group_name, + FALSE); + } +} + static void tp_contact_list_group_ready_cb (TpChannel *channel, const GError *error, @@ -131,6 +201,8 @@ tp_contact_list_group_ready_cb (TpChannel *channel, EmpathyTpContactListPriv *priv = GET_PRIV (list); TpChannel *old_group; const gchar *group_name; + const TpIntSet *members; + GArray *arr; if (error) { DEBUG ("Error: %s", error->message); @@ -159,6 +231,10 @@ tp_contact_list_group_ready_cb (TpChannel *channel, 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); @@ -174,61 +250,13 @@ tp_contact_list_group_ready_cb (TpChannel *channel, g_hash_table_remove (priv->add_to_group, 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) -{ - EmpathyTpContactListPriv *priv = GET_PRIV (list); - const gchar *group_name; - guint i; - - group_name = tp_channel_get_identifier (channel); - - 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); - g_signal_emit_by_name (list, "groups-changed", contact, - group_name, - FALSE); - } + /* 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 @@ -251,11 +279,6 @@ tp_contact_list_group_add_channel (EmpathyTpContactList *list, 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, @@ -486,32 +509,6 @@ tp_contact_list_publish_group_members_changed_cb (TpChannel *channel, } } -static void -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); - - if (error) { - DEBUG ("Error: %s", error->message); - 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); -} - static void tp_contact_list_get_alias_flags_cb (TpConnection *connection, guint flags, @@ -570,31 +567,6 @@ tp_contact_list_get_requestablechannelclasses_cb (TpProxy *connection, } } -static void -tp_contact_list_publish_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_publish_request_channel_cb, - GUINT_TO_POINTER (handle), NULL, - list); -} - static void tp_contact_list_subscribe_group_members_changed_cb (TpChannel *channel, gchar *message, @@ -633,57 +605,6 @@ tp_contact_list_subscribe_group_members_changed_cb (TpChannel *channel, } } -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, @@ -777,23 +698,115 @@ tp_contact_list_finalize (GObject *object) 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 -store_create_channel_cb (TpConnection *conn, - const gchar *path, - GHashTable *properties, - const GError *error, - gpointer user_data, - GObject *weak_object) +got_list_channel (EmpathyTpContactList *list, + TpChannel *channel) { - EmpathyTpContactList *list = user_data; 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; } - priv->stored = tp_channel_new_from_properties (conn, path, properties, NULL); + 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 @@ -804,34 +817,40 @@ conn_ready_cb (TpConnection *connection, EmpathyTpContactList *list = data; EmpathyTpContactListPriv *priv = GET_PRIV (list); GHashTable *request; - GValue *value; if (error != NULL) { DEBUG ("failed: %s", error->message); goto out; } - /* Try to request the 'stored' list. */ - request = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify) tp_g_value_slice_free); - - /* org.freedesktop.Telepathy.Channel.ChannelType */ - value = tp_g_value_slice_new_string (TP_IFACE_CHANNEL_TYPE_CONTACT_LIST); - g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value); - - /* org.freedesktop.Telepathy.Channel.TargetHandleType */ - value = tp_g_value_slice_new_uint (TP_HANDLE_TYPE_LIST); - g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value); - - /* org.freedesktop.Telepathy.Channel.TargetID */ - value = tp_g_value_slice_new_string ("stored"); - g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetID", value); - - tp_cli_connection_interface_requests_call_create_channel (priv->connection, - -1, request, store_create_channel_cb, list, NULL, G_OBJECT (list)); + 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); } @@ -840,8 +859,6 @@ 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); @@ -871,25 +888,8 @@ tp_contact_list_constructed (GObject *list) priv->flags |= EMPATHY_CONTACT_LIST_CAN_GROUP; } - 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)); - - g_object_ref (list); - tp_connection_call_when_ready (priv->connection, conn_ready_cb, list); + 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, @@ -900,15 +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 */ - 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); } static void @@ -1060,11 +1051,11 @@ tp_contact_list_remove (EmpathyContactList *list, 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); - /* Contact will be removed from 'publish' and 'subscribe' too */ - return; } if (priv->subscribe) { @@ -1110,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; } @@ -1139,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; }