Merge branch 'people-nearby-fake-group-613558'
[empathy.git] / libempathy / empathy-tp-contact-list.c
index 49168cd..95ec3be 100644 (file)
@@ -53,6 +53,8 @@ typedef struct {
        GHashTable     *add_to_group; /* group name -> GArray of handles */
 
        EmpathyContactListFlags flags;
+
+       TpProxySignalConnection *new_channels_sig;
 } EmpathyTpContactListPriv;
 
 typedef enum {
@@ -696,45 +698,113 @@ 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
-list_ensure_channel_cb (TpConnection *conn,
-                       gboolean yours,
-                       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;
-       TpChannel *channel;
-
-       if (error != NULL) {
-               DEBUG ("failed: %s\n", error->message);
-               return;
-       }
 
        /* We requested that channel by providing TargetID property, so it's
         * guaranteed that tp_channel_get_identifier will return it. */
-       channel = tp_channel_new_from_properties (conn, path, properties, NULL);
        id = tp_channel_get_identifier (channel);
 
        /* TpChannel emits initial set of members just before being ready */
        if (!tp_strdiff (id, "stored")) {
-               priv->stored = channel;
+               if (priv->stored != NULL)
+                       return;
+               priv->stored = g_object_ref (channel);
        } else if (!tp_strdiff (id, "publish")) {
-               priv->publish = channel;
+               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")) {
-               priv->subscribe = channel;
+               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);
-       } else {
-               g_warn_if_reached ();
+       }
+
+       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);
        }
 }
@@ -758,6 +828,13 @@ conn_ready_cb (TpConnection *connection,
                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,