]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-tp-chat.c
Updated Dutch translation master
[empathy.git] / libempathy / empathy-tp-chat.c
index 28c0e2a19ec666a8040a4eb735f4dea6822098fb..92e634ed2ade9bf2be930600143eda4c04cedbfd 100644 (file)
  * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
-#include <config.h>
-
-#include <string.h>
-
-#include <telepathy-glib/telepathy-glib.h>
+#include "config.h"
+#include "empathy-tp-chat.h"
 
-#include <extensions/extensions.h>
+#include <tp-account-widgets/tpaw-utils.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
-#include "empathy-tp-chat.h"
-#include "empathy-tp-contact-factory.h"
-#include "empathy-contact-list.h"
 #include "empathy-request-util.h"
-#include "empathy-time.h"
 #include "empathy-utils.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CHAT
@@ -42,8 +36,6 @@ struct _EmpathyTpChatPrivate
   EmpathyContact *user;
   EmpathyContact *remote_contact;
   GList *members;
-  /* Queue of messages not signalled yet */
-  GQueue *messages_queue;
   /* Queue of messages signalled but not acked yet */
   GQueue *pending_messages_queue;
 
@@ -63,10 +55,9 @@ struct _EmpathyTpChatPrivate
 
   /* GSimpleAsyncResult used when preparing EMPATHY_TP_CHAT_FEATURE_CORE */
   GSimpleAsyncResult *ready_result;
+  gboolean preparing_password;
 };
 
-static void tp_chat_iface_init (EmpathyContactListIface *iface);
-
 enum
 {
   PROP_0,
@@ -82,16 +73,15 @@ enum
 {
   MESSAGE_RECEIVED,
   SEND_ERROR,
-  CHAT_STATE_CHANGED,
   MESSAGE_ACKNOWLEDGED,
+  SIG_MEMBER_RENAMED,
+  SIG_MEMBERS_CHANGED,
   LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, TP_TYPE_TEXT_CHANNEL,
-       G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
-            tp_chat_iface_init));
+G_DEFINE_TYPE (EmpathyTpChat, empathy_tp_chat, TP_TYPE_TEXT_CHANNEL)
 
 static void
 tp_chat_set_delivery_status (EmpathyTpChat *self,
@@ -142,19 +132,46 @@ tp_chat_async_cb (TpChannel *proxy,
     DEBUG ("Error %s: %s", (gchar *) user_data, error->message);
 }
 
+static void
+update_config_cb (TpChannel *proxy,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  if (error != NULL)
+    DEBUG ("Failed to change config of the room: %s", error->message);
+}
+
 static void
 create_conference_cb (GObject *source,
     GAsyncResult *result,
     gpointer user_data)
 {
+  TpChannel *channel;
   GError *error = NULL;
+  GHashTable *props;
 
-  if (!tp_account_channel_request_create_channel_finish (
-      TP_ACCOUNT_CHANNEL_REQUEST (source), result, &error))
+  channel = tp_account_channel_request_create_and_observe_channel_finish (
+      TP_ACCOUNT_CHANNEL_REQUEST (source), result, &error);
+  if (channel == NULL)
     {
       DEBUG ("Failed to create conference channel: %s", error->message);
       g_error_free (error);
+      return;
     }
+
+  /* Make the channel more confidential as only people invited are supposed to
+   * join it. */
+  props = tp_asv_new (
+      "Private", G_TYPE_BOOLEAN, TRUE,
+      "InviteOnly", G_TYPE_BOOLEAN, TRUE,
+      NULL);
+
+  tp_cli_channel_interface_room_config_call_update_configuration (channel, -1,
+      props, update_config_cb, NULL, NULL, NULL);
+
+  g_object_unref (channel);
+  g_hash_table_unref (props);
 }
 
 void
@@ -179,36 +196,31 @@ empathy_tp_chat_add (EmpathyTpChat *self,
   else if (self->priv->can_upgrade_to_muc)
     {
       TpAccountChannelRequest *req;
-      GHashTable *props;
-      const char *object_path;
-      GPtrArray channels = { (gpointer *) &object_path, 1 };
+      const gchar *channels[2] = { NULL, };
       const char *invitees[2] = { NULL, };
+      TpAccount *account;
 
       invitees[0] = empathy_contact_get_id (contact);
-      object_path = tp_proxy_get_object_path (self);
-
-      props = tp_asv_new (
-          TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
-              TP_IFACE_CHANNEL_TYPE_TEXT,
-          TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
-              TP_HANDLE_TYPE_NONE,
-          TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS,
-              TP_ARRAY_TYPE_OBJECT_PATH_LIST, &channels,
-          TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS,
-              G_TYPE_STRV, invitees,
-          /* FIXME: InvitationMessage ? */
-          NULL);
-
-      req = tp_account_channel_request_new (self->priv->account, props,
+      channels[0] = tp_proxy_get_object_path (self);
+
+      account = empathy_tp_chat_get_account (self);
+
+      req = tp_account_channel_request_new_text (account,
         TP_USER_ACTION_TIME_NOT_USER_ACTION);
 
+      tp_account_channel_request_set_conference_initial_channels (req,
+          channels);
+
+      tp_account_channel_request_set_initial_invitee_ids (req, invitees);
+
+      /* FIXME: InvitationMessage ? */
+
       /* Although this is a MUC, it's anonymous, so CreateChannel is
        * valid. */
-      tp_account_channel_request_create_channel_async (req,
-          EMPATHY_CHAT_BUS_NAME, NULL, create_conference_cb, NULL);
+      tp_account_channel_request_create_and_observe_channel_async (req,
+          EMPATHY_CHAT_TP_BUS_NAME, NULL, create_conference_cb, NULL);
 
       g_object_unref (req);
-      g_hash_table_unref (props);
     }
   else
     {
@@ -216,31 +228,11 @@ empathy_tp_chat_add (EmpathyTpChat *self,
     }
 }
 
-static void
-tp_chat_remove (EmpathyContactList *list,
-    EmpathyContact *contact,
-    const gchar *message)
-{
-  EmpathyTpChat *self = (EmpathyTpChat *) 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));
-
-  handle = empathy_contact_get_handle (contact);
-  tp_cli_channel_interface_group_call_remove_members ((TpChannel *) self, -1,
-      &handles, NULL, NULL, NULL, NULL, NULL);
-}
-
-static GList *
-tp_chat_get_members (EmpathyContactList *list)
+GList *
+empathy_tp_chat_get_members (EmpathyTpChat *self)
 {
-  EmpathyTpChat *self = (EmpathyTpChat *) list;
   GList *members = NULL;
 
-  g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
-
   if (self->priv->members)
     {
       members = g_list_copy (self->priv->members);
@@ -264,58 +256,12 @@ check_ready (EmpathyTpChat *self)
   if (self->priv->ready_result == NULL)
     return;
 
-  if (g_queue_get_length (self->priv->messages_queue) > 0)
-    return;
-
   DEBUG ("Ready");
 
-  g_simple_async_result_complete (self->priv->ready_result);
+  g_simple_async_result_complete_in_idle (self->priv->ready_result);
   tp_clear_object (&self->priv->ready_result);
 }
 
-static void
-tp_chat_emit_queued_messages (EmpathyTpChat *self)
-{
-  EmpathyMessage *message;
-
-  /* Check if we can now emit some queued messages */
-  while ((message = g_queue_peek_head (self->priv->messages_queue)) != NULL)
-    {
-      if (empathy_message_get_sender (message) == NULL)
-        break;
-
-      DEBUG ("Queued message ready");
-      g_queue_pop_head (self->priv->messages_queue);
-      g_queue_push_tail (self->priv->pending_messages_queue, message);
-      g_signal_emit (self, signals[MESSAGE_RECEIVED], 0, message);
-    }
-
-  check_ready (self);
-}
-
-static void
-tp_chat_got_sender_cb (TpConnection *connection,
-    EmpathyContact *contact,
-    const GError *error,
-    gpointer message,
-    GObject *chat)
-{
-  EmpathyTpChat *self = (EmpathyTpChat *) chat;
-
-  if (error)
-    {
-      DEBUG ("Error: %s", error->message);
-      /* Do not block the message queue, just drop this message */
-      g_queue_remove (self->priv->messages_queue, message);
-    }
-  else
-    {
-      empathy_message_set_sender (message, contact);
-    }
-
-  tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (self));
-}
-
 static void
 tp_chat_build_message (EmpathyTpChat *self,
     TpMessage *msg,
@@ -328,26 +274,26 @@ tp_chat_build_message (EmpathyTpChat *self,
   /* FIXME: this is actually a lie for incoming messages. */
   empathy_message_set_receiver (message, self->priv->user);
 
-  g_queue_push_tail (self->priv->messages_queue, message);
-
   sender = tp_signalled_message_get_sender (msg);
   g_assert (sender != NULL);
 
   if (tp_contact_get_handle (sender) == 0)
     {
       empathy_message_set_sender (message, self->priv->user);
-      tp_chat_emit_queued_messages (self);
     }
   else
     {
-      TpConnection *connection = tp_channel_borrow_connection (
-        (TpChannel *) self);
+      EmpathyContact *contact;
+
+      contact = empathy_contact_dup_from_tp_contact (sender);
+
+      empathy_message_set_sender (message, contact);
 
-      empathy_tp_contact_factory_get_from_handle (connection,
-        tp_contact_get_handle (sender),
-        tp_chat_got_sender_cb,
-        message, NULL, G_OBJECT (self));
+      g_object_unref (contact);
     }
+
+  g_queue_push_tail (self->priv->pending_messages_queue, message);
+  g_signal_emit (self, signals[MESSAGE_RECEIVED], 0, message);
 }
 
 static void
@@ -517,7 +463,7 @@ message_sent_cb (TpTextChannel *channel,
 static TpChannelTextSendError
 error_to_text_send_error (GError *error)
 {
-  if (error->domain != TP_ERRORS)
+  if (error->domain != TP_ERROR)
     return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
 
   switch (error->code)
@@ -564,54 +510,12 @@ message_send_cb (GObject *source,
   g_free (token);
 }
 
-typedef struct {
-  EmpathyTpChat *chat;
-  TpChannelChatState state;
-} StateChangedData;
-
-static void
-tp_chat_state_changed_got_contact_cb (TpConnection *connection,
-    EmpathyContact *contact,
-    const GError *error,
-    gpointer user_data,
-    GObject *chat)
-{
-  TpChannelChatState state;
-
-  if (error != NULL)
-    {
-      DEBUG ("Error: %s", error->message);
-      return;
-    }
-
-  state = GPOINTER_TO_UINT (user_data);
-  DEBUG ("Chat state changed for %s (%d): %d",
-    empathy_contact_get_alias (contact),
-    empathy_contact_get_handle (contact), state);
-
-  g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
-}
-
-static void
-tp_chat_state_changed_cb (TpChannel *channel,
-    TpHandle handle,
-    TpChannelChatState state,
-    EmpathyTpChat *self)
-{
-  TpConnection *connection = tp_channel_borrow_connection (
-    (TpChannel *) self);
-
-  empathy_tp_contact_factory_get_from_handle (connection, handle,
-    tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state),
-    NULL, G_OBJECT (self));
-}
-
 static void
 list_pending_messages (EmpathyTpChat *self)
 {
   GList *messages, *l;
 
-  messages = tp_text_channel_get_pending_messages (TP_TEXT_CHANNEL (self));
+  messages = tp_text_channel_dup_pending_messages (TP_TEXT_CHANNEL (self));
 
   for (l = messages; l != NULL; l = g_list_next (l))
     {
@@ -620,7 +524,7 @@ list_pending_messages (EmpathyTpChat *self)
       handle_incoming_message (self, message, FALSE);
     }
 
-  g_list_free (messages);
+  g_list_free_full (messages, g_object_unref);
 }
 
 static void
@@ -774,13 +678,9 @@ tp_chat_dispose (GObject *object)
 {
   EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
 
-  tp_clear_object (&self->priv->account);
   tp_clear_object (&self->priv->remote_contact);
   tp_clear_object (&self->priv->user);
 
-  g_queue_foreach (self->priv->messages_queue, (GFunc) g_object_unref, NULL);
-  g_queue_clear (self->priv->messages_queue);
-
   g_queue_foreach (self->priv->pending_messages_queue,
     (GFunc) g_object_unref, NULL);
   g_queue_clear (self->priv->pending_messages_queue);
@@ -798,7 +698,6 @@ tp_chat_finalize (GObject *object)
 
   DEBUG ("Finalize: %p", object);
 
-  g_queue_free (self->priv->messages_queue);
   g_queue_free (self->priv->pending_messages_queue);
   g_hash_table_unref (self->priv->messages_being_sent);
 
@@ -820,6 +719,9 @@ check_almost_ready (EmpathyTpChat *self)
   if (self->priv->user == NULL)
     return;
 
+  if (self->priv->preparing_password)
+    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. */
@@ -840,57 +742,34 @@ check_almost_ready (EmpathyTpChat *self)
   tp_g_signal_connect_object (self, "message-sent",
     G_CALLBACK (message_sent_cb), self, 0);
 
-  tp_g_signal_connect_object (self, "chat-state-changed",
-    G_CALLBACK (tp_chat_state_changed_cb), self, 0);
-
   check_ready (self);
 }
 
 static void
-tp_chat_got_added_contacts_cb (TpConnection *connection,
-    guint n_contacts,
-    EmpathyContact * const * contacts,
-    guint n_failed,
-    const TpHandle *failed,
-    const GError *error,
-    gpointer user_data,
-    GObject *chat)
+add_members_contact (EmpathyTpChat *self,
+    GPtrArray *contacts)
 {
-  EmpathyTpChat *self = (EmpathyTpChat *) chat;
   guint i;
-  const TpIntSet *members;
-  TpHandle handle;
-  EmpathyContact *contact;
 
-  if (error)
+  for (i = 0; i < contacts->len; i++)
     {
-      DEBUG ("Error: %s", error->message);
-      return;
-    }
+      EmpathyContact *contact;
 
-  members = tp_channel_group_get_members ((TpChannel *) self);
-  for (i = 0; i < n_contacts; i++)
-    {
-      contact = contacts[i];
-      handle = empathy_contact_get_handle (contact);
+      contact = empathy_contact_dup_from_tp_contact (g_ptr_array_index (
+            contacts, i));
 
-      /* Make sure the contact is still member */
-      if (tp_intset_is_member (members, handle))
-        {
-          self->priv->members = g_list_prepend (self->priv->members,
-            g_object_ref (contact));
-          g_signal_emit_by_name (chat, "members-changed",
-                     contact, NULL, 0, NULL, TRUE);
-        }
+      self->priv->members = g_list_prepend (self->priv->members, contact);
+
+      g_signal_emit (self, signals[SIG_MEMBERS_CHANGED], 0,
+                 contact, NULL, 0, NULL, TRUE);
     }
 
-  check_almost_ready (EMPATHY_TP_CHAT (chat));
+  check_almost_ready (self);
 }
 
-static EmpathyContact *
-chat_lookup_contact (EmpathyTpChat *self,
-    TpHandle handle,
-    gboolean remove_)
+static void
+remove_member (EmpathyTpChat *self,
+    EmpathyContact *contact)
 {
   GList *l;
 
@@ -898,97 +777,36 @@ chat_lookup_contact (EmpathyTpChat *self,
     {
       EmpathyContact *c = l->data;
 
-      if (empathy_contact_get_handle (c) != handle)
-        continue;
-
-      if (remove_)
+      if (contact == c)
         {
-          /* Caller takes the reference. */
           self->priv->members = g_list_delete_link (self->priv->members, l);
+          g_object_unref (c);
+          break;
         }
-      else
-        {
-          g_object_ref (c);
-        }
-
-      return c;
     }
-
-  return 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);
-
-  return data;
-}
-
-static void
-contact_rename_data_free (ContactRenameData* data)
-{
-  g_free (data->message);
-  g_free (data);
 }
 
 static void
-tp_chat_got_renamed_contacts_cb (TpConnection *connection,
-    guint n_contacts,
-    EmpathyContact * const * contacts,
-    guint n_failed,
-    const TpHandle *failed,
-    const GError *error,
-    gpointer user_data,
-    GObject *chat)
+contact_renamed (EmpathyTpChat *self,
+    TpContact *old_contact,
+    TpContact *new_contact,
+    TpChannelGroupChangeReason reason,
+    const gchar *message)
 {
-  EmpathyTpChat *self = (EmpathyTpChat *) chat;
-  const TpIntSet *members;
-  TpHandle handle;
   EmpathyContact *old = NULL, *new = NULL;
-  ContactRenameData *rename_data = (ContactRenameData *) user_data;
-
-  if (error)
-    {
-      DEBUG ("Error: %s", error->message);
-      return;
-    }
-
-  /* renamed members can only be delivered one at a time */
-  g_warn_if_fail (n_contacts == 1);
 
-  new = contacts[0];
+  old = empathy_contact_dup_from_tp_contact (old_contact);
+  new = empathy_contact_dup_from_tp_contact (new_contact);
 
-  members = tp_channel_group_get_members ((TpChannel *) self);
-  handle = empathy_contact_get_handle (new);
+  self->priv->members = g_list_prepend (self->priv->members, new);
 
-  old = chat_lookup_contact (self, rename_data->old_handle, TRUE);
-
-  /* Make sure the contact is still member */
-  if (tp_intset_is_member (members, handle))
+  if (old != NULL)
     {
-      self->priv->members = g_list_prepend (self->priv->members,
-        g_object_ref (new));
+      remove_member (self, old);
 
-      if (old != NULL)
-        {
-          g_signal_emit_by_name (self, "member-renamed",
-                     old, new, rename_data->reason,
-                     rename_data->message);
-          g_object_unref (old);
-        }
+      g_signal_emit (self, signals[SIG_MEMBER_RENAMED], 0, old, new,
+          reason, message);
+      g_object_unref (old);
     }
 
   if (self->priv->user == old)
@@ -996,31 +814,29 @@ tp_chat_got_renamed_contacts_cb (TpConnection *connection,
       /* We change our nick */
       tp_clear_object (&self->priv->user);
       self->priv->user = g_object_ref (new);
-      g_object_notify (chat, "self-contact");
+      g_object_notify (G_OBJECT (self), "self-contact");
     }
 
   check_almost_ready (self);
 }
 
-
 static void
-tp_chat_group_members_changed_cb (TpChannel *channel,
-    gchar *message,
-    GArray *added,
-    GArray *removed,
-    GArray *local_pending,
-    GArray *remote_pending,
-    guint actor,
-    guint reason,
+tp_chat_group_contacts_changed_cb (TpChannel *channel,
+    GPtrArray *added,
+    GPtrArray *removed,
+    GPtrArray *local_pending,
+    GPtrArray *remote_pending,
+    TpContact *actor,
+    GHashTable *details,
     EmpathyTpChat *self)
 {
-  EmpathyContact *contact;
   EmpathyContact *actor_contact = NULL;
   guint i;
-  ContactRenameData *rename_data;
-  TpHandle old_handle;
-  TpConnection *connection = tp_channel_borrow_connection (
-    (TpChannel *) self);
+  TpChannelGroupChangeReason reason;
+  const gchar *message;
+
+  reason = tp_asv_get_uint32 (details, "change-reason", NULL);
+  message = tp_asv_get_string (details, "message");
 
   /* Contact renamed */
   if (reason == TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED)
@@ -1033,52 +849,47 @@ tp_chat_group_members_changed_cb (TpChannel *channel,
           return;
         }
 
-      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 (connection,
-        added->len, (TpHandle *) added->data,
-        tp_chat_got_renamed_contacts_cb,
-        rename_data, (GDestroyNotify) contact_rename_data_free,
-        G_OBJECT (self));
+      contact_renamed (self, g_ptr_array_index (removed, 0),
+          g_ptr_array_index (added, 0), reason, message);
       return;
     }
 
-  if (actor != 0)
+  if (actor != NULL)
     {
-      actor_contact = chat_lookup_contact (self, actor, FALSE);
+      actor_contact = empathy_contact_dup_from_tp_contact (actor);
+
       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);
+          DEBUG ("actor %s not a channel member",
+              tp_contact_get_identifier (actor));
         }
     }
 
   /* Remove contacts that are not members anymore */
   for (i = 0; i < removed->len; i++)
     {
-      contact = chat_lookup_contact (self,
-        g_array_index (removed, TpHandle, i), TRUE);
+      TpContact *tp_contact = g_ptr_array_index (removed, i);
+      EmpathyContact *contact;
+
+      contact = empathy_contact_dup_from_tp_contact (tp_contact);
 
       if (contact != NULL)
         {
-          g_signal_emit_by_name (self, "members-changed", contact,
-                     actor_contact, reason, message,
-                     FALSE);
+          remove_member (self, contact);
+
+          g_signal_emit (self, signals[SIG_MEMBERS_CHANGED], 0,
+                     contact, actor_contact, reason, message, FALSE);
           g_object_unref (contact);
         }
     }
 
-  /* Request added contacts */
   if (added->len > 0)
     {
-      empathy_tp_contact_factory_get_from_handles (connection,
-        added->len, (TpHandle *) added->data,
-        tp_chat_got_added_contacts_cb, NULL, NULL,
-        G_OBJECT (self));
+      add_members_contact (self, added);
     }
 
   if (actor_contact != NULL)
@@ -1086,46 +897,22 @@ tp_chat_group_members_changed_cb (TpChannel *channel,
 }
 
 static void
-tp_chat_got_remote_contact_cb (TpConnection *connection,
-    EmpathyContact *contact,
-    const GError *error,
-    gpointer user_data,
-    GObject *chat)
+create_remote_contact (EmpathyTpChat *self,
+    TpContact *contact)
 {
-  EmpathyTpChat *self = (EmpathyTpChat *) chat;
-
-  if (error != NULL)
-    {
-      DEBUG ("Error: %s", error->message);
-      empathy_tp_chat_leave (self, "");
-      return;
-    }
-
-  self->priv->remote_contact = g_object_ref (contact);
-  g_object_notify (chat, "remote-contact");
+  self->priv->remote_contact = empathy_contact_dup_from_tp_contact (contact);
+  g_object_notify (G_OBJECT (self), "remote-contact");
 
   check_almost_ready (self);
 }
 
 static void
-tp_chat_got_self_contact_cb (TpConnection *connection,
-    EmpathyContact *contact,
-    const GError *error,
-    gpointer user_data,
-    GObject *chat)
+create_self_contact (EmpathyTpChat *self,
+    TpContact *contact)
 {
-  EmpathyTpChat *self = (EmpathyTpChat *) chat;
-
-  if (error != NULL)
-    {
-      DEBUG ("Error: %s", error->message);
-      empathy_tp_chat_leave (self, "");
-      return;
-    }
-
-  self->priv->user = g_object_ref (contact);
+  self->priv->user = empathy_contact_dup_from_tp_contact (contact);
   empathy_contact_set_is_user (self->priv->user, TRUE);
-  g_object_notify (chat, "self-contact");
+  g_object_notify (G_OBJECT (self), "self-contact");
   check_almost_ready (self);
 }
 
@@ -1139,9 +926,6 @@ tp_chat_get_property (GObject *object,
 
   switch (param_id)
     {
-      case PROP_ACCOUNT:
-        g_value_set_object (value, self->priv->account);
-        break;
       case PROP_SELF_CONTACT:
         g_value_set_object (value, self->priv->user);
         break;
@@ -1166,25 +950,6 @@ tp_chat_get_property (GObject *object,
     };
 }
 
-static void
-tp_chat_set_property (GObject *object,
-    guint param_id,
-    const GValue *value,
-    GParamSpec   *pspec)
-{
-  EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
-
-  switch (param_id)
-    {
-      case PROP_ACCOUNT:
-        self->priv->account = g_value_dup_object (value);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-        break;
-    };
-}
-
 enum {
   FEAT_READY,
   N_FEAT
@@ -1194,13 +959,14 @@ static const TpProxyFeature *
 tp_chat_list_features (TpProxyClass *cls G_GNUC_UNUSED)
 {
   static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
-  static GQuark need[2] = {0, 0};
+  static GQuark need[3] = {0, 0, 0};
 
   if (G_LIKELY (features[0].name != 0))
     return features;
 
   features[FEAT_READY].name = EMPATHY_TP_CHAT_FEATURE_READY;
   need[0] = TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES;
+  need[1] = TP_CHANNEL_FEATURE_CONTACTS;
   features[FEAT_READY].depends_on = need;
   features[FEAT_READY].prepare_async =
     tp_chat_prepare_ready_async;
@@ -1220,18 +986,9 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
   object_class->dispose = tp_chat_dispose;
   object_class->finalize = tp_chat_finalize;
   object_class->get_property = tp_chat_get_property;
-  object_class->set_property = tp_chat_set_property;
 
   proxy_class->list_features = tp_chat_list_features;
 
-  g_object_class_install_property (object_class, PROP_ACCOUNT,
-      g_param_spec_object ("account", "TpAccount",
-        "the account associated with the chat",
-        TP_TYPE_ACCOUNT,
-        G_PARAM_READWRITE |
-        G_PARAM_CONSTRUCT_ONLY |
-        G_PARAM_STATIC_STRINGS));
-
   /**
    * EmpathyTpChat:self-contact:
    *
@@ -1288,23 +1045,30 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
       G_TYPE_NONE,
       3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
 
-  signals[CHAT_STATE_CHANGED] = g_signal_new ("chat-state-changed-empathy",
+  signals[MESSAGE_ACKNOWLEDGED] = g_signal_new ("message-acknowledged",
       G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST,
       0,
       NULL, NULL,
       g_cclosure_marshal_generic,
       G_TYPE_NONE,
-      2, EMPATHY_TYPE_CONTACT, G_TYPE_UINT);
+      1, EMPATHY_TYPE_MESSAGE);
 
-  signals[MESSAGE_ACKNOWLEDGED] = g_signal_new ("message-acknowledged",
-      G_TYPE_FROM_CLASS (klass),
+  signals[SIG_MEMBER_RENAMED] = g_signal_new ("member-renamed",
+      G_OBJECT_CLASS_TYPE (klass),
       G_SIGNAL_RUN_LAST,
-      0,
-      NULL, NULL,
-      g_cclosure_marshal_generic,
+      0, NULL, NULL, NULL,
       G_TYPE_NONE,
-      1, EMPATHY_TYPE_MESSAGE);
+      4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT,
+      G_TYPE_UINT, G_TYPE_STRING);
+
+  signals[SIG_MEMBERS_CHANGED] = g_signal_new ("members-changed",
+      G_OBJECT_CLASS_TYPE (klass),
+      G_SIGNAL_RUN_LAST,
+      0, NULL, NULL, NULL,
+      G_TYPE_NONE,
+      5, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT,
+      G_TYPE_UINT, G_TYPE_STRING, G_TYPE_BOOLEAN);
 
   g_type_class_add_private (object_class, sizeof (EmpathyTpChatPrivate));
 }
@@ -1315,38 +1079,25 @@ empathy_tp_chat_init (EmpathyTpChat *self)
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_TP_CHAT,
       EmpathyTpChatPrivate);
 
-  self->priv->messages_queue = g_queue_new ();
   self->priv->pending_messages_queue = g_queue_new ();
   self->priv->messages_being_sent = g_hash_table_new_full (
       g_str_hash, g_str_equal, g_free, NULL);
 }
 
-static void
-tp_chat_iface_init (EmpathyContactListIface *iface)
-{
-  iface->remove = tp_chat_remove;
-  iface->get_members = tp_chat_get_members;
-}
-
 EmpathyTpChat *
 empathy_tp_chat_new (TpSimpleClientFactory *factory,
-    TpAccount *account,
     TpConnection *conn,
     const gchar *object_path,
     const GHashTable *immutable_properties)
 {
-  TpProxy *conn_proxy = (TpProxy *) conn;
-
-  g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
   g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
   g_return_val_if_fail (immutable_properties != NULL, NULL);
 
   return g_object_new (EMPATHY_TYPE_TP_CHAT,
       "factory", factory,
-       "account", account,
        "connection", conn,
-       "dbus-daemon", conn_proxy->dbus_daemon,
-       "bus-name", conn_proxy->bus_name,
+       "dbus-daemon", tp_proxy_get_dbus_daemon (conn),
+       "bus-name", tp_proxy_get_bus_name (conn),
        "object-path", object_path,
        "channel-properties", immutable_properties,
        NULL);
@@ -1360,7 +1111,7 @@ empathy_tp_chat_get_id (EmpathyTpChat *self)
   g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
 
   id = tp_channel_get_identifier ((TpChannel *) self);
-  if (!EMP_STR_EMPTY (id))
+  if (!TPAW_STR_EMPTY (id))
     return id;
   else if (self->priv->remote_contact)
     return empathy_contact_get_id (self->priv->remote_contact);
@@ -1380,9 +1131,13 @@ empathy_tp_chat_get_remote_contact (EmpathyTpChat *self)
 TpAccount *
 empathy_tp_chat_get_account (EmpathyTpChat *self)
 {
+  TpConnection *connection;
+
   g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
 
-  return self->priv->account;
+  connection = tp_channel_get_connection (TP_CHANNEL (self));
+
+  return tp_connection_get_account (connection);
 }
 
 void
@@ -1474,61 +1229,30 @@ empathy_tp_chat_leave (EmpathyTpChat *self,
     message, tp_channel_leave_async_cb, self);
 }
 
-static void
-add_members_cb (TpChannel *proxy,
-    const GError *error,
-    gpointer user_data,
-    GObject *weak_object)
-{
-  EmpathyTpChat *self = (EmpathyTpChat *) weak_object;
-
-  if (error != NULL)
-    {
-      DEBUG ("Failed to join chat (%s): %s",
-        tp_channel_get_identifier ((TpChannel *) self), error->message);
-    }
-}
-
-void
-empathy_tp_chat_join (EmpathyTpChat *self)
-{
-  TpHandle self_handle;
-  GArray *members;
-
-  self_handle = tp_channel_group_get_self_handle ((TpChannel *) self);
-
-  members = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1);
-  g_array_append_val (members, self_handle);
-
-  tp_cli_channel_interface_group_call_add_members ((TpChannel *) self, -1,
-      members, "", add_members_cb, NULL, NULL, G_OBJECT (self));
-
-  g_array_unref (members);
-}
-
 gboolean
 empathy_tp_chat_is_invited (EmpathyTpChat *self,
-    TpHandle *inviter)
+    TpContact **inviter)
 {
-  TpHandle self_handle;
+  TpContact *self_contact;
+  TpChannel *channel = TP_CHANNEL (self);
 
   if (!tp_proxy_has_interface (self, TP_IFACE_CHANNEL_INTERFACE_GROUP))
     return FALSE;
 
-  self_handle = tp_channel_group_get_self_handle ((TpChannel *) self);
-  if (self_handle == 0)
+  self_contact = tp_channel_group_get_self_contact (channel);
+  if (self_contact == NULL)
     return FALSE;
 
-  return tp_channel_group_get_local_pending_info ((TpChannel *) self,
-      self_handle, inviter, NULL, NULL);
+  return tp_channel_group_get_local_pending_contact_info (channel,
+      self_contact, inviter, NULL, NULL);
 }
 
 TpChannelChatState
 empathy_tp_chat_get_chat_state (EmpathyTpChat *self,
     EmpathyContact *contact)
 {
-  return tp_channel_get_chat_state ((TpChannel *) self,
-    empathy_contact_get_handle (contact));
+  return tp_text_channel_get_chat_state ((TpTextChannel *) self,
+    empathy_contact_get_tp_contact (contact));
 }
 
 EmpathyContact *
@@ -1546,84 +1270,123 @@ empathy_tp_chat_get_feature_ready (void)
 }
 
 static void
-tp_chat_prepare_ready_async (TpProxy *proxy,
-  const TpProxyFeature *feature,
-  GAsyncReadyCallback callback,
-  gpointer user_data)
+password_feature_prepare_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
 {
-  EmpathyTpChat *self = (EmpathyTpChat *) proxy;
-  TpChannel *channel = (TpChannel *) proxy;
+  EmpathyTpChat *self = user_data;
+  GError *error = NULL;
+
+  if (!tp_proxy_prepare_finish (source, result, &error))
+    {
+      DEBUG ("Failed to prepare Password: %s", error->message);
+      g_error_free (error);
+    }
+
+  self->priv->preparing_password = FALSE;
+
+  check_almost_ready (self);
+}
+
+static void
+continue_preparing (EmpathyTpChat *self)
+{
+  TpChannel *channel = (TpChannel *) self;
   TpConnection *connection;
   gboolean listen_for_dbus_properties_changed = FALSE;
 
-  g_assert (self->priv->ready_result == NULL);
-  self->priv->ready_result = g_simple_async_result_new (G_OBJECT (self),
-    callback, user_data, tp_chat_prepare_ready_async);
+  connection = tp_channel_get_connection (channel);
+
+  if (tp_proxy_has_interface_by_id (self,
+        TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD))
+    {
+      /* The password feature can't be a hard dep on our own feature has we
+       * depend on it only if the channel implements the
+       * Password interface.
+       */
+      GQuark features[] = { TP_CHANNEL_FEATURE_PASSWORD , 0 };
+
+      self->priv->preparing_password = TRUE;
 
-  connection = tp_channel_borrow_connection (channel);
+      tp_proxy_prepare_async (self, features, password_feature_prepare_cb,
+          self);
+    }
 
   if (tp_proxy_has_interface_by_id (self,
             TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP))
     {
-      const TpIntSet *members;
-      GArray *handles;
-      TpHandle handle;
+      GPtrArray *contacts;
+      TpContact *contact;
 
       /* Get self contact from the group's self handle */
-      handle = tp_channel_group_get_self_handle (channel);
-      empathy_tp_contact_factory_get_from_handle (connection,
-        handle, tp_chat_got_self_contact_cb,
-        NULL, NULL, G_OBJECT (self));
+      contact = tp_channel_group_get_self_contact (channel);
+      create_self_contact (self, contact);
 
       /* Get initial member contacts */
-      members = tp_channel_group_get_members (channel);
-      handles = tp_intset_to_array (members);
-      empathy_tp_contact_factory_get_from_handles (connection,
-        handles->len, (TpHandle *) handles->data,
-        tp_chat_got_added_contacts_cb, NULL, NULL, G_OBJECT (self));
+      contacts = tp_channel_group_dup_members_contacts (channel);
+      add_members_contact (self, contacts);
+      g_ptr_array_unref (contacts);
 
       self->priv->can_upgrade_to_muc = FALSE;
 
-      tp_g_signal_connect_object (self, "group-members-changed",
-        G_CALLBACK (tp_chat_group_members_changed_cb), self, 0);
+      tp_g_signal_connect_object (self, "group-contacts-changed",
+        G_CALLBACK (tp_chat_group_contacts_changed_cb), self, 0);
     }
   else
     {
       TpCapabilities *caps;
-      GPtrArray *classes;
-      guint i;
-      TpHandle handle;
+      GVariant *classes, *class;
+      GVariantIter iter;
+      TpContact *contact;
 
       /* Get the self contact from the connection's self handle */
-      handle = tp_connection_get_self_handle (connection);
-      empathy_tp_contact_factory_get_from_handle (connection,
-        handle, tp_chat_got_self_contact_cb,
-        NULL, NULL, G_OBJECT (self));
+      contact = tp_connection_get_self_contact (connection);
+      create_self_contact (self, contact);
 
       /* Get the remote contact */
-      handle = tp_channel_get_handle (channel, NULL);
-      empathy_tp_contact_factory_get_from_handle (connection,
-        handle, tp_chat_got_remote_contact_cb,
-        NULL, NULL, G_OBJECT (self));
+      contact = tp_channel_get_target_contact (channel);
+      create_remote_contact (self, contact);
 
       caps = tp_connection_get_capabilities (connection);
       g_assert (caps != NULL);
 
-      classes = tp_capabilities_get_channel_classes (caps);
+      classes = tp_capabilities_dup_channel_classes_variant (caps);
 
-      for (i = 0; i < classes->len; i++)
+      g_variant_iter_init (&iter, classes);
+      while ((class = g_variant_iter_next_value (&iter)))
         {
-          GValueArray *array = g_ptr_array_index (classes, i);
-          const char **oprops = g_value_get_boxed (
-            g_value_array_get_nth (array, 1));
+          GVariant *fixed, *allowed;
+          const gchar *chan_type = NULL;
 
-          if (tp_strv_contains (oprops,
-                TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS))
+          fixed = g_variant_get_child_value (class, 0);
+          allowed = g_variant_get_child_value (class, 1);
+
+          g_variant_lookup (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE, "&s",
+              &chan_type);
+          if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT))
             {
-              self->priv->can_upgrade_to_muc = TRUE;
-              break;
+              const gchar **oprops;
+
+              oprops = g_variant_get_strv (allowed, NULL);
+
+              if (tp_strv_contains (oprops,
+                    TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS))
+                {
+                  self->priv->can_upgrade_to_muc = TRUE;
+                }
+
+              g_free (oprops);
             }
+
+          g_variant_unref (class);
+          g_variant_unref (fixed);
+          g_variant_unref (allowed);
+
+          if (self->priv->can_upgrade_to_muc)
+            break;
         }
+
+      g_variant_unref (classes);
     }
 
   if (tp_proxy_has_interface_by_id (self,
@@ -1656,3 +1419,49 @@ tp_chat_prepare_ready_async (TpProxy *proxy,
                         G_OBJECT (self), NULL);
     }
 }
+
+static void
+conn_connected_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  EmpathyTpChat *self = user_data;
+  GError *error = NULL;
+
+  if (!tp_proxy_prepare_finish (source, result, &error))
+    {
+      DEBUG ("Failed to prepare Connected: %s", error->message);
+      g_simple_async_result_take_error (self->priv->ready_result, error);
+      g_simple_async_result_complete (self->priv->ready_result);
+      tp_clear_object (&self->priv->ready_result);
+      return;
+    }
+
+  continue_preparing (self);
+}
+
+static void
+tp_chat_prepare_ready_async (TpProxy *proxy,
+  const TpProxyFeature *feature,
+  GAsyncReadyCallback callback,
+  gpointer user_data)
+{
+  EmpathyTpChat *self = (EmpathyTpChat *) proxy;
+  TpChannel *channel = (TpChannel *) proxy;
+  TpConnection *connection;
+  GQuark features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 };
+
+  g_assert (self->priv->ready_result == NULL);
+
+  self->priv->ready_result = g_simple_async_result_new (G_OBJECT (self),
+    callback, user_data, tp_chat_prepare_ready_async);
+
+  connection = tp_channel_get_connection (channel);
+
+  /* First we have to make sure that TP_CONNECTION_FEATURE_CONNECTED is
+   * prepared as we rely on TpConnection::self-contact
+   * in continue_preparing().
+   *
+   * It would be nice if tp-glib could do this for us: fdo#59126 */
+  tp_proxy_prepare_async (connection, features, conn_connected_cb, proxy);
+}