1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007-2008 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Authors: Xavier Claessens <xclaesse@gmail.com>
26 #include <telepathy-glib/telepathy-glib.h>
28 #include <extensions/extensions.h>
30 #include "empathy-tp-chat.h"
31 #include "empathy-tp-contact-factory.h"
32 #include "empathy-contact-list.h"
33 #include "empathy-dispatcher.h"
34 #include "empathy-marshal.h"
35 #include "empathy-time.h"
36 #include "empathy-utils.h"
38 #define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CHAT
39 #include "empathy-debug.h"
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpChat)
43 gboolean dispose_has_run
;
45 TpConnection
*connection
;
47 EmpathyContact
*remote_contact
;
50 gboolean listing_pending_messages
;
51 /* Queue of messages not signalled yet */
52 GQueue
*messages_queue
;
53 /* Queue of messages signalled but not acked yet */
54 GQueue
*pending_messages_queue
;
55 gboolean had_properties_list
;
56 GPtrArray
*properties
;
57 TpChannelPasswordFlags password_flags
;
58 /* TRUE if we fetched the password flag of the channel or if it's not needed
59 * (channel doesn't implement the Password interface) */
60 gboolean got_password_flags
;
62 gboolean can_upgrade_to_muc
;
65 static void tp_chat_iface_init (EmpathyContactListIface
*iface
);
85 static guint signals
[LAST_SIGNAL
];
87 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat
, empathy_tp_chat
, G_TYPE_OBJECT
,
88 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST
,
91 static void acknowledge_messages (EmpathyTpChat
*chat
, GArray
*ids
);
94 tp_chat_invalidated_cb (TpProxy
*proxy
,
100 DEBUG ("Channel invalidated: %s", message
);
101 g_signal_emit (chat
, signals
[DESTROY
], 0);
105 tp_chat_async_cb (TpChannel
*proxy
,
108 GObject
*weak_object
)
111 DEBUG ("Error %s: %s", (gchar
*) user_data
, error
->message
);
116 create_conference_cb (GObject
*source
,
117 GAsyncResult
*result
,
120 GError
*error
= NULL
;
122 if (!tp_account_channel_request_create_channel_finish (
123 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, &error
)) {
124 DEBUG ("Failed to create conference channel: %s", error
->message
);
125 g_error_free (error
);
130 tp_chat_add (EmpathyContactList
*list
,
131 EmpathyContact
*contact
,
132 const gchar
*message
)
134 EmpathyTpChatPriv
*priv
= GET_PRIV (list
);
136 if (tp_proxy_has_interface_by_id (priv
->channel
,
137 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
139 GArray handles
= {(gchar
*) &handle
, 1};
141 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
142 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
144 handle
= empathy_contact_get_handle (contact
);
145 tp_cli_channel_interface_group_call_add_members (priv
->channel
,
146 -1, &handles
, NULL
, NULL
, NULL
, NULL
, NULL
);
147 } else if (priv
->can_upgrade_to_muc
) {
148 TpAccountChannelRequest
*req
;
150 const char *object_path
;
151 GPtrArray channels
= { (gpointer
*) &object_path
, 1 };
152 const char *invitees
[2] = { NULL
, };
154 invitees
[0] = empathy_contact_get_id (contact
);
155 object_path
= tp_proxy_get_object_path (priv
->channel
);
158 TP_IFACE_CHANNEL
".ChannelType", G_TYPE_STRING
,
159 TP_IFACE_CHANNEL_TYPE_TEXT
,
160 TP_IFACE_CHANNEL
".TargetHandleType", G_TYPE_UINT
,
162 EMP_IFACE_CHANNEL_INTERFACE_CONFERENCE
".InitialChannels",
163 TP_ARRAY_TYPE_OBJECT_PATH_LIST
, &channels
,
164 EMP_IFACE_CHANNEL_INTERFACE_CONFERENCE
".InitialInviteeIDs",
165 G_TYPE_STRV
, invitees
,
166 /* FIXME: InvitationMessage ? */
169 req
= tp_account_channel_request_new (priv
->account
, props
,
170 TP_USER_ACTION_TIME_NOT_USER_ACTION
);
172 /* Although this is a MUC, it's anonymous, so CreateChannel is
174 tp_account_channel_request_create_channel_async (req
, NULL
, NULL
,
175 create_conference_cb
, NULL
);
177 g_object_unref (req
);
178 g_hash_table_unref (props
);
180 g_warning ("Cannot add to this channel");
185 tp_chat_remove (EmpathyContactList
*list
,
186 EmpathyContact
*contact
,
187 const gchar
*message
)
189 EmpathyTpChatPriv
*priv
= GET_PRIV (list
);
191 GArray handles
= {(gchar
*) &handle
, 1};
193 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
194 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
196 handle
= empathy_contact_get_handle (contact
);
197 tp_cli_channel_interface_group_call_remove_members (priv
->channel
, -1,
204 tp_chat_get_members (EmpathyContactList
*list
)
206 EmpathyTpChatPriv
*priv
= GET_PRIV (list
);
207 GList
*members
= NULL
;
209 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list
), NULL
);
212 members
= g_list_copy (priv
->members
);
213 g_list_foreach (members
, (GFunc
) g_object_ref
, NULL
);
215 members
= g_list_prepend (members
, g_object_ref (priv
->user
));
216 if (priv
->remote_contact
!= NULL
)
217 members
= g_list_prepend (members
, g_object_ref (priv
->remote_contact
));
224 check_ready (EmpathyTpChat
*chat
)
226 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
231 if (g_queue_get_length (priv
->messages_queue
) > 0)
237 g_object_notify (G_OBJECT (chat
), "ready");
241 tp_chat_emit_queued_messages (EmpathyTpChat
*chat
)
243 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
244 EmpathyMessage
*message
;
246 /* Check if we can now emit some queued messages */
247 while ((message
= g_queue_peek_head (priv
->messages_queue
)) != NULL
) {
248 if (empathy_message_get_sender (message
) == NULL
) {
252 DEBUG ("Queued message ready");
253 g_queue_pop_head (priv
->messages_queue
);
254 g_queue_push_tail (priv
->pending_messages_queue
, message
);
255 g_signal_emit (chat
, signals
[MESSAGE_RECEIVED
], 0, message
);
262 tp_chat_got_sender_cb (TpConnection
*connection
,
263 EmpathyContact
*contact
,
268 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
271 DEBUG ("Error: %s", error
->message
);
272 /* Do not block the message queue, just drop this message */
273 g_queue_remove (priv
->messages_queue
, message
);
275 empathy_message_set_sender (message
, contact
);
278 tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (chat
));
282 tp_chat_build_message (EmpathyTpChat
*chat
,
288 const gchar
*message_body
,
289 TpChannelTextMessageFlags flags
)
291 EmpathyTpChatPriv
*priv
;
292 EmpathyMessage
*message
;
294 priv
= GET_PRIV (chat
);
296 message
= empathy_message_new (message_body
);
297 empathy_message_set_tptype (message
, type
);
298 empathy_message_set_receiver (message
, priv
->user
);
299 empathy_message_set_timestamp (message
, timestamp
);
300 empathy_message_set_id (message
, id
);
301 empathy_message_set_incoming (message
, incoming
);
302 empathy_message_set_flags (message
, flags
);
304 if (flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK
)
305 empathy_message_set_is_backlog (message
, TRUE
);
307 g_queue_push_tail (priv
->messages_queue
, message
);
309 if (from_handle
== 0) {
310 empathy_message_set_sender (message
, priv
->user
);
311 tp_chat_emit_queued_messages (chat
);
313 empathy_tp_contact_factory_get_from_handle (priv
->connection
,
315 tp_chat_got_sender_cb
,
316 message
, NULL
, G_OBJECT (chat
));
321 tp_chat_received_cb (TpChannel
*channel
,
327 const gchar
*message_body
,
331 EmpathyTpChat
*chat
= EMPATHY_TP_CHAT (chat_
);
332 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
334 if (priv
->channel
== NULL
)
337 if (priv
->listing_pending_messages
) {
341 DEBUG ("Message received: %s", message_body
);
343 if (message_flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT
&&
344 !tp_strdiff (message_body
, "")) {
347 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
349 ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), 1);
350 g_array_append_val (ids
, message_id
);
351 acknowledge_messages (chat
, ids
);
352 g_array_free (ids
, TRUE
);
357 tp_chat_build_message (chat
,
368 tp_chat_sent_cb (TpChannel
*channel
,
371 const gchar
*message_body
,
375 EmpathyTpChat
*chat
= EMPATHY_TP_CHAT (chat_
);
376 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
378 if (priv
->channel
== NULL
)
381 DEBUG ("Message sent: %s", message_body
);
383 tp_chat_build_message (chat
,
394 tp_chat_send_error_cb (TpChannel
*channel
,
398 const gchar
*message_body
,
402 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
404 if (priv
->channel
== NULL
)
407 DEBUG ("Error sending '%s' (%d)", message_body
, error_code
);
409 g_signal_emit (chat
, signals
[SEND_ERROR
], 0, message_body
, error_code
);
413 tp_chat_send_cb (TpChannel
*proxy
,
418 EmpathyMessage
*message
= EMPATHY_MESSAGE (user_data
);
421 DEBUG ("Error: %s", error
->message
);
422 g_signal_emit (chat
, signals
[SEND_ERROR
], 0,
423 empathy_message_get_body (message
),
424 TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
);
430 TpChannelChatState state
;
434 tp_chat_state_changed_got_contact_cb (TpConnection
*connection
,
435 EmpathyContact
*contact
,
440 TpChannelChatState state
;
443 DEBUG ("Error: %s", error
->message
);
447 state
= GPOINTER_TO_UINT (user_data
);
448 DEBUG ("Chat state changed for %s (%d): %d",
449 empathy_contact_get_alias (contact
),
450 empathy_contact_get_handle (contact
), state
);
452 g_signal_emit (chat
, signals
[CHAT_STATE_CHANGED
], 0, contact
, state
);
456 tp_chat_state_changed_cb (TpChannel
*channel
,
458 TpChannelChatState state
,
462 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
464 empathy_tp_contact_factory_get_from_handle (priv
->connection
, handle
,
465 tp_chat_state_changed_got_contact_cb
, GUINT_TO_POINTER (state
),
470 tp_chat_list_pending_messages_cb (TpChannel
*channel
,
471 const GPtrArray
*messages_list
,
476 EmpathyTpChat
*chat
= EMPATHY_TP_CHAT (chat_
);
477 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
479 GArray
*empty_non_text_content_ids
= NULL
;
481 priv
->listing_pending_messages
= FALSE
;
483 if (priv
->channel
== NULL
)
487 DEBUG ("Error listing pending messages: %s", error
->message
);
491 for (i
= 0; i
< messages_list
->len
; i
++) {
492 GValueArray
*message_struct
;
493 const gchar
*message_body
;
500 message_struct
= g_ptr_array_index (messages_list
, i
);
502 message_id
= g_value_get_uint (g_value_array_get_nth (message_struct
, 0));
503 timestamp
= g_value_get_uint (g_value_array_get_nth (message_struct
, 1));
504 from_handle
= g_value_get_uint (g_value_array_get_nth (message_struct
, 2));
505 message_type
= g_value_get_uint (g_value_array_get_nth (message_struct
, 3));
506 message_flags
= g_value_get_uint (g_value_array_get_nth (message_struct
, 4));
507 message_body
= g_value_get_string (g_value_array_get_nth (message_struct
, 5));
509 DEBUG ("Message pending: %s", message_body
);
511 if (message_flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT
&&
512 !tp_strdiff (message_body
, "")) {
513 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
515 if (empty_non_text_content_ids
== NULL
) {
516 empty_non_text_content_ids
= g_array_new (FALSE
, FALSE
, sizeof (guint
));
519 g_array_append_val (empty_non_text_content_ids
, message_id
);
523 tp_chat_build_message (chat
,
533 if (empty_non_text_content_ids
!= NULL
) {
534 acknowledge_messages (chat
, empty_non_text_content_ids
);
535 g_array_free (empty_non_text_content_ids
, TRUE
);
542 tp_chat_property_flags_changed_cb (TpProxy
*proxy
,
543 const GPtrArray
*properties
,
547 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
550 if (priv
->channel
== NULL
)
553 if (!priv
->had_properties_list
|| !properties
) {
557 for (i
= 0; i
< properties
->len
; i
++) {
558 GValueArray
*prop_struct
;
559 EmpathyTpChatProperty
*property
;
563 prop_struct
= g_ptr_array_index (properties
, i
);
564 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
565 flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 1));
567 for (j
= 0; j
< priv
->properties
->len
; j
++) {
568 property
= g_ptr_array_index (priv
->properties
, j
);
569 if (property
->id
== id
) {
570 property
->flags
= flags
;
571 DEBUG ("property %s flags changed: %d",
572 property
->name
, property
->flags
);
580 tp_chat_properties_changed_cb (TpProxy
*proxy
,
581 const GPtrArray
*properties
,
585 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
588 if (priv
->channel
== NULL
)
591 if (!priv
->had_properties_list
|| !properties
) {
595 for (i
= 0; i
< properties
->len
; i
++) {
596 GValueArray
*prop_struct
;
597 EmpathyTpChatProperty
*property
;
601 prop_struct
= g_ptr_array_index (properties
, i
);
602 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
603 src_value
= g_value_get_boxed (g_value_array_get_nth (prop_struct
, 1));
605 for (j
= 0; j
< priv
->properties
->len
; j
++) {
606 property
= g_ptr_array_index (priv
->properties
, j
);
607 if (property
->id
== id
) {
608 if (property
->value
) {
609 g_value_copy (src_value
, property
->value
);
611 property
->value
= tp_g_value_slice_dup (src_value
);
614 DEBUG ("property %s changed", property
->name
);
615 g_signal_emit (chat
, signals
[PROPERTY_CHANGED
], 0,
616 property
->name
, property
->value
);
624 tp_chat_get_properties_cb (TpProxy
*proxy
,
625 const GPtrArray
*properties
,
631 DEBUG ("Error getting properties: %s", error
->message
);
635 tp_chat_properties_changed_cb (proxy
, properties
, user_data
, chat
);
639 tp_chat_list_properties_cb (TpProxy
*proxy
,
640 const GPtrArray
*properties
,
645 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
649 if (priv
->channel
== NULL
)
652 priv
->had_properties_list
= TRUE
;
655 DEBUG ("Error listing properties: %s", error
->message
);
659 ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), properties
->len
);
660 priv
->properties
= g_ptr_array_sized_new (properties
->len
);
661 for (i
= 0; i
< properties
->len
; i
++) {
662 GValueArray
*prop_struct
;
663 EmpathyTpChatProperty
*property
;
665 prop_struct
= g_ptr_array_index (properties
, i
);
666 property
= g_slice_new0 (EmpathyTpChatProperty
);
667 property
->id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
668 property
->name
= g_value_dup_string (g_value_array_get_nth (prop_struct
, 1));
669 property
->flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 3));
671 DEBUG ("Adding property name=%s id=%d flags=%d",
672 property
->name
, property
->id
, property
->flags
);
673 g_ptr_array_add (priv
->properties
, property
);
674 if (property
->flags
& TP_PROPERTY_FLAG_READ
) {
675 g_array_append_val (ids
, property
->id
);
679 tp_cli_properties_interface_call_get_properties (proxy
, -1,
681 tp_chat_get_properties_cb
,
685 g_array_free (ids
, TRUE
);
689 empathy_tp_chat_set_property (EmpathyTpChat
*chat
,
693 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
694 EmpathyTpChatProperty
*property
;
697 if (!priv
->had_properties_list
) {
701 for (i
= 0; i
< priv
->properties
->len
; i
++) {
702 property
= g_ptr_array_index (priv
->properties
, i
);
703 if (!tp_strdiff (property
->name
, name
)) {
704 GPtrArray
*properties
;
707 GValue dest_value
= {0, };
709 if (!(property
->flags
& TP_PROPERTY_FLAG_WRITE
)) {
713 g_value_init (&id
, G_TYPE_UINT
);
714 g_value_init (&dest_value
, G_TYPE_VALUE
);
715 g_value_set_uint (&id
, property
->id
);
716 g_value_set_boxed (&dest_value
, value
);
718 prop
= g_value_array_new (2);
719 g_value_array_append (prop
, &id
);
720 g_value_array_append (prop
, &dest_value
);
722 properties
= g_ptr_array_sized_new (1);
723 g_ptr_array_add (properties
, prop
);
725 DEBUG ("Set property %s", name
);
726 tp_cli_properties_interface_call_set_properties (priv
->channel
, -1,
728 (tp_cli_properties_interface_callback_for_set_properties
)
730 "Seting property", NULL
,
733 g_ptr_array_free (properties
, TRUE
);
734 g_value_array_free (prop
);
741 EmpathyTpChatProperty
*
742 empathy_tp_chat_get_property (EmpathyTpChat
*chat
,
745 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
746 EmpathyTpChatProperty
*property
;
749 if (!priv
->had_properties_list
) {
753 for (i
= 0; i
< priv
->properties
->len
; i
++) {
754 property
= g_ptr_array_index (priv
->properties
, i
);
755 if (!tp_strdiff (property
->name
, name
)) {
764 empathy_tp_chat_get_properties (EmpathyTpChat
*chat
)
766 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
768 return priv
->properties
;
772 tp_chat_dispose (GObject
*object
)
774 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
775 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
777 if (priv
->dispose_has_run
)
780 priv
->dispose_has_run
= TRUE
;
782 tp_clear_object (&priv
->account
);
784 if (priv
->connection
!= NULL
)
785 g_object_unref (priv
->connection
);
786 priv
->connection
= NULL
;
788 if (priv
->channel
!= NULL
) {
789 g_signal_handlers_disconnect_by_func (priv
->channel
,
790 tp_chat_invalidated_cb
, self
);
791 g_object_unref (priv
->channel
);
793 priv
->channel
= NULL
;
795 if (priv
->remote_contact
!= NULL
)
796 g_object_unref (priv
->remote_contact
);
797 priv
->remote_contact
= NULL
;
799 if (priv
->user
!= NULL
)
800 g_object_unref (priv
->user
);
803 g_queue_foreach (priv
->messages_queue
, (GFunc
) g_object_unref
, NULL
);
804 g_queue_clear (priv
->messages_queue
);
806 g_queue_foreach (priv
->pending_messages_queue
,
807 (GFunc
) g_object_unref
, NULL
);
808 g_queue_clear (priv
->pending_messages_queue
);
810 if (G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose
)
811 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose (object
);
815 tp_chat_finalize (GObject
*object
)
817 EmpathyTpChatPriv
*priv
= GET_PRIV (object
);
820 DEBUG ("Finalize: %p", object
);
822 if (priv
->properties
) {
823 for (i
= 0; i
< priv
->properties
->len
; i
++) {
824 EmpathyTpChatProperty
*property
;
826 property
= g_ptr_array_index (priv
->properties
, i
);
827 g_free (property
->name
);
828 if (property
->value
) {
829 tp_g_value_slice_free (property
->value
);
831 g_slice_free (EmpathyTpChatProperty
, property
);
833 g_ptr_array_free (priv
->properties
, TRUE
);
836 g_queue_free (priv
->messages_queue
);
837 g_queue_free (priv
->pending_messages_queue
);
839 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->finalize (object
);
843 check_almost_ready (EmpathyTpChat
*chat
)
845 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
850 if (priv
->user
== NULL
)
853 if (!priv
->got_password_flags
)
856 /* We need either the members (room) or the remote contact (private chat).
857 * If the chat is protected by a password we can't get these information so
858 * consider the chat as ready so it can be presented to the user. */
859 if (!empathy_tp_chat_password_needed (chat
) && priv
->members
== NULL
&&
860 priv
->remote_contact
== NULL
)
863 tp_cli_channel_type_text_connect_to_received (priv
->channel
,
866 G_OBJECT (chat
), NULL
);
867 priv
->listing_pending_messages
= TRUE
;
869 /* TpChat will be ready once ListPendingMessages returned and all the messages
870 * have been added to the pending messages queue. */
871 tp_cli_channel_type_text_call_list_pending_messages (priv
->channel
, -1,
873 tp_chat_list_pending_messages_cb
,
877 tp_cli_channel_type_text_connect_to_sent (priv
->channel
,
880 G_OBJECT (chat
), NULL
);
881 tp_cli_channel_type_text_connect_to_send_error (priv
->channel
,
882 tp_chat_send_error_cb
,
884 G_OBJECT (chat
), NULL
);
885 tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv
->channel
,
886 tp_chat_state_changed_cb
,
888 G_OBJECT (chat
), NULL
);
892 tp_chat_update_remote_contact (EmpathyTpChat
*chat
)
894 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
895 EmpathyContact
*contact
= NULL
;
896 TpHandle self_handle
;
897 TpHandleType handle_type
;
900 /* If this is a named chatroom, never pretend it is a private chat */
901 tp_channel_get_handle (priv
->channel
, &handle_type
);
902 if (handle_type
== TP_HANDLE_TYPE_ROOM
) {
906 /* This is an MSN chat, but it's the new style where 1-1 chats don't
907 * have the group interface. If it has the conference interface, then
908 * it is indeed a MUC. */
909 if (tp_proxy_has_interface_by_id (priv
->channel
,
910 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE
)) {
914 /* This is an MSN-like chat where anyone can join the chat at anytime.
915 * If there is only one non-self contact member, we are in a private
916 * chat and we set the "remote-contact" property to that contact. If
917 * there are more, set the "remote-contact" property to NULL and the
918 * UI will display a contact list. */
919 self_handle
= tp_channel_group_get_self_handle (priv
->channel
);
920 for (l
= priv
->members
; l
; l
= l
->next
) {
921 /* Skip self contact if member */
922 if (empathy_contact_get_handle (l
->data
) == self_handle
) {
926 /* We have more than one remote contact, break */
927 if (contact
!= NULL
) {
932 /* If we didn't find yet a remote contact, keep this one */
936 if (priv
->remote_contact
== contact
) {
940 DEBUG ("Changing remote contact from %p to %p",
941 priv
->remote_contact
, contact
);
943 if (priv
->remote_contact
) {
944 g_object_unref (priv
->remote_contact
);
947 priv
->remote_contact
= contact
? g_object_ref (contact
) : NULL
;
948 g_object_notify (G_OBJECT (chat
), "remote-contact");
952 tp_chat_got_added_contacts_cb (TpConnection
*connection
,
954 EmpathyContact
* const * contacts
,
956 const TpHandle
*failed
,
961 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
963 const TpIntSet
*members
;
965 EmpathyContact
*contact
;
968 DEBUG ("Error: %s", error
->message
);
972 members
= tp_channel_group_get_members (priv
->channel
);
973 for (i
= 0; i
< n_contacts
; i
++) {
974 contact
= contacts
[i
];
975 handle
= empathy_contact_get_handle (contact
);
977 /* Make sure the contact is still member */
978 if (tp_intset_is_member (members
, handle
)) {
979 priv
->members
= g_list_prepend (priv
->members
,
980 g_object_ref (contact
));
981 g_signal_emit_by_name (chat
, "members-changed",
982 contact
, NULL
, 0, NULL
, TRUE
);
986 tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat
));
987 check_almost_ready (EMPATHY_TP_CHAT (chat
));
990 static EmpathyContact
*
991 chat_lookup_contact (EmpathyTpChat
*chat
,
995 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
998 for (l
= priv
->members
; l
; l
= l
->next
) {
999 EmpathyContact
*c
= l
->data
;
1001 if (empathy_contact_get_handle (c
) != handle
) {
1006 /* Caller takes the reference. */
1007 priv
->members
= g_list_delete_link (priv
->members
, l
);
1020 TpHandle old_handle
;
1023 } ContactRenameData
;
1025 static ContactRenameData
*
1026 contact_rename_data_new (TpHandle handle
,
1028 const gchar
* message
)
1030 ContactRenameData
*data
= g_new (ContactRenameData
, 1);
1031 data
->old_handle
= handle
;
1032 data
->reason
= reason
;
1033 data
->message
= g_strdup (message
);
1039 contact_rename_data_free (ContactRenameData
* data
)
1041 g_free (data
->message
);
1046 tp_chat_got_renamed_contacts_cb (TpConnection
*connection
,
1048 EmpathyContact
* const * contacts
,
1050 const TpHandle
*failed
,
1051 const GError
*error
,
1055 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1056 const TpIntSet
*members
;
1058 EmpathyContact
*old
= NULL
, *new = NULL
;
1059 ContactRenameData
*rename_data
= (ContactRenameData
*) user_data
;
1062 DEBUG ("Error: %s", error
->message
);
1066 /* renamed members can only be delivered one at a time */
1067 g_warn_if_fail (n_contacts
== 1);
1071 members
= tp_channel_group_get_members (priv
->channel
);
1072 handle
= empathy_contact_get_handle (new);
1074 old
= chat_lookup_contact (EMPATHY_TP_CHAT (chat
),
1075 rename_data
->old_handle
, TRUE
);
1077 /* Make sure the contact is still member */
1078 if (tp_intset_is_member (members
, handle
)) {
1079 priv
->members
= g_list_prepend (priv
->members
,
1080 g_object_ref (new));
1083 g_signal_emit_by_name (chat
, "member-renamed",
1084 old
, new, rename_data
->reason
,
1085 rename_data
->message
);
1086 g_object_unref (old
);
1090 if (priv
->user
== old
) {
1091 /* We change our nick */
1092 g_object_unref (priv
->user
);
1093 priv
->user
= g_object_ref (new);
1096 tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat
));
1097 check_almost_ready (EMPATHY_TP_CHAT (chat
));
1102 tp_chat_group_members_changed_cb (TpChannel
*self
,
1106 GArray
*local_pending
,
1107 GArray
*remote_pending
,
1110 EmpathyTpChat
*chat
)
1112 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1113 EmpathyContact
*contact
;
1114 EmpathyContact
*actor_contact
= NULL
;
1116 ContactRenameData
*rename_data
;
1117 TpHandle old_handle
;
1119 /* Contact renamed */
1120 if (reason
== TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED
) {
1121 /* there can only be a single 'added' and a single 'removed' handle */
1122 g_warn_if_fail (removed
->len
== 1);
1123 g_warn_if_fail (added
->len
== 1);
1125 old_handle
= g_array_index (removed
, guint
, 0);
1127 rename_data
= contact_rename_data_new (old_handle
, reason
, message
);
1128 empathy_tp_contact_factory_get_from_handles (priv
->connection
,
1129 added
->len
, (TpHandle
*) added
->data
,
1130 tp_chat_got_renamed_contacts_cb
,
1131 rename_data
, (GDestroyNotify
) contact_rename_data_free
,
1137 actor_contact
= chat_lookup_contact (chat
, actor
, FALSE
);
1138 if (actor_contact
== NULL
) {
1139 /* FIXME: handle this a tad more gracefully: perhaps
1140 * the actor was a server op. We could use the
1141 * contact-ids detail of MembersChangedDetailed.
1143 DEBUG ("actor %u not a channel member", actor
);
1147 /* Remove contacts that are not members anymore */
1148 for (i
= 0; i
< removed
->len
; i
++) {
1149 contact
= chat_lookup_contact (chat
,
1150 g_array_index (removed
, TpHandle
, i
), TRUE
);
1152 if (contact
!= NULL
) {
1153 g_signal_emit_by_name (chat
, "members-changed", contact
,
1154 actor_contact
, reason
, message
,
1156 g_object_unref (contact
);
1160 /* Request added contacts */
1161 if (added
->len
> 0) {
1162 empathy_tp_contact_factory_get_from_handles (priv
->connection
,
1163 added
->len
, (TpHandle
*) added
->data
,
1164 tp_chat_got_added_contacts_cb
, NULL
, NULL
,
1168 tp_chat_update_remote_contact (chat
);
1170 if (actor_contact
!= NULL
) {
1171 g_object_unref (actor_contact
);
1176 tp_chat_got_remote_contact_cb (TpConnection
*connection
,
1177 EmpathyContact
*contact
,
1178 const GError
*error
,
1182 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1185 DEBUG ("Error: %s", error
->message
);
1186 empathy_tp_chat_leave (EMPATHY_TP_CHAT (chat
));
1190 priv
->remote_contact
= g_object_ref (contact
);
1191 g_object_notify (chat
, "remote-contact");
1193 check_almost_ready (EMPATHY_TP_CHAT (chat
));
1197 tp_chat_got_self_contact_cb (TpConnection
*connection
,
1198 EmpathyContact
*contact
,
1199 const GError
*error
,
1203 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1206 DEBUG ("Error: %s", error
->message
);
1207 empathy_tp_chat_leave (EMPATHY_TP_CHAT (chat
));
1211 priv
->user
= g_object_ref (contact
);
1212 empathy_contact_set_is_user (priv
->user
, TRUE
);
1213 check_almost_ready (EMPATHY_TP_CHAT (chat
));
1217 password_flags_changed_cb (TpChannel
*channel
,
1221 GObject
*weak_object
)
1223 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (weak_object
);
1224 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1225 gboolean was_needed
, needed
;
1227 was_needed
= empathy_tp_chat_password_needed (self
);
1229 priv
->password_flags
|= added
;
1230 priv
->password_flags
^= removed
;
1232 needed
= empathy_tp_chat_password_needed (self
);
1234 if (was_needed
!= needed
)
1235 g_object_notify (G_OBJECT (self
), "password-needed");
1239 got_password_flags_cb (TpChannel
*proxy
,
1240 guint password_flags
,
1241 const GError
*error
,
1243 GObject
*weak_object
)
1245 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (weak_object
);
1246 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1248 priv
->got_password_flags
= TRUE
;
1249 priv
->password_flags
= password_flags
;
1251 check_almost_ready (EMPATHY_TP_CHAT (self
));
1255 tp_chat_constructor (GType type
,
1257 GObjectConstructParam
*props
)
1260 EmpathyTpChatPriv
*priv
;
1263 chat
= G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->constructor (type
, n_props
, props
);
1265 priv
= GET_PRIV (chat
);
1267 priv
->connection
= g_object_ref (tp_account_get_connection (priv
->account
));
1268 tp_g_signal_connect_object (priv
->channel
, "invalidated",
1269 G_CALLBACK (tp_chat_invalidated_cb
),
1272 if (tp_proxy_has_interface_by_id (priv
->channel
,
1273 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
1274 const TpIntSet
*members
;
1277 /* Get self contact from the group's self handle */
1278 handle
= tp_channel_group_get_self_handle (priv
->channel
);
1279 empathy_tp_contact_factory_get_from_handle (priv
->connection
,
1280 handle
, tp_chat_got_self_contact_cb
,
1283 /* Get initial member contacts */
1284 members
= tp_channel_group_get_members (priv
->channel
);
1285 handles
= tp_intset_to_array (members
);
1286 empathy_tp_contact_factory_get_from_handles (priv
->connection
,
1287 handles
->len
, (TpHandle
*) handles
->data
,
1288 tp_chat_got_added_contacts_cb
, NULL
, NULL
, chat
);
1290 priv
->can_upgrade_to_muc
= FALSE
;
1292 tp_g_signal_connect_object (priv
->channel
, "group-members-changed",
1293 G_CALLBACK (tp_chat_group_members_changed_cb
), chat
, 0);
1295 EmpathyDispatcher
*dispatcher
= empathy_dispatcher_dup_singleton ();
1298 /* Get the self contact from the connection's self handle */
1299 handle
= tp_connection_get_self_handle (priv
->connection
);
1300 empathy_tp_contact_factory_get_from_handle (priv
->connection
,
1301 handle
, tp_chat_got_self_contact_cb
,
1304 /* Get the remote contact */
1305 handle
= tp_channel_get_handle (priv
->channel
, NULL
);
1306 empathy_tp_contact_factory_get_from_handle (priv
->connection
,
1307 handle
, tp_chat_got_remote_contact_cb
,
1310 list
= empathy_dispatcher_find_requestable_channel_classes (
1311 dispatcher
, priv
->connection
,
1312 tp_channel_get_channel_type (priv
->channel
),
1313 TP_UNKNOWN_HANDLE_TYPE
, NULL
);
1315 for (ptr
= list
; ptr
; ptr
= ptr
->next
) {
1316 GValueArray
*array
= ptr
->data
;
1317 const char **oprops
= g_value_get_boxed (
1318 g_value_array_get_nth (array
, 1));
1320 if (tp_strv_contains (oprops
, EMP_IFACE_CHANNEL_INTERFACE_CONFERENCE
".InitialChannels")) {
1321 priv
->can_upgrade_to_muc
= TRUE
;
1327 g_object_unref (dispatcher
);
1330 if (tp_proxy_has_interface_by_id (priv
->channel
,
1331 TP_IFACE_QUARK_PROPERTIES_INTERFACE
)) {
1332 tp_cli_properties_interface_call_list_properties (priv
->channel
, -1,
1333 tp_chat_list_properties_cb
,
1336 tp_cli_properties_interface_connect_to_properties_changed (priv
->channel
,
1337 tp_chat_properties_changed_cb
,
1339 G_OBJECT (chat
), NULL
);
1340 tp_cli_properties_interface_connect_to_property_flags_changed (priv
->channel
,
1341 tp_chat_property_flags_changed_cb
,
1343 G_OBJECT (chat
), NULL
);
1346 /* Check if the chat is password protected */
1347 if (tp_proxy_has_interface_by_id (priv
->channel
,
1348 TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD
)) {
1349 priv
->got_password_flags
= FALSE
;
1351 tp_cli_channel_interface_password_connect_to_password_flags_changed
1352 (priv
->channel
, password_flags_changed_cb
, chat
, NULL
,
1353 G_OBJECT (chat
), NULL
);
1355 tp_cli_channel_interface_password_call_get_password_flags
1356 (priv
->channel
, -1, got_password_flags_cb
, chat
, NULL
, chat
);
1358 /* No Password interface, so no need to fetch the password flags */
1359 priv
->got_password_flags
= TRUE
;
1366 tp_chat_get_property (GObject
*object
,
1371 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
1372 EmpathyTpChatPriv
*priv
= GET_PRIV (object
);
1376 g_value_set_object (value
, priv
->account
);
1379 g_value_set_object (value
, priv
->channel
);
1381 case PROP_REMOTE_CONTACT
:
1382 g_value_set_object (value
, priv
->remote_contact
);
1385 g_value_set_boolean (value
, priv
->ready
);
1387 case PROP_PASSWORD_NEEDED
:
1388 g_value_set_boolean (value
, empathy_tp_chat_password_needed (self
));
1391 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1397 tp_chat_set_property (GObject
*object
,
1399 const GValue
*value
,
1402 EmpathyTpChatPriv
*priv
= GET_PRIV (object
);
1406 priv
->account
= g_value_dup_object (value
);
1409 priv
->channel
= g_value_dup_object (value
);
1412 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1418 empathy_tp_chat_class_init (EmpathyTpChatClass
*klass
)
1420 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1422 object_class
->dispose
= tp_chat_dispose
;
1423 object_class
->finalize
= tp_chat_finalize
;
1424 object_class
->constructor
= tp_chat_constructor
;
1425 object_class
->get_property
= tp_chat_get_property
;
1426 object_class
->set_property
= tp_chat_set_property
;
1428 g_object_class_install_property (object_class
,
1430 g_param_spec_object ("account",
1432 "the account associated with the chat",
1435 G_PARAM_CONSTRUCT_ONLY
|
1436 G_PARAM_STATIC_STRINGS
));
1438 g_object_class_install_property (object_class
,
1440 g_param_spec_object ("channel",
1441 "telepathy channel",
1442 "The text channel for the chat",
1445 G_PARAM_CONSTRUCT_ONLY
));
1447 g_object_class_install_property (object_class
,
1448 PROP_REMOTE_CONTACT
,
1449 g_param_spec_object ("remote-contact",
1450 "The remote contact",
1451 "The remote contact if there is no group iface on the channel",
1452 EMPATHY_TYPE_CONTACT
,
1455 g_object_class_install_property (object_class
,
1457 g_param_spec_boolean ("ready",
1458 "Is the object ready",
1459 "This object can't be used until this becomes true",
1463 g_object_class_install_property (object_class
,
1464 PROP_PASSWORD_NEEDED
,
1465 g_param_spec_boolean ("password-needed",
1467 "TRUE if a password is needed to join the channel",
1472 signals
[MESSAGE_RECEIVED
] =
1473 g_signal_new ("message-received",
1474 G_TYPE_FROM_CLASS (klass
),
1478 g_cclosure_marshal_VOID__OBJECT
,
1480 1, EMPATHY_TYPE_MESSAGE
);
1482 signals
[SEND_ERROR
] =
1483 g_signal_new ("send-error",
1484 G_TYPE_FROM_CLASS (klass
),
1488 _empathy_marshal_VOID__STRING_UINT
,
1490 2, G_TYPE_STRING
, G_TYPE_UINT
);
1492 signals
[CHAT_STATE_CHANGED
] =
1493 g_signal_new ("chat-state-changed",
1494 G_TYPE_FROM_CLASS (klass
),
1498 _empathy_marshal_VOID__OBJECT_UINT
,
1500 2, EMPATHY_TYPE_CONTACT
, G_TYPE_UINT
);
1502 signals
[PROPERTY_CHANGED
] =
1503 g_signal_new ("property-changed",
1504 G_TYPE_FROM_CLASS (klass
),
1508 _empathy_marshal_VOID__STRING_BOXED
,
1510 2, G_TYPE_STRING
, G_TYPE_VALUE
);
1513 g_signal_new ("destroy",
1514 G_TYPE_FROM_CLASS (klass
),
1518 g_cclosure_marshal_VOID__VOID
,
1522 g_type_class_add_private (object_class
, sizeof (EmpathyTpChatPriv
));
1526 empathy_tp_chat_init (EmpathyTpChat
*chat
)
1528 EmpathyTpChatPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (chat
,
1529 EMPATHY_TYPE_TP_CHAT
, EmpathyTpChatPriv
);
1532 priv
->messages_queue
= g_queue_new ();
1533 priv
->pending_messages_queue
= g_queue_new ();
1537 tp_chat_iface_init (EmpathyContactListIface
*iface
)
1539 iface
->add
= tp_chat_add
;
1540 iface
->remove
= tp_chat_remove
;
1541 iface
->get_members
= tp_chat_get_members
;
1545 empathy_tp_chat_new (TpAccount
*account
,
1548 g_return_val_if_fail (TP_IS_ACCOUNT (account
), NULL
);
1549 g_return_val_if_fail (TP_IS_CHANNEL (channel
), NULL
);
1551 return g_object_new (EMPATHY_TYPE_TP_CHAT
,
1558 empathy_tp_chat_close (EmpathyTpChat
*chat
) {
1559 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1561 /* If there are still messages left, it'll come back..
1562 * We loose the ordering of sent messages though */
1563 tp_cli_channel_call_close (priv
->channel
, -1, tp_chat_async_cb
,
1564 "closing channel", NULL
, NULL
);
1568 empathy_tp_chat_get_id (EmpathyTpChat
*chat
)
1570 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1574 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1576 id
= tp_channel_get_identifier (priv
->channel
);
1577 if (!EMP_STR_EMPTY (id
))
1579 else if (priv
->remote_contact
)
1580 return empathy_contact_get_id (priv
->remote_contact
);
1587 empathy_tp_chat_get_remote_contact (EmpathyTpChat
*chat
)
1589 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1591 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1592 g_return_val_if_fail (priv
->ready
, NULL
);
1594 return priv
->remote_contact
;
1598 empathy_tp_chat_get_channel (EmpathyTpChat
*chat
)
1600 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1602 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1604 return priv
->channel
;
1608 empathy_tp_chat_get_account (EmpathyTpChat
*chat
)
1610 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1612 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1614 return priv
->account
;
1618 empathy_tp_chat_get_connection (EmpathyTpChat
*chat
)
1620 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1622 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1624 return tp_channel_borrow_connection (priv
->channel
);
1627 empathy_tp_chat_is_ready (EmpathyTpChat
*chat
)
1629 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1631 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), FALSE
);
1637 empathy_tp_chat_send (EmpathyTpChat
*chat
,
1638 EmpathyMessage
*message
)
1640 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1641 const gchar
*message_body
;
1642 TpChannelTextMessageType message_type
;
1644 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1645 g_return_if_fail (EMPATHY_IS_MESSAGE (message
));
1646 g_return_if_fail (priv
->ready
);
1648 message_body
= empathy_message_get_body (message
);
1649 message_type
= empathy_message_get_tptype (message
);
1651 DEBUG ("Sending message: %s", message_body
);
1652 tp_cli_channel_type_text_call_send (priv
->channel
, -1,
1656 g_object_ref (message
),
1657 (GDestroyNotify
) g_object_unref
,
1662 empathy_tp_chat_set_state (EmpathyTpChat
*chat
,
1663 TpChannelChatState state
)
1665 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1667 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1668 g_return_if_fail (priv
->ready
);
1670 if (tp_proxy_has_interface_by_id (priv
->channel
,
1671 TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE
)) {
1672 DEBUG ("Set state: %d", state
);
1673 tp_cli_channel_interface_chat_state_call_set_chat_state (priv
->channel
, -1,
1676 "setting chat state",
1684 empathy_tp_chat_get_pending_messages (EmpathyTpChat
*chat
)
1686 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1688 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat
), NULL
);
1689 g_return_val_if_fail (priv
->ready
, NULL
);
1691 return priv
->pending_messages_queue
->head
;
1695 acknowledge_messages (EmpathyTpChat
*chat
, GArray
*ids
) {
1696 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1698 tp_cli_channel_type_text_call_acknowledge_pending_messages (
1699 priv
->channel
, -1, ids
, tp_chat_async_cb
,
1700 "acknowledging received message", NULL
, G_OBJECT (chat
));
1704 empathy_tp_chat_acknowledge_message (EmpathyTpChat
*chat
,
1705 EmpathyMessage
*message
) {
1706 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1707 GArray
*message_ids
;
1711 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1712 g_return_if_fail (priv
->ready
);
1714 if (!empathy_message_is_incoming (message
))
1717 message_ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), 1);
1719 id
= empathy_message_get_id (message
);
1720 g_array_append_val (message_ids
, id
);
1721 acknowledge_messages (chat
, message_ids
);
1722 g_array_free (message_ids
, TRUE
);
1725 m
= g_queue_find (priv
->pending_messages_queue
, message
);
1726 g_assert (m
!= NULL
);
1727 g_queue_delete_link (priv
->pending_messages_queue
, m
);
1728 g_object_unref (message
);
1732 empathy_tp_chat_acknowledge_messages (EmpathyTpChat
*chat
,
1733 const GSList
*messages
) {
1734 EmpathyTpChatPriv
*priv
= GET_PRIV (chat
);
1735 /* Copy messages as the messges list (probably is) our own */
1736 GSList
*msgs
= g_slist_copy ((GSList
*) messages
);
1739 GArray
*message_ids
;
1741 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat
));
1742 g_return_if_fail (priv
->ready
);
1744 length
= g_slist_length ((GSList
*) messages
);
1749 message_ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), length
);
1751 for (l
= msgs
; l
!= NULL
; l
= g_slist_next (l
)) {
1754 EmpathyMessage
*message
= EMPATHY_MESSAGE (l
->data
);
1756 m
= g_queue_find (priv
->pending_messages_queue
, message
);
1757 g_assert (m
!= NULL
);
1758 g_queue_delete_link (priv
->pending_messages_queue
, m
);
1760 if (empathy_message_is_incoming (message
)) {
1761 guint id
= empathy_message_get_id (message
);
1762 g_array_append_val (message_ids
, id
);
1764 g_object_unref (message
);
1767 if (message_ids
->len
> 0)
1768 acknowledge_messages (chat
, message_ids
);
1770 g_array_free (message_ids
, TRUE
);
1771 g_slist_free (msgs
);
1775 empathy_tp_chat_acknowledge_all_messages (EmpathyTpChat
*chat
)
1777 empathy_tp_chat_acknowledge_messages (chat
,
1778 (GSList
*) empathy_tp_chat_get_pending_messages (chat
));
1782 empathy_tp_chat_password_needed (EmpathyTpChat
*self
)
1784 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1786 return priv
->password_flags
& TP_CHANNEL_PASSWORD_FLAG_PROVIDE
;
1790 provide_password_cb (TpChannel
*channel
,
1792 const GError
*error
,
1794 GObject
*weak_object
)
1796 GSimpleAsyncResult
*result
= user_data
;
1798 if (error
!= NULL
) {
1799 g_simple_async_result_set_from_error (result
, error
);
1801 else if (!correct
) {
1802 /* The current D-Bus API is a bit weird so re-use the
1803 * AuthenticationFailed error */
1804 g_simple_async_result_set_error (result
, TP_ERRORS
,
1805 TP_ERROR_AUTHENTICATION_FAILED
, "Wrong password");
1808 g_simple_async_result_complete (result
);
1809 g_object_unref (result
);
1813 empathy_tp_chat_provide_password_async (EmpathyTpChat
*self
,
1814 const gchar
*password
,
1815 GAsyncReadyCallback callback
,
1818 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1819 GSimpleAsyncResult
*result
;
1821 result
= g_simple_async_result_new (G_OBJECT (self
),
1822 callback
, user_data
,
1823 empathy_tp_chat_provide_password_finish
);
1825 tp_cli_channel_interface_password_call_provide_password
1826 (priv
->channel
, -1, password
, provide_password_cb
, result
,
1827 NULL
, G_OBJECT (self
));
1831 empathy_tp_chat_provide_password_finish (EmpathyTpChat
*self
,
1832 GAsyncResult
*result
,
1835 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result
),
1839 g_return_val_if_fail (g_simple_async_result_is_valid (result
,
1840 G_OBJECT (self
), empathy_tp_chat_provide_password_finish
), FALSE
);
1846 * empathy_tp_chat_can_add_contact:
1848 * Returns: %TRUE if empathy_contact_list_add() will work for this channel.
1849 * That is if this chat is a 1-to-1 channel that can be upgraded to
1850 * a MUC using the Conference interface or if the channel is a MUC.
1853 empathy_tp_chat_can_add_contact (EmpathyTpChat
*self
)
1855 EmpathyTpChatPriv
*priv
;
1857 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), FALSE
);
1859 priv
= GET_PRIV (self
);
1861 return priv
->can_upgrade_to_muc
||
1862 tp_proxy_has_interface_by_id (priv
->channel
,
1863 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
);;
1867 leave_remove_members_cb (TpChannel
*proxy
,
1868 const GError
*error
,
1870 GObject
*weak_object
)
1872 EmpathyTpChat
*self
= user_data
;
1877 DEBUG ("RemoveMembers failed (%s); closing the channel", error
->message
);
1878 empathy_tp_chat_close (self
);
1882 empathy_tp_chat_leave (EmpathyTpChat
*self
)
1884 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1885 TpHandle self_handle
;
1888 if (!tp_proxy_has_interface_by_id (priv
->channel
,
1889 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
1890 empathy_tp_chat_close (self
);
1894 self_handle
= tp_channel_group_get_self_handle (priv
->channel
);
1895 if (self_handle
== 0) {
1896 /* we are not member of the channel */
1897 empathy_tp_chat_close (self
);
1901 array
= g_array_sized_new (FALSE
, FALSE
, sizeof (TpHandle
), 1);
1902 g_array_insert_val (array
, 0, self_handle
);
1904 tp_cli_channel_interface_group_call_remove_members (priv
->channel
, -1, array
,
1905 "", leave_remove_members_cb
, self
, NULL
, G_OBJECT (self
));
1907 g_array_free (array
, TRUE
);
1911 add_members_cb (TpChannel
*proxy
,
1912 const GError
*error
,
1914 GObject
*weak_object
)
1916 EmpathyTpChatPriv
*priv
= GET_PRIV (weak_object
);
1918 if (error
!= NULL
) {
1919 DEBUG ("Failed to join chat (%s): %s",
1920 tp_channel_get_identifier (priv
->channel
), error
->message
);
1925 empathy_tp_chat_join (EmpathyTpChat
*self
)
1927 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1928 TpHandle self_handle
;
1931 self_handle
= tp_channel_group_get_self_handle (priv
->channel
);
1933 members
= g_array_sized_new (FALSE
, FALSE
, sizeof (TpHandle
), 1);
1934 g_array_append_val (members
, self_handle
);
1936 tp_cli_channel_interface_group_call_add_members (priv
->channel
, -1, members
,
1937 "", add_members_cb
, NULL
, NULL
, G_OBJECT (self
));
1939 g_array_free (members
, TRUE
);
1943 empathy_tp_chat_is_invited (EmpathyTpChat
*self
,
1946 EmpathyTpChatPriv
*priv
= GET_PRIV (self
);
1947 TpHandle self_handle
;
1949 if (!tp_proxy_has_interface (priv
->channel
, TP_IFACE_CHANNEL_INTERFACE_GROUP
))
1952 self_handle
= tp_channel_group_get_self_handle (priv
->channel
);
1953 if (self_handle
== 0)
1956 return tp_channel_group_get_local_pending_info (priv
->channel
, self_handle
,
1957 inviter
, NULL
, NULL
);