]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-tp-chat.c
Merge branch 'sasl'
[empathy.git] / libempathy / empathy-tp-chat.c
index cad014df362c64e6f42799014d0b8c65b8a32a38..f1351049aedf8809d421fce0834a63eeb8514ba9 100644 (file)
@@ -29,9 +29,7 @@
 
 #include "empathy-tp-chat.h"
 #include "empathy-tp-contact-factory.h"
-#include "empathy-contact-monitor.h"
 #include "empathy-contact-list.h"
-#include "empathy-dispatcher.h"
 #include "empathy-marshal.h"
 #include "empathy-time.h"
 #include "empathy-utils.h"
@@ -42,8 +40,8 @@
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpChat)
 typedef struct {
        gboolean               dispose_has_run;
-       EmpathyTpContactFactory *factory;
-       EmpathyContactMonitor *contact_monitor;
+       TpAccount             *account;
+       TpConnection          *connection;
        EmpathyContact        *user;
        EmpathyContact        *remote_contact;
        GList                 *members;
@@ -67,6 +65,7 @@ static void tp_chat_iface_init         (EmpathyContactListIface *iface);
 
 enum {
        PROP_0,
+       PROP_ACCOUNT,
        PROP_CHANNEL,
        PROP_REMOTE_CONTACT,
        PROP_PASSWORD_NEEDED,
@@ -112,6 +111,20 @@ tp_chat_async_cb (TpChannel *proxy,
        }
 }
 
+static void
+create_conference_cb (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+       GError *error = NULL;
+
+       if (!tp_account_channel_request_create_channel_finish (
+                       TP_ACCOUNT_CHANNEL_REQUEST (source), result, &error)) {
+               DEBUG ("Failed to create conference channel: %s", error->message);
+               g_error_free (error);
+       }
+}
+
 static void
 tp_chat_add (EmpathyContactList *list,
             EmpathyContact     *contact,
@@ -131,38 +144,37 @@ tp_chat_add (EmpathyContactList *list,
                tp_cli_channel_interface_group_call_add_members (priv->channel,
                        -1, &handles, NULL, NULL, NULL, NULL, NULL);
        } else if (priv->can_upgrade_to_muc) {
-               EmpathyDispatcher *dispatcher;
-               TpConnection      *connection;
+               TpAccountChannelRequest *req;
                GHashTable        *props;
                const char        *object_path;
                GPtrArray          channels = { (gpointer *) &object_path, 1 };
                const char        *invitees[2] = { NULL, };
 
-               dispatcher = empathy_dispatcher_dup_singleton ();
-               connection = tp_channel_borrow_connection (priv->channel);
-
                invitees[0] = empathy_contact_get_id (contact);
                object_path = tp_proxy_get_object_path (priv->channel);
 
                props = tp_asv_new (
-                   TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING,
+                   TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
                        TP_IFACE_CHANNEL_TYPE_TEXT,
-                   TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_UINT,
+                   TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
                        TP_HANDLE_TYPE_NONE,
-                   EMP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels",
+                   TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS,
                        TP_ARRAY_TYPE_OBJECT_PATH_LIST, &channels,
-                   EMP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs",
+                   TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS,
                        G_TYPE_STRV, invitees,
                    /* FIXME: InvitationMessage ? */
                    NULL);
 
+               req = tp_account_channel_request_new (priv->account, props,
+                       TP_USER_ACTION_TIME_NOT_USER_ACTION);
+
                /* Although this is a MUC, it's anonymous, so CreateChannel is
-                * valid.
-                * props now belongs to EmpathyDispatcher, don't free it */
-               empathy_dispatcher_create_channel (dispatcher, connection,
-                               props, EMPATHY_DISPATCHER_NON_USER_ACTION, NULL, NULL);
+                * valid. */
+               tp_account_channel_request_create_channel_async (req, NULL, NULL,
+                       create_conference_cb, NULL);
 
-               g_object_unref (dispatcher);
+               g_object_unref (req);
+               g_hash_table_unref (props);
        } else {
                g_warning ("Cannot add to this channel");
        }
@@ -207,20 +219,21 @@ tp_chat_get_members (EmpathyContactList *list)
        return members;
 }
 
-static EmpathyContactMonitor *
-tp_chat_get_monitor (EmpathyContactList *list)
+static void
+check_ready (EmpathyTpChat *chat)
 {
-       EmpathyTpChatPriv *priv;
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
-       g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
+       if (priv->ready)
+               return;
 
-       priv = GET_PRIV (list);
+       if (g_queue_get_length (priv->messages_queue) > 0)
+               return;
 
-       if (priv->contact_monitor == NULL) {
-               priv->contact_monitor = empathy_contact_monitor_new_for_iface (list);
-       }
+       DEBUG ("Ready");
 
-       return priv->contact_monitor;
+       priv->ready = TRUE;
+       g_object_notify (G_OBJECT (chat), "ready");
 }
 
 static void
@@ -240,10 +253,12 @@ tp_chat_emit_queued_messages (EmpathyTpChat *chat)
                g_queue_push_tail (priv->pending_messages_queue, message);
                g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
        }
+
+       check_ready (chat);
 }
 
 static void
-tp_chat_got_sender_cb (EmpathyTpContactFactory *factory,
+tp_chat_got_sender_cb (TpConnection            *connection,
                       EmpathyContact          *contact,
                       const GError            *error,
                       gpointer                 message,
@@ -285,13 +300,16 @@ tp_chat_build_message (EmpathyTpChat *chat,
        empathy_message_set_incoming (message, incoming);
        empathy_message_set_flags (message, flags);
 
+       if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK)
+               empathy_message_set_is_backlog (message, TRUE);
+
        g_queue_push_tail (priv->messages_queue, message);
 
        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,
+               empathy_tp_contact_factory_get_from_handle (priv->connection,
                        from_handle,
                        tp_chat_got_sender_cb,
                        message, NULL, G_OBJECT (chat));
@@ -412,7 +430,7 @@ typedef struct {
 } StateChangedData;
 
 static void
-tp_chat_state_changed_got_contact_cb (EmpathyTpContactFactory *factory,
+tp_chat_state_changed_got_contact_cb (TpConnection            *connection,
                                      EmpathyContact          *contact,
                                      const GError            *error,
                                      gpointer                 user_data,
@@ -427,7 +445,7 @@ tp_chat_state_changed_got_contact_cb (EmpathyTpContactFactory *factory,
 
        state = GPOINTER_TO_UINT (user_data);
        DEBUG ("Chat state changed for %s (%d): %d",
-               empathy_contact_get_name (contact),
+               empathy_contact_get_alias (contact),
                empathy_contact_get_handle (contact), state);
 
        g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
@@ -442,7 +460,7 @@ tp_chat_state_changed_cb (TpChannel *channel,
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
-       empathy_tp_contact_factory_get_from_handle (priv->factory, handle,
+       empathy_tp_contact_factory_get_from_handle (priv->connection, handle,
                tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state),
                NULL, chat);
 }
@@ -515,6 +533,8 @@ tp_chat_list_pending_messages_cb (TpChannel       *channel,
                acknowledge_messages (chat, empty_non_text_content_ids);
                g_array_free (empty_non_text_content_ids, TRUE);
        }
+
+       check_ready (chat);
 }
 
 static void
@@ -758,6 +778,12 @@ tp_chat_dispose (GObject *object)
 
        priv->dispose_has_run = TRUE;
 
+       tp_clear_object (&priv->account);
+
+       if (priv->connection != NULL)
+               g_object_unref (priv->connection);
+       priv->connection = NULL;
+
        if (priv->channel != NULL) {
                g_signal_handlers_disconnect_by_func (priv->channel,
                        tp_chat_invalidated_cb, self);
@@ -769,18 +795,10 @@ tp_chat_dispose (GObject *object)
                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);
 
@@ -821,7 +839,7 @@ tp_chat_finalize (GObject *object)
 }
 
 static void
-tp_chat_check_if_ready (EmpathyTpChat *chat)
+check_almost_ready (EmpathyTpChat *chat)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
@@ -841,13 +859,14 @@ tp_chat_check_if_ready (EmpathyTpChat *chat)
            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;
+
+       /* TpChat will be ready once ListPendingMessages returned and all the messages
+        * have been added to the pending messages queue. */
        tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
                                                             FALSE,
                                                             tp_chat_list_pending_messages_cb,
@@ -866,8 +885,6 @@ tp_chat_check_if_ready (EmpathyTpChat *chat)
                                                                           tp_chat_state_changed_cb,
                                                                           NULL, NULL,
                                                                           G_OBJECT (chat), NULL);
-       priv->ready = TRUE;
-       g_object_notify (G_OBJECT (chat), "ready");
 }
 
 static void
@@ -889,7 +906,7 @@ tp_chat_update_remote_contact (EmpathyTpChat *chat)
         * have the group interface. If it has the conference interface, then
         * it is indeed a MUC. */
        if (tp_proxy_has_interface_by_id (priv->channel,
-                                         EMP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE)) {
+                                         TP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE)) {
                return;
        }
 
@@ -931,7 +948,7 @@ tp_chat_update_remote_contact (EmpathyTpChat *chat)
 }
 
 static void
-tp_chat_got_added_contacts_cb (EmpathyTpContactFactory *factory,
+tp_chat_got_added_contacts_cb (TpConnection            *connection,
                               guint                    n_contacts,
                               EmpathyContact * const * contacts,
                               guint                    n_failed,
@@ -966,7 +983,7 @@ tp_chat_got_added_contacts_cb (EmpathyTpContactFactory *factory,
        }
 
        tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat));
-       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+       check_almost_ready (EMPATHY_TP_CHAT (chat));
 }
 
 static EmpathyContact *
@@ -1025,7 +1042,7 @@ contact_rename_data_free (ContactRenameData* data)
 }
 
 static void
-tp_chat_got_renamed_contacts_cb (EmpathyTpContactFactory *factory,
+tp_chat_got_renamed_contacts_cb (TpConnection            *connection,
                                  guint                    n_contacts,
                                  EmpathyContact * const * contacts,
                                  guint                    n_failed,
@@ -1076,7 +1093,7 @@ tp_chat_got_renamed_contacts_cb (EmpathyTpContactFactory *factory,
        }
 
        tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat));
-       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+       check_almost_ready (EMPATHY_TP_CHAT (chat));
 }
 
 
@@ -1101,13 +1118,16 @@ tp_chat_group_members_changed_cb (TpChannel     *self,
        /* 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);
+               if (removed->len != 1 || added->len != 1) {
+                       g_warning ("RENAMED with %u added, %u removed (expected 1, 1)",
+                               added->len, removed->len);
+                       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 (priv->factory,
+               empathy_tp_contact_factory_get_from_handles (priv->connection,
                        added->len, (TpHandle *) added->data,
                        tp_chat_got_renamed_contacts_cb,
                        rename_data, (GDestroyNotify) contact_rename_data_free,
@@ -1141,7 +1161,7 @@ tp_chat_group_members_changed_cb (TpChannel     *self,
 
        /* Request added contacts */
        if (added->len > 0) {
-               empathy_tp_contact_factory_get_from_handles (priv->factory,
+               empathy_tp_contact_factory_get_from_handles (priv->connection,
                        added->len, (TpHandle *) added->data,
                        tp_chat_got_added_contacts_cb, NULL, NULL,
                        G_OBJECT (chat));
@@ -1155,7 +1175,7 @@ tp_chat_group_members_changed_cb (TpChannel     *self,
 }
 
 static void
-tp_chat_got_remote_contact_cb (EmpathyTpContactFactory *factory,
+tp_chat_got_remote_contact_cb (TpConnection            *connection,
                               EmpathyContact          *contact,
                               const GError            *error,
                               gpointer                 user_data,
@@ -1172,11 +1192,11 @@ tp_chat_got_remote_contact_cb (EmpathyTpContactFactory *factory,
        priv->remote_contact = g_object_ref (contact);
        g_object_notify (chat, "remote-contact");
 
-       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+       check_almost_ready (EMPATHY_TP_CHAT (chat));
 }
 
 static void
-tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory,
+tp_chat_got_self_contact_cb (TpConnection            *connection,
                             EmpathyContact          *contact,
                             const GError            *error,
                             gpointer                 user_data,
@@ -1192,7 +1212,7 @@ tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory,
 
        priv->user = g_object_ref (contact);
        empathy_contact_set_is_user (priv->user, TRUE);
-       tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+       check_almost_ready (EMPATHY_TP_CHAT (chat));
 }
 
 static void
@@ -1230,7 +1250,7 @@ got_password_flags_cb (TpChannel *proxy,
        priv->got_password_flags = TRUE;
        priv->password_flags = password_flags;
 
-       tp_chat_check_if_ready (EMPATHY_TP_CHAT (self));
+       check_almost_ready (EMPATHY_TP_CHAT (self));
 }
 
 static GObject *
@@ -1240,18 +1260,19 @@ tp_chat_constructor (GType                  type,
 {
        GObject           *chat;
        EmpathyTpChatPriv *priv;
-       TpConnection      *connection;
        TpHandle           handle;
 
        chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
 
        priv = GET_PRIV (chat);
 
-       connection = tp_channel_borrow_connection (priv->channel);
-       priv->factory = empathy_tp_contact_factory_dup_singleton (connection);
-       g_signal_connect (priv->channel, "invalidated",
+       priv->connection = g_object_ref (tp_account_get_connection (priv->account));
+       tp_g_signal_connect_object (priv->channel, "invalidated",
                          G_CALLBACK (tp_chat_invalidated_cb),
-                         chat);
+                         chat, 0);
+
+       g_assert (tp_proxy_is_prepared (priv->connection,
+               TP_CONNECTION_FEATURE_CAPABILITIES));
 
        if (tp_proxy_has_interface_by_id (priv->channel,
                                          TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
@@ -1260,55 +1281,53 @@ tp_chat_constructor (GType                  type,
 
                /* 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,
+               empathy_tp_contact_factory_get_from_handle (priv->connection,
                        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,
+               empathy_tp_contact_factory_get_from_handles (priv->connection,
                        handles->len, (TpHandle *) handles->data,
                        tp_chat_got_added_contacts_cb, NULL, NULL, chat);
 
                priv->can_upgrade_to_muc = FALSE;
 
-               g_signal_connect (priv->channel, "group-members-changed",
-                       G_CALLBACK (tp_chat_group_members_changed_cb), chat);
+               tp_g_signal_connect_object (priv->channel, "group-members-changed",
+                       G_CALLBACK (tp_chat_group_members_changed_cb), chat, 0);
        } else {
-               EmpathyDispatcher *dispatcher = empathy_dispatcher_dup_singleton ();
-               GList *list, *ptr;
+               TpCapabilities *caps;
+               GPtrArray *classes;
+               guint i;
 
                /* 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_connection_get_self_handle (priv->connection);
+               empathy_tp_contact_factory_get_from_handle (priv->connection,
                        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,
+               empathy_tp_contact_factory_get_from_handle (priv->connection,
                        handle, tp_chat_got_remote_contact_cb,
                        NULL, NULL, chat);
 
-               list = empathy_dispatcher_find_requestable_channel_classes (
-                       dispatcher, connection,
-                       tp_channel_get_channel_type (priv->channel),
-                       TP_UNKNOWN_HANDLE_TYPE, NULL);
+               caps = tp_connection_get_capabilities (priv->connection);
+               g_assert (caps != NULL);
+
+               classes = tp_capabilities_get_channel_classes (caps);
 
-               for (ptr = list; ptr; ptr = ptr->next) {
-                       GValueArray *array = ptr->data;
+               for (i = 0; i < classes->len; i++) {
+                       GValueArray *array = g_ptr_array_index (classes, i);
                        const char **oprops = g_value_get_boxed (
                                g_value_array_get_nth (array, 1));
 
-                       if (tp_strv_contains (oprops, EMP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels")) {
+                       if (tp_strv_contains (oprops, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS)) {
                                priv->can_upgrade_to_muc = TRUE;
                                break;
                        }
                }
-
-               g_list_free (list);
-               g_object_unref (dispatcher);
        }
 
        if (tp_proxy_has_interface_by_id (priv->channel,
@@ -1356,6 +1375,9 @@ tp_chat_get_property (GObject    *object,
        EmpathyTpChatPriv *priv = GET_PRIV (object);
 
        switch (param_id) {
+       case PROP_ACCOUNT:
+               g_value_set_object (value, priv->account);
+               break;
        case PROP_CHANNEL:
                g_value_set_object (value, priv->channel);
                break;
@@ -1383,6 +1405,9 @@ tp_chat_set_property (GObject      *object,
        EmpathyTpChatPriv *priv = GET_PRIV (object);
 
        switch (param_id) {
+       case PROP_ACCOUNT:
+               priv->account = g_value_dup_object (value);
+               break;
        case PROP_CHANNEL:
                priv->channel = g_value_dup_object (value);
                break;
@@ -1403,6 +1428,16 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
        object_class->get_property = tp_chat_get_property;
        object_class->set_property = tp_chat_set_property;
 
+       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));
+
        g_object_class_install_property (object_class,
                                         PROP_CHANNEL,
                                         g_param_spec_object ("channel",
@@ -1497,7 +1532,6 @@ empathy_tp_chat_init (EmpathyTpChat *chat)
                EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv);
 
        chat->priv = priv;
-       priv->contact_monitor = NULL;
        priv->messages_queue = g_queue_new ();
        priv->pending_messages_queue = g_queue_new ();
 }
@@ -1508,13 +1542,17 @@ tp_chat_iface_init (EmpathyContactListIface *iface)
        iface->add         = tp_chat_add;
        iface->remove      = tp_chat_remove;
        iface->get_members = tp_chat_get_members;
-       iface->get_monitor = tp_chat_get_monitor;
 }
 
 EmpathyTpChat *
-empathy_tp_chat_new (TpChannel *channel)
+empathy_tp_chat_new (TpAccount *account,
+                    TpChannel *channel)
 {
+       g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
+       g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
+
        return g_object_new (EMPATHY_TYPE_TP_CHAT,
+                            "account", account,
                             "channel", channel,
                             NULL);
 }
@@ -1569,6 +1607,16 @@ empathy_tp_chat_get_channel (EmpathyTpChat *chat)
        return priv->channel;
 }
 
+TpAccount *
+empathy_tp_chat_get_account (EmpathyTpChat *chat)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+       g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
+
+       return priv->account;
+}
+
 TpConnection *
 empathy_tp_chat_get_connection (EmpathyTpChat *chat)
 {
@@ -1578,7 +1626,6 @@ empathy_tp_chat_get_connection (EmpathyTpChat *chat)
 
        return tp_channel_borrow_connection (priv->channel);
 }
-
 gboolean
 empathy_tp_chat_is_ready (EmpathyTpChat *chat)
 {
@@ -1862,3 +1909,53 @@ empathy_tp_chat_leave (EmpathyTpChat *self)
 
        g_array_free (array, TRUE);
 }
+
+static void
+add_members_cb (TpChannel *proxy,
+               const GError *error,
+               gpointer user_data,
+               GObject *weak_object)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (weak_object);
+
+       if (error != NULL) {
+               DEBUG ("Failed to join chat (%s): %s",
+                       tp_channel_get_identifier (priv->channel), error->message);
+       }
+}
+
+void
+empathy_tp_chat_join (EmpathyTpChat *self)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
+       TpHandle self_handle;
+       GArray *members;
+
+       self_handle = tp_channel_group_get_self_handle (priv->channel);
+
+       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 (priv->channel, -1, members,
+               "", add_members_cb, NULL, NULL, G_OBJECT (self));
+
+       g_array_free (members, TRUE);
+}
+
+gboolean
+empathy_tp_chat_is_invited (EmpathyTpChat *self,
+                           TpHandle *inviter)
+{
+       EmpathyTpChatPriv *priv = GET_PRIV (self);
+       TpHandle self_handle;
+
+       if (!tp_proxy_has_interface (priv->channel, TP_IFACE_CHANNEL_INTERFACE_GROUP))
+               return FALSE;
+
+       self_handle = tp_channel_group_get_self_handle (priv->channel);
+       if (self_handle == 0)
+               return FALSE;
+
+       return tp_channel_group_get_local_pending_info (priv->channel, self_handle,
+               inviter, NULL, NULL);
+}