]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-tp-chat.c
add myself to AUTHORS
[empathy.git] / libempathy / empathy-tp-chat.c
index bfcdd1a4c890a15828502ea7fbad4a3abe885981..e53caefec5ff5cf500ba0ac3d7290a277f2ca7eb 100644 (file)
@@ -15,7 +15,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 <xclaesse@gmail.com>
  */
 
 #include <telepathy-glib/channel.h>
 #include <telepathy-glib/dbus.h>
 #include <telepathy-glib/util.h>
+#include <telepathy-glib/interfaces.h>
 
 #include "empathy-tp-chat.h"
-#include "empathy-contact-factory.h"
+#include "empathy-tp-contact-factory.h"
 #include "empathy-contact-monitor.h"
 #include "empathy-contact-list.h"
 #include "empathy-marshal.h"
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpChat)
 typedef struct {
        gboolean               dispose_has_run;
-       EmpathyContactFactory *factory;
+       EmpathyTpContactFactory *factory;
        EmpathyContactMonitor *contact_monitor;
        EmpathyContact        *user;
        EmpathyContact        *remote_contact;
-       EmpathyTpGroup        *group;
-       McAccount             *account;
+       GList                 *members;
        TpChannel             *channel;
-       gchar                 *id;
        gboolean               listing_pending_messages;
        /* Queue of messages not signalled yet */
        GQueue                *messages_queue;
@@ -56,23 +55,20 @@ typedef struct {
        GQueue                *pending_messages_queue;
        gboolean               had_properties_list;
        GPtrArray             *properties;
+       TpChannelPasswordFlags password_flags;
+       /* TRUE if we fetched the password flag of the channel or if it's not needed
+        * (channel doesn't implement the Password interface) */
+       gboolean               got_password_flags;
        gboolean               ready;
-       guint                  members_count;
 } EmpathyTpChatPriv;
 
-typedef struct {
-       gchar          *name;
-       guint           id;
-       TpPropertyFlags flags;
-       GValue         *value;
-} TpChatProperty;
-
 static void tp_chat_iface_init         (EmpathyContactListIface *iface);
 
 enum {
        PROP_0,
        PROP_CHANNEL,
        PROP_REMOTE_CONTACT,
+       PROP_PASSWORD_NEEDED,
        PROP_READY,
 };
 
@@ -91,6 +87,8 @@ G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
                                                tp_chat_iface_init));
 
+static void acknowledge_messages (EmpathyTpChat *chat, GArray *ids);
+
 static void
 tp_chat_invalidated_cb (TpProxy       *proxy,
                        guint          domain,
@@ -98,14 +96,8 @@ tp_chat_invalidated_cb (TpProxy       *proxy,
                        gchar         *message,
                        EmpathyTpChat *chat)
 {
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-
-       g_object_unref (priv->channel);
-       priv->channel = NULL;
-
        DEBUG ("Channel invalidated: %s", message);
        g_signal_emit (chat, signals[DESTROY], 0);
-
 }
 
 static void
@@ -115,125 +107,27 @@ tp_chat_async_cb (TpChannel *proxy,
                  GObject *weak_object)
 {
        if (error) {
-               DEBUG ("Error %s: %s", (gchar*) user_data, error->message);
-       }
-}
-
-static void
-tp_chat_member_added_cb (EmpathyTpGroup *group,
-                        EmpathyContact *contact,
-                        EmpathyContact *actor,
-                        guint           reason,
-                        const gchar    *message,
-                        EmpathyTpChat  *chat)
-{
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       guint              handle_type = 0;
-
-       if (priv->channel == NULL)
-               return;
-
-       priv->members_count++;
-       g_signal_emit_by_name (chat, "members-changed",
-                              contact, actor, reason, message,
-                              TRUE);
-
-       g_object_get (priv->channel, "handle-type", &handle_type, NULL);
-       if (handle_type == TP_HANDLE_TYPE_ROOM) {
-               return;
-       }
-
-       if (priv->members_count > 2 && priv->remote_contact) {
-               /* We now have more than 2 members, this is not a p2p chat
-                * anymore. Remove the remote-contact as it makes no sense, the
-                * EmpathyContactList interface must be used now. */
-               g_object_unref (priv->remote_contact);
-               priv->remote_contact = NULL;
-               g_object_notify (G_OBJECT (chat), "remote-contact");
-       }
-       if (priv->members_count <= 2 && !priv->remote_contact &&
-           !empathy_contact_is_user (contact)) {
-               /* This is a p2p chat, if it's not ourself that means this is
-                * the remote contact with who we are chatting. This is to
-                * avoid forcing the usage of the EmpathyContactList interface
-                * for p2p chats. */
-               priv->remote_contact = g_object_ref (contact);
-               g_object_notify (G_OBJECT (chat), "remote-contact");
-       }
-}
-
-static void
-tp_chat_member_removed_cb (EmpathyTpGroup *group,
-                          EmpathyContact *contact,
-                          EmpathyContact *actor,
-                          guint           reason,
-                          const gchar    *message,
-                          EmpathyTpChat  *chat)
-{
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       guint              handle_type = 0;
-
-       if (priv->channel == NULL)
-               return;
-
-       priv->members_count--;
-       g_signal_emit_by_name (chat, "members-changed",
-                              contact, actor, reason, message,
-                              FALSE);
-
-       g_object_get (priv->channel, "handle-type", &handle_type, NULL);
-       if (handle_type == TP_HANDLE_TYPE_ROOM) {
-               return;
-       }
-
-       if (priv->members_count <= 2 && !priv->remote_contact) {
-               GList *members, *l;
-
-               /* We are not a MUC anymore, get the remote contact back */
-               members = empathy_tp_group_get_members (group);
-               for (l = members; l; l = l->next) {
-                       if (!empathy_contact_is_user (l->data)) {
-                               priv->remote_contact = g_object_ref (l->data);
-                               g_object_notify (G_OBJECT (chat), "remote-contact");
-                               break;
-                       }
-               }
-               g_list_foreach (members, (GFunc) g_object_unref, NULL);
-               g_list_free (members);
+               DEBUG ("Error %s: %s", (gchar *) user_data, error->message);
        }
 }
 
-static void
-tp_chat_local_pending_cb  (EmpathyTpGroup *group,
-                          EmpathyContact *contact,
-                          EmpathyContact *actor,
-                          guint           reason,
-                          const gchar    *message,
-                          EmpathyTpChat  *chat)
-{
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-
-       if (priv->channel == NULL)
-               return;
-
-       g_signal_emit_by_name (chat, "pendings-changed",
-                              contact, actor, reason, message,
-                              TRUE);
-}
-
 static void
 tp_chat_add (EmpathyContactList *list,
             EmpathyContact     *contact,
             const gchar        *message)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (list);
+       TpHandle           handle;
+       GArray             handles = {(gchar *) &handle, 1};
 
        g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
        g_return_if_fail (EMPATHY_IS_CONTACT (contact));
 
-       if (priv->group) {
-               empathy_tp_group_add_member (priv->group, contact, message);
-       }
+       handle = empathy_contact_get_handle (contact);
+       tp_cli_channel_interface_group_call_add_members (priv->channel, -1,
+                                                        &handles, NULL,
+                                                        NULL, NULL, NULL,
+                                                        NULL);
 }
 
 static void
@@ -242,13 +136,17 @@ tp_chat_remove (EmpathyContactList *list,
                const gchar        *message)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (list);
+       TpHandle           handle;
+       GArray             handles = {(gchar *) &handle, 1};
 
        g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
        g_return_if_fail (EMPATHY_IS_CONTACT (contact));
 
-       if (priv->group) {
-               empathy_tp_group_remove_member (priv->group, contact, message);
-       }
+       handle = empathy_contact_get_handle (contact);
+       tp_cli_channel_interface_group_call_remove_members (priv->channel, -1,
+                                                           &handles, NULL,
+                                                           NULL, NULL, NULL,
+                                                           NULL);
 }
 
 static GList *
@@ -259,11 +157,13 @@ tp_chat_get_members (EmpathyContactList *list)
 
        g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
 
-       if (priv->group) {
-               members = empathy_tp_group_get_members (priv->group);
+       if (priv->members) {
+               members = g_list_copy (priv->members);
+               g_list_foreach (members, (GFunc) g_object_ref, NULL);
        } else {
                members = g_list_prepend (members, g_object_ref (priv->user));
-               members = g_list_prepend (members, g_object_ref (priv->remote_contact));
+               if (priv->remote_contact != NULL)
+                       members = g_list_prepend (members, g_object_ref (priv->remote_contact));
        }
 
        return members;
@@ -285,119 +185,79 @@ tp_chat_get_monitor (EmpathyContactList *list)
        return priv->contact_monitor;
 }
 
-static EmpathyMessage *
-tp_chat_build_message (EmpathyTpChat *chat,
-                      guint          id,
-                      guint          type,
-                      guint          timestamp,
-                      guint          from_handle,
-                      const gchar   *message_body)
+static void
+tp_chat_emit_queued_messages (EmpathyTpChat *chat)
 {
-       EmpathyTpChatPriv *priv;
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
        EmpathyMessage    *message;
-       EmpathyContact    *sender;
 
-       priv = GET_PRIV (chat);
-
-       if (from_handle == 0) {
-               sender = g_object_ref (priv->user);
-       } else {
-               sender = empathy_contact_factory_get_from_handle (priv->factory,
-                                                                 priv->account,
-                                                                 from_handle);
-       }
-
-       message = empathy_message_new (message_body);
-       empathy_message_set_tptype (message, type);
-       empathy_message_set_sender (message, sender);
-       empathy_message_set_receiver (message, priv->user);
-       empathy_message_set_timestamp (message, timestamp);
-       empathy_message_set_id (message, id);
-
-       g_object_unref (sender);
-
-       return message;
-}
-
-static void
-tp_chat_sender_ready_notify_cb (EmpathyContact *contact,
-                               GParamSpec     *param_spec,
-                               EmpathyTpChat  *chat)
-{
-       EmpathyTpChatPriv   *priv = GET_PRIV (chat);
-       EmpathyMessage      *message;
-       EmpathyContactReady  ready;
-       EmpathyContact      *sender = NULL;
-       gboolean             removed = FALSE;
-
-       /* Emit all messages queued until we find a message with not
-        * ready sender (in case of a MUC we could have more than one sender).
-        * When leaving this loop, sender is the first not ready contact queued
-        * and removed tells if at least one message got removed
-        * from the queue. */
+       /* Check if we can now emit some queued messages */
        while ((message = g_queue_peek_head (priv->messages_queue)) != NULL) {
-               sender = empathy_message_get_sender (message);
-               ready = empathy_contact_get_ready (sender);
-
-               if ((ready & EMPATHY_CONTACT_READY_NAME) == 0 ||
-                   (ready & EMPATHY_CONTACT_READY_ID) == 0) {
+               if (empathy_message_get_sender (message) == NULL) {
                        break;
                }
 
                DEBUG ("Queued message ready");
-               message = g_queue_pop_head (priv->messages_queue);
+               g_queue_pop_head (priv->messages_queue);
                g_queue_push_tail (priv->pending_messages_queue, message);
                g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
-               removed = TRUE;
        }
+}
 
-       if (removed) {
-               /* We removed at least one message from the queue, disconnect
-                * the ready signal from the previous contact */
-               g_signal_handlers_disconnect_by_func (contact,
-                                                     tp_chat_sender_ready_notify_cb,
-                                                     chat);
-
-               if (g_queue_get_length (priv->messages_queue) > 0) {
-                       /* We still have queued message, connect the ready
-                        * signal on the new first message sender. */
-                       g_signal_connect (sender, "notify::ready",
-                                         G_CALLBACK (tp_chat_sender_ready_notify_cb),
-                                         chat);
-               }
+static void
+tp_chat_got_sender_cb (EmpathyTpContactFactory *factory,
+                      EmpathyContact          *contact,
+                      const GError            *error,
+                      gpointer                 message,
+                      GObject                 *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               /* Do not block the message queue, just drop this message */
+               g_queue_remove (priv->messages_queue, message);
+       } else {
+               empathy_message_set_sender (message, contact);
        }
+
+       tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (chat));
 }
 
 static void
-tp_chat_emit_or_queue_message (EmpathyTpChat  *chat,
-                              EmpathyMessage *message)
+tp_chat_build_message (EmpathyTpChat *chat,
+                      gboolean       incoming,
+                      guint          id,
+                      guint          type,
+                      guint          timestamp,
+                      guint          from_handle,
+                      const gchar   *message_body,
+                      TpChannelTextMessageFlags flags)
 {
-       EmpathyTpChatPriv   *priv = GET_PRIV (chat);
-       EmpathyContact      *sender;
-       EmpathyContactReady  ready;
+       EmpathyTpChatPriv *priv;
+       EmpathyMessage    *message;
 
-       if (g_queue_get_length (priv->messages_queue) > 0) {
-               DEBUG ("Message queue not empty");
-               g_queue_push_tail (priv->messages_queue, g_object_ref (message));
-               return;
-       }
+       priv = GET_PRIV (chat);
 
+       message = empathy_message_new (message_body);
+       empathy_message_set_tptype (message, type);
+       empathy_message_set_receiver (message, priv->user);
+       empathy_message_set_timestamp (message, timestamp);
+       empathy_message_set_id (message, id);
+       empathy_message_set_incoming (message, incoming);
+       empathy_message_set_flags (message, flags);
 
-       sender = empathy_message_get_sender (message);
-       ready = empathy_contact_get_ready (sender);
-       if ((ready & EMPATHY_CONTACT_READY_NAME) &&
-           (ready & EMPATHY_CONTACT_READY_ID)) {
-               DEBUG ("Message queue empty and sender ready");
-               g_queue_push_tail (priv->pending_messages_queue, g_object_ref (message));
-               g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
-               return;
-       }
+       g_queue_push_tail (priv->messages_queue, message);
 
-       DEBUG ("Sender not ready");
-       g_queue_push_tail (priv->messages_queue, g_object_ref (message));
-       g_signal_connect (sender, "notify::ready",
-                         G_CALLBACK (tp_chat_sender_ready_notify_cb),
-                         chat);
+       if (from_handle == 0) {
+               empathy_message_set_sender (message, priv->user);
+               tp_chat_emit_queued_messages (chat);
+       } else {
+               empathy_tp_contact_factory_get_from_handle (priv->factory,
+                       from_handle,
+                       tp_chat_got_sender_cb,
+                       message, NULL, G_OBJECT (chat));
+       }
 }
 
 static void
@@ -409,10 +269,10 @@ tp_chat_received_cb (TpChannel   *channel,
                     guint        message_flags,
                     const gchar *message_body,
                     gpointer     user_data,
-                    GObject     *chat)
+                    GObject     *chat_)
 {
+       EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       EmpathyMessage    *message;
 
        if (priv->channel == NULL)
                return;
@@ -420,18 +280,31 @@ tp_chat_received_cb (TpChannel   *channel,
        if (priv->listing_pending_messages) {
                return;
        }
+
        DEBUG ("Message received: %s", message_body);
 
-       message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
-                                        message_id,
-                                        message_type,
-                                        timestamp,
-                                        from_handle,
-                                        message_body);
+       if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
+           !tp_strdiff (message_body, "")) {
+               GArray *ids;
 
-       tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
-       g_object_unref (message);
+               DEBUG ("Empty message with NonTextContent, ignoring and acking.");
+
+               ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+               g_array_append_val (ids, message_id);
+               acknowledge_messages (chat, ids);
+               g_array_free (ids, TRUE);
+
+               return;
+       }
+
+       tp_chat_build_message (chat,
+                              TRUE,
+                              message_id,
+                              message_type,
+                              timestamp,
+                              from_handle,
+                              message_body,
+                              message_flags);
 }
 
 static void
@@ -440,25 +313,24 @@ tp_chat_sent_cb (TpChannel   *channel,
                 guint        message_type,
                 const gchar *message_body,
                 gpointer     user_data,
-                GObject     *chat)
+                GObject     *chat_)
 {
+       EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       EmpathyMessage *message;
 
        if (priv->channel == NULL)
                return;
 
        DEBUG ("Message sent: %s", message_body);
 
-       message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
-                                        0,
-                                        message_type,
-                                        timestamp,
-                                        0,
-                                        message_body);
-
-       tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
-       g_object_unref (message);
+       tp_chat_build_message (chat,
+                              FALSE,
+                              0,
+                              message_type,
+                              timestamp,
+                              0,
+                              message_body,
+                              0);
 }
 
 static void
@@ -470,23 +342,14 @@ tp_chat_send_error_cb (TpChannel   *channel,
                       gpointer     user_data,
                       GObject     *chat)
 {
-       EmpathyMessage *message;
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
        if (priv->channel == NULL)
                return;
 
-       DEBUG ("Message sent error: %s (%d)", message_body, error_code);
+       DEBUG ("Error sending '%s' (%d)", message_body, error_code);
 
-       message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
-                                        0,
-                                        message_type,
-                                        timestamp,
-                                        0,
-                                        message_body);
-
-       g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code);
-       g_object_unref (message);
+       g_signal_emit (chat, signals[SEND_ERROR], 0, message_body, error_code);
 }
 
 static void
@@ -499,33 +362,51 @@ tp_chat_send_cb (TpChannel    *proxy,
 
        if (error) {
                DEBUG ("Error: %s", error->message);
-               g_signal_emit (chat, signals[SEND_ERROR], 0, message,
+               g_signal_emit (chat, signals[SEND_ERROR], 0,
+                              empathy_message_get_body (message),
                               TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN);
        }
 }
 
+typedef struct {
+       EmpathyTpChat *chat;
+       TpChannelChatState state;
+} StateChangedData;
+
 static void
-tp_chat_state_changed_cb (TpChannel *channel,
-                         guint      handle,
-                         guint      state,
-                         gpointer   user_data,
-                         GObject   *chat)
+tp_chat_state_changed_got_contact_cb (EmpathyTpContactFactory *factory,
+                                     EmpathyContact          *contact,
+                                     const GError            *error,
+                                     gpointer                 user_data,
+                                     GObject                 *chat)
 {
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       EmpathyContact    *contact;
+       TpChannelChatState state;
 
-       if (priv->channel == NULL)
+       if (error) {
+               DEBUG ("Error: %s", error->message);
                return;
+       }
 
-       contact = empathy_contact_factory_get_from_handle (priv->factory,
-                                                          priv->account,
-                                                          handle);
-
+       state = GPOINTER_TO_UINT (user_data);
        DEBUG ("Chat state changed for %s (%d): %d",
-               empathy_contact_get_name (contact), handle, state);
+               empathy_contact_get_name (contact),
+               empathy_contact_get_handle (contact), state);
 
        g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
-       g_object_unref (contact);
+}
+
+static void
+tp_chat_state_changed_cb (TpChannel *channel,
+                         TpHandle   handle,
+                         TpChannelChatState state,
+                         gpointer   user_data,
+                         GObject   *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+       empathy_tp_contact_factory_get_from_handle (priv->factory, handle,
+               tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state),
+               NULL, chat);
 }
 
 static void
@@ -533,10 +414,12 @@ tp_chat_list_pending_messages_cb (TpChannel       *channel,
                                  const GPtrArray *messages_list,
                                  const GError    *error,
                                  gpointer         user_data,
-                                 GObject         *chat)
+                                 GObject         *chat_)
 {
+       EmpathyTpChat     *chat = EMPATHY_TP_CHAT (chat_);
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
        guint              i;
+       GArray            *empty_non_text_content_ids = NULL;
 
        priv->listing_pending_messages = FALSE;
 
@@ -549,7 +432,6 @@ tp_chat_list_pending_messages_cb (TpChannel       *channel,
        }
 
        for (i = 0; i < messages_list->len; i++) {
-               EmpathyMessage *message;
                GValueArray    *message_struct;
                const gchar    *message_body;
                guint           message_id;
@@ -569,15 +451,31 @@ tp_chat_list_pending_messages_cb (TpChannel       *channel,
 
                DEBUG ("Message pending: %s", message_body);
 
-               message = tp_chat_build_message (EMPATHY_TP_CHAT (chat),
-                                                message_id,
-                                                message_type,
-                                                timestamp,
-                                                from_handle,
-                                                message_body);
+               if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
+                   !tp_strdiff (message_body, "")) {
+                       DEBUG ("Empty message with NonTextContent, ignoring and acking.");
 
-               tp_chat_emit_or_queue_message (EMPATHY_TP_CHAT (chat), message);
-               g_object_unref (message);
+                       if (empty_non_text_content_ids == NULL) {
+                               empty_non_text_content_ids = g_array_new (FALSE, FALSE, sizeof (guint));
+                       }
+
+                       g_array_append_val (empty_non_text_content_ids, message_id);
+                       continue;
+               }
+
+               tp_chat_build_message (chat,
+                                      TRUE,
+                                      message_id,
+                                      message_type,
+                                      timestamp,
+                                      from_handle,
+                                      message_body,
+                                      message_flags);
+       }
+
+       if (empty_non_text_content_ids != NULL) {
+               acknowledge_messages (chat, empty_non_text_content_ids);
+               g_array_free (empty_non_text_content_ids, TRUE);
        }
 }
 
@@ -598,10 +496,10 @@ tp_chat_property_flags_changed_cb (TpProxy         *proxy,
        }
 
        for (i = 0; i < properties->len; i++) {
-               GValueArray    *prop_struct;
-               TpChatProperty *property;
-               guint           id;
-               guint           flags;
+               GValueArray           *prop_struct;
+               EmpathyTpChatProperty *property;
+               guint                  id;
+               guint                  flags;
 
                prop_struct = g_ptr_array_index (properties, i);
                id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
@@ -636,10 +534,10 @@ tp_chat_properties_changed_cb (TpProxy         *proxy,
        }
 
        for (i = 0; i < properties->len; i++) {
-               GValueArray    *prop_struct;
-               TpChatProperty *property;
-               guint           id;
-               GValue         *src_value;
+               GValueArray           *prop_struct;
+               EmpathyTpChatProperty *property;
+               guint                  id;
+               GValue                *src_value;
 
                prop_struct = g_ptr_array_index (properties, i);
                id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
@@ -702,11 +600,11 @@ tp_chat_list_properties_cb (TpProxy         *proxy,
        ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), properties->len);
        priv->properties = g_ptr_array_sized_new (properties->len);
        for (i = 0; i < properties->len; i++) {
-               GValueArray    *prop_struct;
-               TpChatProperty *property;
+               GValueArray           *prop_struct;
+               EmpathyTpChatProperty *property;
 
                prop_struct = g_ptr_array_index (properties, i);
-               property = g_slice_new0 (TpChatProperty);
+               property = g_slice_new0 (EmpathyTpChatProperty);
                property->id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
                property->name = g_value_dup_string (g_value_array_get_nth (prop_struct, 1));
                property->flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 3));
@@ -733,11 +631,13 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat,
                              const gchar   *name,
                              const GValue  *value)
 {
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       TpChatProperty    *property;
-       guint              i;
+       EmpathyTpChatPriv     *priv = GET_PRIV (chat);
+       EmpathyTpChatProperty *property;
+       guint                  i;
 
-       g_return_if_fail (priv->ready);
+       if (!priv->had_properties_list) {
+               return;
+       }
 
        for (i = 0; i < priv->properties->len; i++) {
                property = g_ptr_array_index (priv->properties, i);
@@ -779,79 +679,136 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat,
        }
 }
 
-static void
-tp_chat_channel_ready_cb (EmpathyTpChat *chat)
+EmpathyTpChatProperty *
+empathy_tp_chat_get_property (EmpathyTpChat *chat,
+                             const gchar   *name)
+{
+       EmpathyTpChatPriv     *priv = GET_PRIV (chat);
+       EmpathyTpChatProperty *property;
+       guint                  i;
+
+       if (!priv->had_properties_list) {
+               return NULL;
+       }
+
+       for (i = 0; i < priv->properties->len; i++) {
+               property = g_ptr_array_index (priv->properties, i);
+               if (!tp_strdiff (property->name, name)) {
+                       return property;
+               }
+       }
+
+       return NULL;
+}
+
+GPtrArray *
+empathy_tp_chat_get_properties (EmpathyTpChat *chat)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
-       TpConnection      *connection;
-       guint              handle, handle_type;
 
-       if (priv->channel == NULL)
-               return;
+       return priv->properties;
+}
 
-       DEBUG ("Channel ready");
+static void
+tp_chat_dispose (GObject *object)
+{
+       EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
 
-       g_object_get (priv->channel,
-                     "connection", &connection,
-                     "handle", &handle,
-                     "handle_type", &handle_type,
-                     NULL);
+       if (priv->dispose_has_run)
+               return;
 
-       if (handle_type != TP_HANDLE_TYPE_NONE && handle != 0) {
-               GArray *handles;
-               gchar **names;
-
-               handles = g_array_new (FALSE, FALSE, sizeof (guint));
-               g_array_append_val (handles, handle);
-               tp_cli_connection_run_inspect_handles (connection, -1,
-                                                      handle_type, handles,
-                                                      &names, NULL, NULL);
-               priv->id = *names;
-               g_array_free (handles, TRUE);
-               g_free (names);
-       }
+       priv->dispose_has_run = TRUE;
 
-       if (handle_type == TP_HANDLE_TYPE_CONTACT && handle != 0) {
-               priv->remote_contact = empathy_contact_factory_get_from_handle (priv->factory,
-                                                                               priv->account,
-                                                                               handle);
-               g_object_notify (G_OBJECT (chat), "remote-contact");
+       if (priv->channel != NULL) {
+               g_signal_handlers_disconnect_by_func (priv->channel,
+                       tp_chat_invalidated_cb, self);
+               g_object_unref (priv->channel);
        }
+       priv->channel = NULL;
 
-       if (tp_proxy_has_interface_by_id (priv->channel,
-                                         TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
-               priv->group = empathy_tp_group_new (priv->channel);
-
-               g_signal_connect (priv->group, "member-added",
-                                 G_CALLBACK (tp_chat_member_added_cb),
-                                 chat);
-               g_signal_connect (priv->group, "member-removed",
-                                 G_CALLBACK (tp_chat_member_removed_cb),
-                                 chat);
-               g_signal_connect (priv->group, "local-pending",
-                                 G_CALLBACK (tp_chat_local_pending_cb),
-                                 chat);
-               empathy_run_until_ready (priv->group);
-       } else {
-               priv->members_count = 2;
-       }
-       
-       if (tp_proxy_has_interface_by_id (priv->channel,
-                                         TP_IFACE_QUARK_PROPERTIES_INTERFACE)) {
-               tp_cli_properties_interface_call_list_properties (priv->channel, -1,
-                                                                 tp_chat_list_properties_cb,
-                                                                 NULL, NULL,
-                                                                 G_OBJECT (chat));
-               tp_cli_properties_interface_connect_to_properties_changed (priv->channel,
-                                                                          tp_chat_properties_changed_cb,
-                                                                          NULL, NULL,
-                                                                          G_OBJECT (chat), NULL);
-               tp_cli_properties_interface_connect_to_property_flags_changed (priv->channel,
-                                                                              tp_chat_property_flags_changed_cb,
-                                                                              NULL, NULL,
-                                                                              G_OBJECT (chat), NULL);
+       if (priv->remote_contact != NULL)
+               g_object_unref (priv->remote_contact);
+       priv->remote_contact = NULL;
+
+       if (priv->factory != NULL)
+               g_object_unref (priv->factory);
+       priv->factory = NULL;
+
+       if (priv->user != NULL)
+               g_object_unref (priv->user);
+       priv->user = NULL;
+
+       if (priv->contact_monitor)
+               g_object_unref (priv->contact_monitor);
+       priv->contact_monitor = NULL;
+
+       g_queue_foreach (priv->messages_queue, (GFunc) g_object_unref, NULL);
+       g_queue_clear (priv->messages_queue);
+
+       g_queue_foreach (priv->pending_messages_queue,
+               (GFunc) g_object_unref, NULL);
+       g_queue_clear (priv->pending_messages_queue);
+
+       if (G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose)
+               G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose (object);
+}
+
+static void
+tp_chat_finalize (GObject *object)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (object);
+       guint              i;
+
+       DEBUG ("Finalize: %p", object);
+
+       if (priv->properties) {
+               for (i = 0; i < priv->properties->len; i++) {
+                       EmpathyTpChatProperty *property;
+
+                       property = g_ptr_array_index (priv->properties, i);
+                       g_free (property->name);
+                       if (property->value) {
+                               tp_g_value_slice_free (property->value);
+                       }
+                       g_slice_free (EmpathyTpChatProperty, property);
+               }
+               g_ptr_array_free (priv->properties, TRUE);
        }
 
+       g_queue_free (priv->messages_queue);
+       g_queue_free (priv->pending_messages_queue);
+
+       G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
+}
+
+static void
+tp_chat_check_if_ready (EmpathyTpChat *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+       if (priv->ready)
+               return;
+
+       if (priv->user == NULL)
+               return;
+
+       if (!priv->got_password_flags)
+               return;
+
+       /* We need either the members (room) or the remote contact (private chat).
+        * If the chat is protected by a password we can't get these information so
+        * consider the chat as ready so it can be presented to the user. */
+       if (!empathy_tp_chat_password_needed (chat) && priv->members == NULL &&
+           priv->remote_contact == NULL)
+               return;
+
+       DEBUG ("Ready!");
+
+       tp_cli_channel_type_text_connect_to_received (priv->channel,
+                                                     tp_chat_received_cb,
+                                                     NULL, NULL,
+                                                     G_OBJECT (chat), NULL);
        priv->listing_pending_messages = TRUE;
        tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
                                                             FALSE,
@@ -859,10 +816,6 @@ tp_chat_channel_ready_cb (EmpathyTpChat *chat)
                                                             NULL, NULL,
                                                             G_OBJECT (chat));
 
-       tp_cli_channel_type_text_connect_to_received (priv->channel,
-                                                     tp_chat_received_cb,
-                                                     NULL, NULL,
-                                                     G_OBJECT (chat), NULL);
        tp_cli_channel_type_text_connect_to_sent (priv->channel,
                                                  tp_chat_sent_cb,
                                                  NULL, NULL,
@@ -875,116 +828,357 @@ tp_chat_channel_ready_cb (EmpathyTpChat *chat)
                                                                           tp_chat_state_changed_cb,
                                                                           NULL, NULL,
                                                                           G_OBJECT (chat), NULL);
-       tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
-                                                                          tp_chat_state_changed_cb,
-                                                                          NULL, NULL,
-                                                                          G_OBJECT (chat), NULL);
-
        priv->ready = TRUE;
        g_object_notify (G_OBJECT (chat), "ready");
 }
 
 static void
-tp_chat_dispose (GObject *object)
+tp_chat_update_remote_contact (EmpathyTpChat *chat)
 {
-       EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
-       EmpathyTpChatPriv *priv = GET_PRIV (self);
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+       EmpathyContact *contact = NULL;
+       TpHandle self_handle;
+       TpHandleType handle_type;
+       GList *l;
 
-       if (priv->dispose_has_run)
+       /* If this is a named chatroom, never pretend it is a private chat */
+       tp_channel_get_handle (priv->channel, &handle_type);
+       if (handle_type == TP_HANDLE_TYPE_ROOM) {
                return;
+       }
 
-       priv->dispose_has_run = TRUE;
+       /* This is an MSN-like chat where anyone can join the chat at anytime.
+        * If there is only one non-self contact member, we are in a private
+        * chat and we set the "remote-contact" property to that contact. If
+        * there are more, set the "remote-contact" property to NULL and the
+        * UI will display a contact list. */
+       self_handle = tp_channel_group_get_self_handle (priv->channel);
+       for (l = priv->members; l; l = l->next) {
+               /* Skip self contact if member */
+               if (empathy_contact_get_handle (l->data) == self_handle) {
+                       continue;
+               }
 
-       if (priv->channel != NULL)
-               {
-                       g_signal_handlers_disconnect_by_func (priv->channel,
-                               tp_chat_invalidated_cb, self);
-                       g_object_unref (priv->channel);
-                       priv->channel = NULL;
+               /* We have more than one remote contact, break */
+               if (contact != NULL) {
+                       contact = NULL;
+                       break;
                }
 
-       if (priv->remote_contact != NULL)
+               /* If we didn't find yet a remote contact, keep this one */
+               contact = l->data;
+       }
+
+       if (priv->remote_contact == contact) {
+               return;
+       }
+
+       DEBUG ("Changing remote contact from %p to %p",
+               priv->remote_contact, contact);
+
+       if (priv->remote_contact) {
                g_object_unref (priv->remote_contact);
+       }
 
-       priv->remote_contact = NULL;
+       priv->remote_contact = contact ? g_object_ref (contact) : NULL;
+       g_object_notify (G_OBJECT (chat), "remote-contact");
+}
 
-       if (priv->group != NULL)
-               g_object_unref (priv->group);
-       priv->group = NULL;
+static void
+tp_chat_got_added_contacts_cb (EmpathyTpContactFactory *factory,
+                              guint                    n_contacts,
+                              EmpathyContact * const * contacts,
+                              guint                    n_failed,
+                              const TpHandle          *failed,
+                              const GError            *error,
+                              gpointer                 user_data,
+                              GObject                 *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+       guint i;
+       const TpIntSet *members;
+       TpHandle handle;
+       EmpathyContact *contact;
 
-       if (priv->factory != NULL)
-               g_object_unref (priv->factory);
-       priv->factory = NULL;
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               return;
+       }
 
-       if (priv->user != NULL);
-               g_object_unref (priv->user);
-       priv->user = NULL;
+       members = tp_channel_group_get_members (priv->channel);
+       for (i = 0; i < n_contacts; i++) {
+               contact = contacts[i];
+               handle = empathy_contact_get_handle (contact);
+
+               /* Make sure the contact is still member */
+               if (tp_intset_is_member (members, handle)) {
+                       priv->members = g_list_prepend (priv->members,
+                               g_object_ref (contact));
+                       g_signal_emit_by_name (chat, "members-changed",
+                                              contact, NULL, 0, NULL, TRUE);
+               }
+       }
 
-       if (priv->account != NULL);
-               g_object_unref (priv->account);
-       priv->account = NULL;
+       tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat));
+       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+}
+
+static EmpathyContact *
+chat_lookup_contact (EmpathyTpChat *chat,
+                    TpHandle       handle,
+                    gboolean       remove_)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+       GList *l;
+
+       for (l = priv->members; l; l = l->next) {
+               EmpathyContact *c = l->data;
+
+               if (empathy_contact_get_handle (c) != handle) {
+                       continue;
+               }
 
-       if (!g_queue_is_empty (priv->messages_queue)) {
-               EmpathyMessage *message;
-               EmpathyContact *contact;
+               if (remove_) {
+                       /* Caller takes the reference. */
+                       priv->members = g_list_delete_link (priv->members, l);
+               } else {
+                       g_object_ref (c);
+               }
 
-               message = g_queue_peek_head (priv->messages_queue);
-               contact = empathy_message_get_sender (message);
-               g_signal_handlers_disconnect_by_func (contact,
-               tp_chat_sender_ready_notify_cb, object);
+               return c;
        }
 
-       g_list_foreach (priv->messages_queue->head,
-               (GFunc) g_object_unref, NULL);
+       return NULL;
+}
 
-       g_list_foreach (priv->pending_messages_queue->head,
-               (GFunc) g_object_unref, NULL);
+typedef struct
+{
+    TpHandle old_handle;
+    guint reason;
+    gchar *message;
+} ContactRenameData;
+
+static ContactRenameData *
+contact_rename_data_new (TpHandle handle,
+                        guint reason,
+                        const gchar* message)
+{
+       ContactRenameData *data = g_new (ContactRenameData, 1);
+       data->old_handle = handle;
+       data->reason = reason;
+       data->message = g_strdup (message);
 
-       if (G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose)
-               G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose (object);
+       return data;
 }
 
 static void
-tp_chat_finalize (GObject *object)
+contact_rename_data_free (ContactRenameData* data)
 {
-       EmpathyTpChatPriv *priv = GET_PRIV (object);
-       guint              i;
+       g_free (data->message);
+       g_free (data);
+}
 
-       DEBUG ("Finalize: %p", object);
+static void
+tp_chat_got_renamed_contacts_cb (EmpathyTpContactFactory *factory,
+                                 guint                    n_contacts,
+                                 EmpathyContact * const * contacts,
+                                 guint                    n_failed,
+                                 const TpHandle          *failed,
+                                 const GError            *error,
+                                 gpointer                 user_data,
+                                 GObject                 *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+       const TpIntSet *members;
+       TpHandle handle;
+       EmpathyContact *old = NULL, *new = NULL;
+       ContactRenameData *rename_data = (ContactRenameData *) user_data;
 
-       if (priv->properties) {
-               for (i = 0; i < priv->properties->len; i++) {
-                       TpChatProperty *property;
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               return;
+       }
 
-                       property = g_ptr_array_index (priv->properties, i);
-                       g_free (property->name);
-                       if (property->value) {
-                               tp_g_value_slice_free (property->value);
-                       }
-                       g_slice_free (TpChatProperty, property);
+       /* renamed members can only be delivered one at a time */
+       g_warn_if_fail (n_contacts == 1);
+
+       new = contacts[0];
+
+       members = tp_channel_group_get_members (priv->channel);
+       handle = empathy_contact_get_handle (new);
+
+       old = chat_lookup_contact (EMPATHY_TP_CHAT (chat),
+                                  rename_data->old_handle, TRUE);
+
+       /* Make sure the contact is still member */
+       if (tp_intset_is_member (members, handle)) {
+               priv->members = g_list_prepend (priv->members,
+                       g_object_ref (new));
+
+               if (old != NULL) {
+                       g_signal_emit_by_name (chat, "member-renamed",
+                                              old, new, rename_data->reason,
+                                              rename_data->message);
+                       g_object_unref (old);
                }
-               g_ptr_array_free (priv->properties, TRUE);
        }
 
-       if (priv->remote_contact) {
-               g_object_unref (priv->remote_contact);
+       tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat));
+       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+}
+
+
+static void
+tp_chat_group_members_changed_cb (TpChannel     *self,
+                                 gchar         *message,
+                                 GArray        *added,
+                                 GArray        *removed,
+                                 GArray        *local_pending,
+                                 GArray        *remote_pending,
+                                 guint          actor,
+                                 guint          reason,
+                                 EmpathyTpChat *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+       EmpathyContact *contact;
+       EmpathyContact *actor_contact = NULL;
+       guint i;
+       ContactRenameData *rename_data;
+       TpHandle old_handle;
+
+       /* Contact renamed */
+       if (reason == TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED) {
+               /* there can only be a single 'added' and a single 'removed' handle */
+               g_warn_if_fail (removed->len == 1);
+               g_warn_if_fail (added->len == 1);
+
+               old_handle = g_array_index (removed, guint, 0);
+
+               rename_data = contact_rename_data_new (old_handle, reason, message);
+               empathy_tp_contact_factory_get_from_handles (priv->factory,
+                       added->len, (TpHandle *) added->data,
+                       tp_chat_got_renamed_contacts_cb,
+                       rename_data, (GDestroyNotify) contact_rename_data_free,
+                       G_OBJECT (chat));
+               return;
        }
-       if (priv->group) {
-               g_object_unref (priv->group);
+
+       if (actor != 0) {
+               actor_contact = chat_lookup_contact (chat, actor, FALSE);
+               if (actor_contact == NULL) {
+                       /* FIXME: handle this a tad more gracefully: perhaps
+                        * the actor was a server op. We could use the
+                        * contact-ids detail of MembersChangedDetailed.
+                        */
+                       DEBUG ("actor %u not a channel member", actor);
+               }
        }
 
-       if (priv->contact_monitor) {
-               g_object_unref (priv->contact_monitor);
+       /* Remove contacts that are not members anymore */
+       for (i = 0; i < removed->len; i++) {
+               contact = chat_lookup_contact (chat,
+                       g_array_index (removed, TpHandle, i), TRUE);
+
+               if (contact != NULL) {
+                       g_signal_emit_by_name (chat, "members-changed", contact,
+                                              actor_contact, reason, message,
+                                              FALSE);
+                       g_object_unref (contact);
+               }
        }
 
-       g_object_unref (priv->factory);
-       g_object_unref (priv->user);
-       g_object_unref (priv->account);
-       g_free (priv->id);
-       g_queue_free (priv->messages_queue);
-       g_queue_free (priv->pending_messages_queue);
+       /* Request added contacts */
+       if (added->len > 0) {
+               empathy_tp_contact_factory_get_from_handles (priv->factory,
+                       added->len, (TpHandle *) added->data,
+                       tp_chat_got_added_contacts_cb, NULL, NULL,
+                       G_OBJECT (chat));
+       }
 
-       G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
+       tp_chat_update_remote_contact (chat);
+
+       if (actor_contact != NULL) {
+               g_object_unref (actor_contact);
+       }
+}
+
+static void
+tp_chat_got_remote_contact_cb (EmpathyTpContactFactory *factory,
+                              EmpathyContact          *contact,
+                              const GError            *error,
+                              gpointer                 user_data,
+                              GObject                 *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               empathy_tp_chat_close (EMPATHY_TP_CHAT (chat));
+               return;
+       }
+
+       priv->remote_contact = g_object_ref (contact);
+       g_object_notify (chat, "remote-contact");
+
+       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+}
+
+static void
+tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory,
+                            EmpathyContact          *contact,
+                            const GError            *error,
+                            gpointer                 user_data,
+                            GObject                 *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+       if (error) {
+               DEBUG ("Error: %s", error->message);
+               empathy_tp_chat_close (EMPATHY_TP_CHAT (chat));
+               return;
+       }
+
+       priv->user = g_object_ref (contact);
+       empathy_contact_set_is_user (priv->user, TRUE);
+       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+}
+
+static void
+password_flags_changed_cb (TpChannel *channel,
+    guint added,
+    guint removed,
+    gpointer user_data,
+    GObject *weak_object)
+{
+       EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
+       gboolean was_needed, needed;
+
+       was_needed = empathy_tp_chat_password_needed (self);
+
+       priv->password_flags |= added;
+       priv->password_flags ^= removed;
+
+       needed = empathy_tp_chat_password_needed (self);
+
+       if (was_needed != needed)
+               g_object_notify (G_OBJECT (self), "password-needed");
+}
+
+static void
+got_password_flags_cb (TpChannel *proxy,
+                            guint password_flags,
+                            const GError *error,
+                            gpointer user_data,
+                            GObject *weak_object)
+{
+       EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+       priv->got_password_flags = TRUE;
+       priv->password_flags = password_flags;
+
+       tp_chat_check_if_ready (EMPATHY_TP_CHAT (self));
 }
 
 static GObject *
@@ -994,26 +1188,83 @@ tp_chat_constructor (GType                  type,
 {
        GObject           *chat;
        EmpathyTpChatPriv *priv;
-       gboolean           channel_ready;
+       TpConnection      *connection;
+       TpHandle           handle;
 
        chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
 
        priv = GET_PRIV (chat);
-       priv->account = empathy_channel_get_account (priv->channel);
-       priv->factory = empathy_contact_factory_dup_singleton ();
-       priv->user = empathy_contact_factory_get_user (priv->factory, priv->account);
 
+       connection = tp_channel_borrow_connection (priv->channel);
+       priv->factory = empathy_tp_contact_factory_dup_singleton (connection);
        g_signal_connect (priv->channel, "invalidated",
                          G_CALLBACK (tp_chat_invalidated_cb),
                          chat);
 
-       g_object_get (priv->channel, "channel-ready", &channel_ready, NULL);
-       if (channel_ready) {
-               tp_chat_channel_ready_cb (EMPATHY_TP_CHAT (chat));
+       if (tp_proxy_has_interface_by_id (priv->channel,
+                                         TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
+               const TpIntSet *members;
+               GArray *handles;
+
+               /* Get self contact from the group's self handle */
+               handle = tp_channel_group_get_self_handle (priv->channel);
+               empathy_tp_contact_factory_get_from_handle (priv->factory,
+                       handle, tp_chat_got_self_contact_cb,
+                       NULL, NULL, chat);
+
+               /* Get initial member contacts */
+               members = tp_channel_group_get_members (priv->channel);
+               handles = tp_intset_to_array (members);
+               empathy_tp_contact_factory_get_from_handles (priv->factory,
+                       handles->len, (TpHandle *) handles->data,
+                       tp_chat_got_added_contacts_cb, NULL, NULL, chat);
+
+               g_signal_connect (priv->channel, "group-members-changed",
+                       G_CALLBACK (tp_chat_group_members_changed_cb), chat);
+       } else {
+               /* Get the self contact from the connection's self handle */
+               handle = tp_connection_get_self_handle (connection);
+               empathy_tp_contact_factory_get_from_handle (priv->factory,
+                       handle, tp_chat_got_self_contact_cb,
+                       NULL, NULL, chat);
+
+               /* Get the remote contact */
+               handle = tp_channel_get_handle (priv->channel, NULL);
+               empathy_tp_contact_factory_get_from_handle (priv->factory,
+                       handle, tp_chat_got_remote_contact_cb,
+                       NULL, NULL, chat);
+       }
+
+       if (tp_proxy_has_interface_by_id (priv->channel,
+                                         TP_IFACE_QUARK_PROPERTIES_INTERFACE)) {
+               tp_cli_properties_interface_call_list_properties (priv->channel, -1,
+                                                                 tp_chat_list_properties_cb,
+                                                                 NULL, NULL,
+                                                                 G_OBJECT (chat));
+               tp_cli_properties_interface_connect_to_properties_changed (priv->channel,
+                                                                          tp_chat_properties_changed_cb,
+                                                                          NULL, NULL,
+                                                                          G_OBJECT (chat), NULL);
+               tp_cli_properties_interface_connect_to_property_flags_changed (priv->channel,
+                                                                              tp_chat_property_flags_changed_cb,
+                                                                              NULL, NULL,
+                                                                              G_OBJECT (chat), NULL);
+       }
+
+       /* Check if the chat is password protected */
+       if (tp_proxy_has_interface_by_id (priv->channel,
+                                         TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD)) {
+               priv->got_password_flags = FALSE;
+
+               tp_cli_channel_interface_password_connect_to_password_flags_changed
+                       (priv->channel, password_flags_changed_cb, chat, NULL,
+                        G_OBJECT (chat), NULL);
+
+               tp_cli_channel_interface_password_call_get_password_flags
+                       (priv->channel, -1, got_password_flags_cb, chat, NULL, chat);
        } else {
-               g_signal_connect_swapped (priv->channel, "notify::channel-ready",
-                                         G_CALLBACK (tp_chat_channel_ready_cb),
-                                         chat);
+               /* No Password interface, so no need to fetch the password flags */
+               priv->got_password_flags = TRUE;
        }
 
        return chat;
@@ -1025,6 +1276,7 @@ tp_chat_get_property (GObject    *object,
                      GValue     *value,
                      GParamSpec *pspec)
 {
+       EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
        EmpathyTpChatPriv *priv = GET_PRIV (object);
 
        switch (param_id) {
@@ -1037,6 +1289,9 @@ tp_chat_get_property (GObject    *object,
        case PROP_READY:
                g_value_set_boolean (value, priv->ready);
                break;
+       case PROP_PASSWORD_NEEDED:
+               g_value_set_boolean (value, empathy_tp_chat_password_needed (self));
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
                break;
@@ -1088,6 +1343,7 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
                                                              "The remote contact if there is no group iface on the channel",
                                                              EMPATHY_TYPE_CONTACT,
                                                              G_PARAM_READABLE));
+
        g_object_class_install_property (object_class,
                                         PROP_READY,
                                         g_param_spec_boolean ("ready",
@@ -1096,6 +1352,14 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
                                                               FALSE,
                                                               G_PARAM_READABLE));
 
+       g_object_class_install_property (object_class,
+                                        PROP_PASSWORD_NEEDED,
+                                        g_param_spec_boolean ("password-needed",
+                                                              "password needed",
+                                                              "TRUE if a password is needed to join the channel",
+                                                              FALSE,
+                                                              G_PARAM_READABLE));
+
        /* Signals */
        signals[MESSAGE_RECEIVED] =
                g_signal_new ("message-received",
@@ -1113,9 +1377,9 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL, NULL,
-                             _empathy_marshal_VOID__OBJECT_UINT,
+                             _empathy_marshal_VOID__STRING_UINT,
                              G_TYPE_NONE,
-                             2, EMPATHY_TYPE_MESSAGE, G_TYPE_UINT);
+                             2, G_TYPE_STRING, G_TYPE_UINT);
 
        signals[CHAT_STATE_CHANGED] =
                g_signal_new ("chat-state-changed",
@@ -1184,28 +1448,28 @@ empathy_tp_chat_close (EmpathyTpChat *chat) {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
        /* If there are still messages left, it'll come back..
-          We loose the ordering of sent messages though */
-       g_signal_handlers_disconnect_by_func (priv->channel,
-               tp_chat_invalidated_cb, chat);
-
+        * We loose the ordering of sent messages though */
        tp_cli_channel_call_close (priv->channel, -1, tp_chat_async_cb,
                "closing channel", NULL, NULL);
-
-       g_object_unref (priv->channel);
-       priv->channel = NULL;
-
-       g_signal_emit (chat, signals[DESTROY], 0);
 }
 
 const gchar *
 empathy_tp_chat_get_id (EmpathyTpChat *chat)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
+       const gchar *id;
+
 
        g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
-       g_return_val_if_fail (priv->ready, NULL);
 
-       return priv->id;
+       id = tp_channel_get_identifier (priv->channel);
+       if (!EMP_STR_EMPTY (id))
+               return id;
+       else if (priv->remote_contact)
+               return empathy_contact_get_id (priv->remote_contact);
+       else
+               return NULL;
+
 }
 
 EmpathyContact *
@@ -1214,28 +1478,29 @@ empathy_tp_chat_get_remote_contact (EmpathyTpChat *chat)
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
        g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
+       g_return_val_if_fail (priv->ready, NULL);
 
        return priv->remote_contact;
 }
 
-McAccount *
-empathy_tp_chat_get_account (EmpathyTpChat *chat)
+TpChannel *
+empathy_tp_chat_get_channel (EmpathyTpChat *chat)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
-       g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE);
+       g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
 
-       return priv->account;
+       return priv->channel;
 }
 
-TpChannel *
-empathy_tp_chat_get_channel (EmpathyTpChat *chat)
+TpConnection *
+empathy_tp_chat_get_connection (EmpathyTpChat *chat)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
        g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
 
-       return priv->channel;
+       return tp_channel_borrow_connection (priv->channel);
 }
 
 gboolean
@@ -1248,16 +1513,6 @@ empathy_tp_chat_is_ready (EmpathyTpChat *chat)
        return priv->ready;
 }
 
-guint
-empathy_tp_chat_get_members_count (EmpathyTpChat *chat)
-{
-       EmpathyTpChatPriv *priv = GET_PRIV (chat);
-
-       g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), 0);
-
-       return priv->members_count;
-}
-
 void
 empathy_tp_chat_send (EmpathyTpChat *chat,
                      EmpathyMessage *message)
@@ -1292,13 +1547,16 @@ empathy_tp_chat_set_state (EmpathyTpChat      *chat,
        g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
        g_return_if_fail (priv->ready);
 
-       DEBUG ("Set state: %d", state);
-       tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
-                                                                state,
-                                                                tp_chat_async_cb,
-                                                                "setting chat state",
-                                                                NULL,
-                                                                G_OBJECT (chat));
+       if (tp_proxy_has_interface_by_id (priv->channel,
+                                         TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE)) {
+               DEBUG ("Set state: %d", state);
+               tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
+                                                                        state,
+                                                                        tp_chat_async_cb,
+                                                                        "setting chat state",
+                                                                        NULL,
+                                                                        G_OBJECT (chat));
+       }
 }
 
 
@@ -1307,6 +1565,9 @@ empathy_tp_chat_get_pending_messages (EmpathyTpChat *chat)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
+       g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
+       g_return_val_if_fail (priv->ready, NULL);
+
        return priv->pending_messages_queue->head;
 }
 
@@ -1327,7 +1588,10 @@ empathy_tp_chat_acknowledge_message (EmpathyTpChat *chat,
        GList *m;
        guint id;
 
-       if (empathy_message_get_sender (message) == priv->user)
+       g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
+       g_return_if_fail (priv->ready);
+
+       if (!empathy_message_is_incoming (message))
                goto out;
 
        message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
@@ -1354,7 +1618,10 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
        guint length;
        GArray *message_ids;
 
-       length = g_list_length ((GList *)messages);
+       g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
+       g_return_if_fail (priv->ready);
+
+       length = g_list_length ((GList *) messages);
 
        if (length == 0)
                return;
@@ -1370,10 +1637,11 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
                g_assert (m != NULL);
                g_queue_delete_link (priv->pending_messages_queue, m);
 
-               if (empathy_message_get_sender (message) != priv->user) {
+               if (empathy_message_is_incoming (message)) {
                        guint id = empathy_message_get_id (message);
                        g_array_append_val (message_ids, id);
                }
+               g_object_unref (message);
        }
 
        if (message_ids->len > 0)
@@ -1382,3 +1650,67 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
        g_array_free (message_ids, TRUE);
        g_list_free (msgs);
 }
+
+gboolean
+empathy_tp_chat_password_needed (EmpathyTpChat *self)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+       return priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
+}
+
+static void
+provide_password_cb (TpChannel *channel,
+                                     gboolean correct,
+                                     const GError *error,
+                                     gpointer user_data,
+                                     GObject *weak_object)
+{
+       GSimpleAsyncResult *result = user_data;
+
+       if (error != NULL) {
+               g_simple_async_result_set_from_error (result, error);
+       }
+       else if (!correct) {
+               /* The current D-Bus API is a bit weird so re-use the
+                * AuthenticationFailed error */
+               g_simple_async_result_set_error (result, TP_ERRORS,
+                                                TP_ERROR_AUTHENTICATION_FAILED, "Wrong password");
+       }
+
+       g_simple_async_result_complete (result);
+       g_object_unref (result);
+}
+
+void
+empathy_tp_chat_provide_password_async (EmpathyTpChat *self,
+                                                    const gchar *password,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer user_data)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
+       GSimpleAsyncResult *result;
+
+       result = g_simple_async_result_new (G_OBJECT (self),
+                                           callback, user_data,
+                                           empathy_tp_chat_provide_password_finish);
+
+       tp_cli_channel_interface_password_call_provide_password
+               (priv->channel, -1, password, provide_password_cb, result,
+                NULL, G_OBJECT (self));
+}
+
+gboolean
+empathy_tp_chat_provide_password_finish (EmpathyTpChat *self,
+                                                     GAsyncResult *result,
+                                                     GError **error)
+{
+       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+               error))
+               return FALSE;
+
+       g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                             G_OBJECT (self), empathy_tp_chat_provide_password_finish), FALSE);
+
+       return TRUE;
+}