X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-chat.c;h=b9b731384f5fa7be91dd3e6d8fec7e18feac003d;hp=19a367e9e4697318e0902e48d0d8c9cfce828ba2;hb=20075065ac090531ca887c900bf8b0cb367a0336;hpb=349f9e961602f67960ff6a0ad295a1e40c46a747 diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c index 19a367e9..b9b73138 100644 --- a/libempathy/empathy-tp-chat.c +++ b/libempathy/empathy-tp-chat.c @@ -28,8 +28,7 @@ #include #include "empathy-tp-chat.h" -#include "empathy-tp-group.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" @@ -42,14 +41,12 @@ #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; @@ -58,7 +55,6 @@ typedef struct { gboolean had_properties_list; GPtrArray *properties; gboolean ready; - guint members_count; } EmpathyTpChatPriv; typedef struct { @@ -92,6 +88,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, @@ -101,12 +99,11 @@ tp_chat_invalidated_cb (TpProxy *proxy, { 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); + g_object_unref (priv->channel); + priv->channel = NULL; } static void @@ -120,121 +117,23 @@ tp_chat_async_cb (TpChannel *proxy, } } -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); - } -} - -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 @@ -243,13 +142,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 * @@ -260,8 +163,9 @@ 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)); @@ -286,119 +190,64 @@ 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, + GList *contacts, + gpointer message, + GObject *chat) +{ + empathy_message_set_sender (message, contacts->data); + 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, + guint id, + guint type, + guint timestamp, + guint from_handle, + const gchar *message_body) { - 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); + g_queue_push_tail (priv->messages_queue, message); - 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; + if (from_handle == 0) { + empathy_message_set_sender (message, priv->user); + tp_chat_emit_queued_messages (chat); + } else { + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &from_handle, + tp_chat_got_sender_cb, + message, NULL, G_OBJECT (chat)); } - - 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); } static void @@ -410,10 +259,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; @@ -424,15 +273,26 @@ tp_chat_received_cb (TpChannel *channel, 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, + message_id, + message_type, + timestamp, + from_handle, + message_body); } static void @@ -441,25 +301,22 @@ 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, + 0, + message_type, + timestamp, + 0, + message_body); } static void @@ -471,7 +328,6 @@ tp_chat_send_error_cb (TpChannel *channel, gpointer user_data, GObject *chat) { - EmpathyMessage *message; EmpathyTpChatPriv *priv = GET_PRIV (chat); if (priv->channel == NULL) @@ -479,15 +335,12 @@ tp_chat_send_error_cb (TpChannel *channel, DEBUG ("Message sent error: %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); + tp_chat_build_message (EMPATHY_TP_CHAT (chat), + 0, + message_type, + timestamp, + 0, + message_body); } static void @@ -505,28 +358,40 @@ tp_chat_send_cb (TpChannel *proxy, } } +typedef struct { + EmpathyTpChat *chat; + TpChannelChatState state; +} StateChangedData; + +static void +tp_chat_state_changed_got_contact_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyContact *contact = contacts->data; + TpChannelChatState state; + + state = GPOINTER_TO_UINT (user_data); + DEBUG ("Chat state changed for %s (%d): %d", + empathy_contact_get_name (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, - guint handle, - guint state, + TpHandle handle, + TpChannelChatState state, gpointer user_data, GObject *chat) { EmpathyTpChatPriv *priv = GET_PRIV (chat); - EmpathyContact *contact; - if (priv->channel == NULL) - return; - - contact = empathy_contact_factory_get_from_handle (priv->factory, - priv->account, - handle); - - DEBUG ("Chat state changed for %s (%d): %d", - empathy_contact_get_name (contact), handle, state); - - g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state); - g_object_unref (contact); + empathy_tp_contact_factory_get_from_handles (priv->factory, 1, &handle, + tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state), + NULL, chat); } static void @@ -534,10 +399,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; @@ -550,7 +417,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; @@ -570,15 +436,29 @@ 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, + message_id, + message_type, + timestamp, + from_handle, + message_body); + } + + if (empty_non_text_content_ids != NULL) { + acknowledge_messages (chat, empty_non_text_content_ids); + g_array_free (empty_non_text_content_ids, TRUE); } } @@ -738,8 +618,6 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat, TpChatProperty *property; guint i; - g_return_if_fail (priv->ready); - for (i = 0; i < priv->properties->len; i++) { property = g_ptr_array_index (priv->properties, i); if (!tp_strdiff (property->name, name)) { @@ -781,78 +659,90 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat, } static void -tp_chat_channel_ready_cb (EmpathyTpChat *chat) +tp_chat_dispose (GObject *object) { - EmpathyTpChatPriv *priv = GET_PRIV (chat); - TpConnection *connection; - guint handle, handle_type; + EmpathyTpChat *self = EMPATHY_TP_CHAT (object); + EmpathyTpChatPriv *priv = GET_PRIV (self); - if (priv->channel == NULL) + if (priv->dispose_has_run) return; - DEBUG ("Channel ready"); - - g_object_get (priv->channel, - "connection", &connection, - "handle", &handle, - "handle_type", &handle_type, - NULL); + priv->dispose_has_run = TRUE; - 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); + 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 (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->remote_contact != NULL) + g_object_unref (priv->remote_contact); + priv->remote_contact = 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 (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++) { + TpChatProperty *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 (TpChatProperty, property); + } + g_ptr_array_free (priv->properties, TRUE); } - - 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); + + 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 || priv->user == NULL || + (priv->members == NULL && priv->remote_contact == NULL)) { + return; } + DEBUG ("Ready!"); + priv->listing_pending_messages = TRUE; tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1, FALSE, @@ -880,103 +770,183 @@ tp_chat_channel_ready_cb (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 -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) - g_object_unref (priv->remote_contact); + /* If we didn't find yet a remote contact, keep this one */ + contact = l->data; + } - priv->remote_contact = NULL; + if (priv->remote_contact == contact) { + return; + } - if (priv->group != NULL) - g_object_unref (priv->group); - priv->group = NULL; + DEBUG ("Changing remote contact from %p to %p", + priv->remote_contact, contact); - if (priv->factory != NULL) - g_object_unref (priv->factory); - priv->factory = NULL; + if (priv->remote_contact) { + g_object_unref (priv->remote_contact); + } - if (priv->user != NULL); - g_object_unref (priv->user); - priv->user = NULL; + priv->remote_contact = contact ? g_object_ref (contact) : NULL; + g_object_notify (G_OBJECT (chat), "remote-contact"); +} + +static void +tp_chat_got_added_contacts_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + GList *l; + const TpIntSet *members; + TpHandle handle; + EmpathyContact *contact; + + members = tp_channel_group_get_members (priv->channel); + for (l = contacts; l; l = l->next) { + contact = l->data; + 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, FALSE); + } + } - 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)); +} - if (priv->contact_monitor) - g_object_unref (priv->contact_monitor); - priv->contact_monitor = NULL; +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; + TpHandle handle; + guint i; + GList *l; - if (!g_queue_is_empty (priv->messages_queue)) { - EmpathyMessage *message; - EmpathyContact *contact; + /* Remove contacts that are not members anymore */ + for (i = 0; i < removed->len; i++) { + for (l = priv->members; l; l = l->next) { + contact = l->data; + handle = empathy_contact_get_handle (contact); + if (handle == g_array_index (removed, TpHandle, i)) { + priv->members = g_list_delete_link (priv->members, l); + g_signal_emit_by_name (chat, "members-changed", + contact, actor, reason, + message, FALSE); + g_object_unref (contact); + break; + } + } + } - 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); + /* 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_list_foreach (priv->messages_queue->head, - (GFunc) g_object_unref, NULL); + tp_chat_update_remote_contact (chat); +} - g_list_foreach (priv->pending_messages_queue->head, - (GFunc) g_object_unref, NULL); +static void +tp_chat_got_remote_contact_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); - if (G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose) - G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose (object); + priv->remote_contact = g_object_ref (contacts->data); + g_object_notify (chat, "remote-contact"); + + tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat)); } static void -tp_chat_finalize (GObject *object) +tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory, + GList *contacts, + gpointer user_data, + GObject *chat) { - EmpathyTpChatPriv *priv = GET_PRIV (object); - guint i; + EmpathyTpChatPriv *priv = GET_PRIV (chat); - DEBUG ("Finalize: %p", object); + priv->user = g_object_ref (contacts->data); + empathy_contact_set_is_user (priv->user, TRUE); + tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat)); +} - if (priv->properties) { - for (i = 0; i < priv->properties->len; i++) { - TpChatProperty *property; +static void +tp_chat_get_self_handle_cb (TpConnection *connection, + TpHandle self_handle, + const GError *error, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); - 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); - } - g_ptr_array_free (priv->properties, TRUE); + if (error) { + DEBUG ("Error: %s", error->message); + tp_cli_channel_call_close (priv->channel, -1, + NULL, NULL, NULL, NULL); + return; } - - g_free (priv->id); - g_queue_free (priv->messages_queue); - g_queue_free (priv->pending_messages_queue); - - G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object); + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &self_handle, + tp_chat_got_self_contact_cb, + NULL, NULL, chat); } static GObject * @@ -986,26 +956,66 @@ 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_handles (priv->factory, + 1, &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_swapped (priv->channel, "group-members-changed", + G_CALLBACK (tp_chat_group_members_changed_cb), chat); } else { - g_signal_connect_swapped (priv->channel, "notify::channel-ready", - G_CALLBACK (tp_chat_channel_ready_cb), - chat); + /* Get the self contact from the connection's self handle */ + tp_cli_connection_call_get_self_handle (connection, -1, + tp_chat_get_self_handle_cb, NULL, NULL, chat); + + /* Get the remote contact */ + handle = tp_channel_get_handle (priv->channel, NULL); + empathy_tp_contact_factory_get_from_handles (priv->factory, + 1, &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); } return chat; @@ -1080,6 +1090,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", @@ -1195,9 +1206,8 @@ empathy_tp_chat_get_id (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->id; + return tp_channel_get_identifier (priv->channel); } EmpathyContact * @@ -1206,28 +1216,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 @@ -1240,16 +1251,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) @@ -1299,6 +1300,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; } @@ -1319,6 +1323,9 @@ empathy_tp_chat_acknowledge_message (EmpathyTpChat *chat, GList *m; guint id; + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + g_return_if_fail (priv->ready); + if (empathy_message_get_sender (message) == priv->user) goto out; @@ -1346,6 +1353,9 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat, guint length; GArray *message_ids; + g_return_if_fail (EMPATHY_IS_TP_CHAT (chat)); + g_return_if_fail (priv->ready); + length = g_list_length ((GList *)messages); if (length == 0) @@ -1366,6 +1376,7 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat, guint id = empathy_message_get_id (message); g_array_append_val (message_ids, id); } + g_object_unref (message); } if (message_ids->len > 0) @@ -1374,3 +1385,4 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat, g_array_free (message_ids, TRUE); g_list_free (msgs); } +