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-marshal.h"
34 #include "empathy-request-util.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 struct _EmpathyTpChatPrivate
{
44 EmpathyContact
*remote_contact
;
46 /* Queue of messages not signalled yet */
47 GQueue
*messages_queue
;
48 /* Queue of messages signalled but not acked yet */
49 GQueue
*pending_messages_queue
;
50 gboolean had_properties_list
;
51 GPtrArray
*properties
;
52 gboolean can_upgrade_to_muc
;
54 GHashTable
*messages_being_sent
;
56 /* GSimpleAsyncResult used when preparing EMPATHY_TP_CHAT_FEATURE_CORE */
57 GSimpleAsyncResult
*ready_result
;
60 static void tp_chat_iface_init (EmpathyContactListIface
*iface
);
66 PROP_N_MESSAGES_SENDING
,
78 static guint signals
[LAST_SIGNAL
];
80 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat
, empathy_tp_chat
, TP_TYPE_TEXT_CHANNEL
,
81 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST
,
85 tp_chat_set_delivery_status (EmpathyTpChat
*self
,
87 EmpathyDeliveryStatus delivery_status
)
89 TpDeliveryReportingSupportFlags flags
=
90 tp_text_channel_get_delivery_reporting_support (
91 TP_TEXT_CHANNEL (self
));
93 /* channel must support receiving failures and successes */
94 if (!tp_str_empty (token
) &&
95 flags
& TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES
&&
96 flags
& TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES
) {
98 DEBUG ("Delivery status (%s) = %u", token
, delivery_status
);
100 switch (delivery_status
) {
101 case EMPATHY_DELIVERY_STATUS_NONE
:
102 g_hash_table_remove (self
->priv
->messages_being_sent
,
107 g_hash_table_insert (self
->priv
->messages_being_sent
,
109 GUINT_TO_POINTER (delivery_status
));
113 g_object_notify (G_OBJECT (self
), "n-messages-sending");
117 static void tp_chat_prepare_ready_async (TpProxy
*proxy
,
118 const TpProxyFeature
*feature
,
119 GAsyncReadyCallback callback
,
123 tp_chat_async_cb (TpChannel
*proxy
,
126 GObject
*weak_object
)
129 DEBUG ("Error %s: %s", (gchar
*) user_data
, error
->message
);
134 create_conference_cb (GObject
*source
,
135 GAsyncResult
*result
,
138 GError
*error
= NULL
;
140 if (!tp_account_channel_request_create_channel_finish (
141 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, &error
)) {
142 DEBUG ("Failed to create conference channel: %s", error
->message
);
143 g_error_free (error
);
148 tp_chat_add (EmpathyContactList
*list
,
149 EmpathyContact
*contact
,
150 const gchar
*message
)
152 EmpathyTpChat
*self
= (EmpathyTpChat
*) list
;
153 TpChannel
*channel
= (TpChannel
*) self
;
155 if (tp_proxy_has_interface_by_id (self
,
156 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
158 GArray handles
= {(gchar
*) &handle
, 1};
160 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
161 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
163 handle
= empathy_contact_get_handle (contact
);
164 tp_cli_channel_interface_group_call_add_members (channel
,
165 -1, &handles
, NULL
, NULL
, NULL
, NULL
, NULL
);
166 } else if (self
->priv
->can_upgrade_to_muc
) {
167 TpAccountChannelRequest
*req
;
169 const char *object_path
;
170 GPtrArray channels
= { (gpointer
*) &object_path
, 1 };
171 const char *invitees
[2] = { NULL
, };
173 invitees
[0] = empathy_contact_get_id (contact
);
174 object_path
= tp_proxy_get_object_path (self
);
177 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
178 TP_IFACE_CHANNEL_TYPE_TEXT
,
179 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
181 TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS
,
182 TP_ARRAY_TYPE_OBJECT_PATH_LIST
, &channels
,
183 TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS
,
184 G_TYPE_STRV
, invitees
,
185 /* FIXME: InvitationMessage ? */
188 req
= tp_account_channel_request_new (self
->priv
->account
, props
,
189 TP_USER_ACTION_TIME_NOT_USER_ACTION
);
191 /* Although this is a MUC, it's anonymous, so CreateChannel is
193 tp_account_channel_request_create_channel_async (req
, EMPATHY_CHAT_BUS_NAME
,
194 NULL
, create_conference_cb
, NULL
);
196 g_object_unref (req
);
197 g_hash_table_unref (props
);
199 g_warning ("Cannot add to this channel");
204 tp_chat_remove (EmpathyContactList
*list
,
205 EmpathyContact
*contact
,
206 const gchar
*message
)
208 EmpathyTpChat
*self
= (EmpathyTpChat
*) list
;
210 GArray handles
= {(gchar
*) &handle
, 1};
212 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
213 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
215 handle
= empathy_contact_get_handle (contact
);
216 tp_cli_channel_interface_group_call_remove_members ((TpChannel
*) self
, -1,
223 tp_chat_get_members (EmpathyContactList
*list
)
225 EmpathyTpChat
*self
= (EmpathyTpChat
*) list
;
226 GList
*members
= NULL
;
228 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list
), NULL
);
230 if (self
->priv
->members
) {
231 members
= g_list_copy (self
->priv
->members
);
232 g_list_foreach (members
, (GFunc
) g_object_ref
, NULL
);
234 members
= g_list_prepend (members
, g_object_ref (self
->priv
->user
));
235 if (self
->priv
->remote_contact
!= NULL
)
236 members
= g_list_prepend (members
, g_object_ref (self
->priv
->remote_contact
));
243 check_ready (EmpathyTpChat
*self
)
245 if (self
->priv
->ready_result
== NULL
)
248 if (g_queue_get_length (self
->priv
->messages_queue
) > 0)
253 g_simple_async_result_complete (self
->priv
->ready_result
);
254 tp_clear_object (&self
->priv
->ready_result
);
258 tp_chat_emit_queued_messages (EmpathyTpChat
*self
)
260 EmpathyMessage
*message
;
262 /* Check if we can now emit some queued messages */
263 while ((message
= g_queue_peek_head (self
->priv
->messages_queue
)) != NULL
) {
264 if (empathy_message_get_sender (message
) == NULL
) {
268 DEBUG ("Queued message ready");
269 g_queue_pop_head (self
->priv
->messages_queue
);
270 g_queue_push_tail (self
->priv
->pending_messages_queue
, message
);
271 g_signal_emit (self
, signals
[MESSAGE_RECEIVED
], 0, message
);
278 tp_chat_got_sender_cb (TpConnection
*connection
,
279 EmpathyContact
*contact
,
284 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
287 DEBUG ("Error: %s", error
->message
);
288 /* Do not block the message queue, just drop this message */
289 g_queue_remove (self
->priv
->messages_queue
, message
);
291 empathy_message_set_sender (message
, contact
);
294 tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (self
));
298 tp_chat_build_message (EmpathyTpChat
*self
,
302 EmpathyMessage
*message
;
305 message
= empathy_message_new_from_tp_message (msg
, incoming
);
306 /* FIXME: this is actually a lie for incoming messages. */
307 empathy_message_set_receiver (message
, self
->priv
->user
);
309 g_queue_push_tail (self
->priv
->messages_queue
, message
);
311 sender
= tp_signalled_message_get_sender (msg
);
312 g_assert (sender
!= NULL
);
314 if (tp_contact_get_handle (sender
) == 0) {
315 empathy_message_set_sender (message
, self
->priv
->user
);
316 tp_chat_emit_queued_messages (self
);
318 TpConnection
*connection
= tp_channel_borrow_connection (
321 empathy_tp_contact_factory_get_from_handle (connection
,
322 tp_contact_get_handle (sender
),
323 tp_chat_got_sender_cb
,
324 message
, NULL
, G_OBJECT (self
));
329 handle_delivery_report (EmpathyTpChat
*self
,
332 TpDeliveryStatus delivery_status
;
333 const GHashTable
*header
;
334 TpChannelTextSendError delivery_error
;
337 const gchar
*message_body
= NULL
;
338 const gchar
*delivery_dbus_error
;
339 const gchar
*delivery_token
= NULL
;
341 header
= tp_message_peek (message
, 0);
345 delivery_token
= tp_asv_get_string (header
, "delivery-token");
346 delivery_status
= tp_asv_get_uint32 (header
, "delivery-status", &valid
);
350 } else if (delivery_status
== TP_DELIVERY_STATUS_ACCEPTED
) {
351 DEBUG ("Accepted %s", delivery_token
);
352 tp_chat_set_delivery_status (self
, delivery_token
,
353 EMPATHY_DELIVERY_STATUS_ACCEPTED
);
355 } else if (delivery_status
== TP_DELIVERY_STATUS_DELIVERED
) {
356 DEBUG ("Delivered %s", delivery_token
);
357 tp_chat_set_delivery_status (self
, delivery_token
,
358 EMPATHY_DELIVERY_STATUS_NONE
);
360 } else if (delivery_status
!= TP_DELIVERY_STATUS_PERMANENTLY_FAILED
) {
364 delivery_error
= tp_asv_get_uint32 (header
, "delivery-error", &valid
);
366 delivery_error
= TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
;
368 delivery_dbus_error
= tp_asv_get_string (header
, "delivery-dbus-error");
370 /* TODO: ideally we should use tp-glib API giving us the echoed message as a
371 * TpMessage. (fdo #35884) */
372 echo
= tp_asv_get_boxed (header
, "delivery-echo",
373 TP_ARRAY_TYPE_MESSAGE_PART_LIST
);
374 if (echo
!= NULL
&& echo
->len
>= 2) {
375 const GHashTable
*echo_body
;
377 echo_body
= g_ptr_array_index (echo
, 1);
378 if (echo_body
!= NULL
)
379 message_body
= tp_asv_get_string (echo_body
, "content");
382 tp_chat_set_delivery_status (self
, delivery_token
,
383 EMPATHY_DELIVERY_STATUS_NONE
);
384 g_signal_emit (self
, signals
[SEND_ERROR
], 0, message_body
,
385 delivery_error
, delivery_dbus_error
);
388 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self
),
389 message
, NULL
, NULL
);
393 handle_incoming_message (EmpathyTpChat
*self
,
399 if (tp_message_is_delivery_report (message
)) {
400 handle_delivery_report (self
, message
);
404 message_body
= tp_message_to_text (message
, NULL
);
406 DEBUG ("Message %s (channel %s): %s",
407 pending
? "pending" : "received",
408 tp_proxy_get_object_path (self
), message_body
);
410 if (message_body
== NULL
) {
411 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
413 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self
),
414 message
, NULL
, NULL
);
418 tp_chat_build_message (self
, message
, TRUE
);
420 g_free (message_body
);
424 message_received_cb (TpTextChannel
*channel
,
428 handle_incoming_message (self
, message
, FALSE
);
432 find_pending_message_func (gconstpointer a
,
435 EmpathyMessage
*msg
= (EmpathyMessage
*) a
;
436 TpMessage
*message
= (TpMessage
*) b
;
438 if (empathy_message_get_tp_message (msg
) == message
)
445 pending_message_removed_cb (TpTextChannel
*channel
,
451 m
= g_queue_find_custom (self
->priv
->pending_messages_queue
, message
,
452 find_pending_message_func
);
457 g_signal_emit (self
, signals
[MESSAGE_ACKNOWLEDGED
], 0, m
->data
);
459 g_object_unref (m
->data
);
460 g_queue_delete_link (self
->priv
->pending_messages_queue
, m
);
464 message_sent_cb (TpTextChannel
*channel
,
466 TpMessageSendingFlags flags
,
472 message_body
= tp_message_to_text (message
, NULL
);
474 DEBUG ("Message sent: %s", message_body
);
476 tp_chat_build_message (self
, message
, FALSE
);
478 g_free (message_body
);
481 static TpChannelTextSendError
482 error_to_text_send_error (GError
*error
)
484 if (error
->domain
!= TP_ERRORS
)
485 return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
;
487 switch (error
->code
) {
488 case TP_ERROR_OFFLINE
:
489 return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE
;
490 case TP_ERROR_INVALID_HANDLE
:
491 return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT
;
492 case TP_ERROR_PERMISSION_DENIED
:
493 return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED
;
494 case TP_ERROR_NOT_IMPLEMENTED
:
495 return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED
;
498 return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
;
502 message_send_cb (GObject
*source
,
503 GAsyncResult
*result
,
506 EmpathyTpChat
*self
= user_data
;
507 TpTextChannel
*channel
= (TpTextChannel
*) source
;
509 GError
*error
= NULL
;
511 if (!tp_text_channel_send_message_finish (channel
, result
, &token
, &error
)) {
512 DEBUG ("Error: %s", error
->message
);
514 /* FIXME: we should use the body of the message as first argument of the
515 * signal but can't easily get it as we just get a user_data pointer. Once
516 * we'll have rebased EmpathyTpChat on top of TpTextChannel we'll be able
517 * to use the user_data pointer to pass the message and fix this. */
518 g_signal_emit (self
, signals
[SEND_ERROR
], 0,
519 NULL
, error_to_text_send_error (error
), NULL
);
521 g_error_free (error
);
524 tp_chat_set_delivery_status (self
, token
,
525 EMPATHY_DELIVERY_STATUS_SENDING
);
531 TpChannelChatState state
;
535 tp_chat_state_changed_got_contact_cb (TpConnection
*connection
,
536 EmpathyContact
*contact
,
541 TpChannelChatState state
;
544 DEBUG ("Error: %s", error
->message
);
548 state
= GPOINTER_TO_UINT (user_data
);
549 DEBUG ("Chat state changed for %s (%d): %d",
550 empathy_contact_get_alias (contact
),
551 empathy_contact_get_handle (contact
), state
);
553 g_signal_emit (chat
, signals
[CHAT_STATE_CHANGED
], 0, contact
, state
);
557 tp_chat_state_changed_cb (TpChannel
*channel
,
559 TpChannelChatState state
,
562 TpConnection
*connection
= tp_channel_borrow_connection (
565 empathy_tp_contact_factory_get_from_handle (connection
, handle
,
566 tp_chat_state_changed_got_contact_cb
, GUINT_TO_POINTER (state
),
567 NULL
, G_OBJECT (self
));
571 list_pending_messages (EmpathyTpChat
*self
)
575 messages
= tp_text_channel_get_pending_messages (
576 TP_TEXT_CHANNEL (self
));
578 for (l
= messages
; l
!= NULL
; l
= g_list_next (l
)) {
579 TpMessage
*message
= l
->data
;
581 handle_incoming_message (self
, message
, FALSE
);
584 g_list_free (messages
);
588 tp_chat_property_flags_changed_cb (TpProxy
*proxy
,
589 const GPtrArray
*properties
,
593 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
596 if (!self
->priv
->had_properties_list
|| !properties
) {
600 for (i
= 0; i
< properties
->len
; i
++) {
601 GValueArray
*prop_struct
;
602 EmpathyTpChatProperty
*property
;
606 prop_struct
= g_ptr_array_index (properties
, i
);
607 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
608 flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 1));
610 for (j
= 0; j
< self
->priv
->properties
->len
; j
++) {
611 property
= g_ptr_array_index (self
->priv
->properties
, j
);
612 if (property
->id
== id
) {
613 property
->flags
= flags
;
614 DEBUG ("property %s flags changed: %d",
615 property
->name
, property
->flags
);
623 tp_chat_properties_changed_cb (TpProxy
*proxy
,
624 const GPtrArray
*properties
,
628 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
631 if (!self
->priv
->had_properties_list
|| !properties
) {
635 for (i
= 0; i
< properties
->len
; i
++) {
636 GValueArray
*prop_struct
;
637 EmpathyTpChatProperty
*property
;
641 prop_struct
= g_ptr_array_index (properties
, i
);
642 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
643 src_value
= g_value_get_boxed (g_value_array_get_nth (prop_struct
, 1));
645 for (j
= 0; j
< self
->priv
->properties
->len
; j
++) {
646 property
= g_ptr_array_index (self
->priv
->properties
, j
);
647 if (property
->id
== id
) {
648 if (property
->value
) {
649 g_value_copy (src_value
, property
->value
);
651 property
->value
= tp_g_value_slice_dup (src_value
);
654 DEBUG ("property %s changed", property
->name
);
655 g_signal_emit (chat
, signals
[PROPERTY_CHANGED
], 0,
656 property
->name
, property
->value
);
664 tp_chat_get_properties_cb (TpProxy
*proxy
,
665 const GPtrArray
*properties
,
671 DEBUG ("Error getting properties: %s", error
->message
);
675 tp_chat_properties_changed_cb (proxy
, properties
, user_data
, chat
);
679 tp_chat_list_properties_cb (TpProxy
*proxy
,
680 const GPtrArray
*properties
,
685 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
689 self
->priv
->had_properties_list
= TRUE
;
692 DEBUG ("Error listing properties: %s", error
->message
);
696 ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), properties
->len
);
697 self
->priv
->properties
= g_ptr_array_sized_new (properties
->len
);
698 for (i
= 0; i
< properties
->len
; i
++) {
699 GValueArray
*prop_struct
;
700 EmpathyTpChatProperty
*property
;
702 prop_struct
= g_ptr_array_index (properties
, i
);
703 property
= g_slice_new0 (EmpathyTpChatProperty
);
704 property
->id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
705 property
->name
= g_value_dup_string (g_value_array_get_nth (prop_struct
, 1));
706 property
->flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 3));
708 DEBUG ("Adding property name=%s id=%d flags=%d",
709 property
->name
, property
->id
, property
->flags
);
710 g_ptr_array_add (self
->priv
->properties
, property
);
711 if (property
->flags
& TP_PROPERTY_FLAG_READ
) {
712 g_array_append_val (ids
, property
->id
);
716 tp_cli_properties_interface_call_get_properties (proxy
, -1,
718 tp_chat_get_properties_cb
,
722 g_array_free (ids
, TRUE
);
726 empathy_tp_chat_set_property (EmpathyTpChat
*self
,
730 EmpathyTpChatProperty
*property
;
733 if (!self
->priv
->had_properties_list
) {
737 for (i
= 0; i
< self
->priv
->properties
->len
; i
++) {
738 property
= g_ptr_array_index (self
->priv
->properties
, i
);
739 if (!tp_strdiff (property
->name
, name
)) {
740 GPtrArray
*properties
;
743 GValue dest_value
= {0, };
745 if (!(property
->flags
& TP_PROPERTY_FLAG_WRITE
)) {
749 g_value_init (&id
, G_TYPE_UINT
);
750 g_value_init (&dest_value
, G_TYPE_VALUE
);
751 g_value_set_uint (&id
, property
->id
);
752 g_value_set_boxed (&dest_value
, value
);
754 prop
= g_value_array_new (2);
755 g_value_array_append (prop
, &id
);
756 g_value_array_append (prop
, &dest_value
);
758 properties
= g_ptr_array_sized_new (1);
759 g_ptr_array_add (properties
, prop
);
761 DEBUG ("Set property %s", name
);
762 tp_cli_properties_interface_call_set_properties (self
, -1,
764 (tp_cli_properties_interface_callback_for_set_properties
)
766 "Seting property", NULL
,
769 g_ptr_array_free (properties
, TRUE
);
770 g_value_array_free (prop
);
777 EmpathyTpChatProperty
*
778 empathy_tp_chat_get_property (EmpathyTpChat
*self
,
781 EmpathyTpChatProperty
*property
;
784 if (!self
->priv
->had_properties_list
) {
788 for (i
= 0; i
< self
->priv
->properties
->len
; i
++) {
789 property
= g_ptr_array_index (self
->priv
->properties
, i
);
790 if (!tp_strdiff (property
->name
, name
)) {
799 empathy_tp_chat_get_properties (EmpathyTpChat
*self
)
801 return self
->priv
->properties
;
805 tp_chat_dispose (GObject
*object
)
807 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
809 tp_clear_object (&self
->priv
->account
);
810 tp_clear_object (&self
->priv
->remote_contact
);
811 tp_clear_object (&self
->priv
->user
);
813 g_queue_foreach (self
->priv
->messages_queue
, (GFunc
) g_object_unref
, NULL
);
814 g_queue_clear (self
->priv
->messages_queue
);
816 g_queue_foreach (self
->priv
->pending_messages_queue
,
817 (GFunc
) g_object_unref
, NULL
);
818 g_queue_clear (self
->priv
->pending_messages_queue
);
820 tp_clear_object (&self
->priv
->ready_result
);
822 if (G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose
)
823 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose (object
);
827 tp_chat_finalize (GObject
*object
)
829 EmpathyTpChat
*self
= (EmpathyTpChat
*) object
;
832 DEBUG ("Finalize: %p", object
);
834 if (self
->priv
->properties
) {
835 for (i
= 0; i
< self
->priv
->properties
->len
; i
++) {
836 EmpathyTpChatProperty
*property
;
838 property
= g_ptr_array_index (self
->priv
->properties
, i
);
839 g_free (property
->name
);
840 if (property
->value
) {
841 tp_g_value_slice_free (property
->value
);
843 g_slice_free (EmpathyTpChatProperty
, property
);
845 g_ptr_array_free (self
->priv
->properties
, TRUE
);
848 g_queue_free (self
->priv
->messages_queue
);
849 g_queue_free (self
->priv
->pending_messages_queue
);
850 g_hash_table_destroy (self
->priv
->messages_being_sent
);
852 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->finalize (object
);
856 check_almost_ready (EmpathyTpChat
*self
)
858 TpChannel
*channel
= (TpChannel
*) self
;
860 if (self
->priv
->ready_result
== NULL
)
863 if (self
->priv
->user
== NULL
)
866 /* We need either the members (room) or the remote contact (private chat).
867 * If the chat is protected by a password we can't get these information so
868 * consider the chat as ready so it can be presented to the user. */
869 if (!tp_channel_password_needed (channel
) && self
->priv
->members
== NULL
&&
870 self
->priv
->remote_contact
== NULL
)
873 g_assert (tp_proxy_is_prepared (self
,
874 TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES
));
876 tp_g_signal_connect_object (self
, "message-received",
877 G_CALLBACK (message_received_cb
), self
, 0);
878 tp_g_signal_connect_object (self
, "pending-message-removed",
879 G_CALLBACK (pending_message_removed_cb
), self
, 0);
881 list_pending_messages (self
);
883 tp_g_signal_connect_object (self
, "message-sent",
884 G_CALLBACK (message_sent_cb
), self
, 0);
886 tp_g_signal_connect_object (self
, "chat-state-changed",
887 G_CALLBACK (tp_chat_state_changed_cb
), self
, 0);
893 tp_chat_update_remote_contact (EmpathyTpChat
*self
)
895 TpChannel
*channel
= (TpChannel
*) self
;
896 EmpathyContact
*contact
= NULL
;
897 TpHandle self_handle
;
898 TpHandleType handle_type
;
901 /* If this is a named chatroom, never pretend it is a private chat */
902 tp_channel_get_handle (channel
, &handle_type
);
903 if (handle_type
== TP_HANDLE_TYPE_ROOM
) {
907 /* This is an MSN chat, but it's the new style where 1-1 chats don't
908 * have the group interface. If it has the conference interface, then
909 * it is indeed a MUC. */
910 if (tp_proxy_has_interface_by_id (self
,
911 TP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE
)) {
915 /* This is an MSN-like chat where anyone can join the chat at anytime.
916 * If there is only one non-self contact member, we are in a private
917 * chat and we set the "remote-contact" property to that contact. If
918 * there are more, set the "remote-contact" property to NULL and the
919 * UI will display a contact list. */
920 self_handle
= tp_channel_group_get_self_handle (channel
);
921 for (l
= self
->priv
->members
; l
; l
= l
->next
) {
922 /* Skip self contact if member */
923 if (empathy_contact_get_handle (l
->data
) == self_handle
) {
927 /* We have more than one remote contact, break */
928 if (contact
!= NULL
) {
933 /* If we didn't find yet a remote contact, keep this one */
937 if (self
->priv
->remote_contact
== contact
) {
941 DEBUG ("Changing remote contact from %p to %p",
942 self
->priv
->remote_contact
, contact
);
944 if (self
->priv
->remote_contact
) {
945 g_object_unref (self
->priv
->remote_contact
);
948 self
->priv
->remote_contact
= contact
? g_object_ref (contact
) : NULL
;
949 g_object_notify (G_OBJECT (self
), "remote-contact");
953 tp_chat_got_added_contacts_cb (TpConnection
*connection
,
955 EmpathyContact
* const * contacts
,
957 const TpHandle
*failed
,
962 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
964 const TpIntSet
*members
;
966 EmpathyContact
*contact
;
969 DEBUG ("Error: %s", error
->message
);
973 members
= tp_channel_group_get_members ((TpChannel
*) self
);
974 for (i
= 0; i
< n_contacts
; i
++) {
975 contact
= contacts
[i
];
976 handle
= empathy_contact_get_handle (contact
);
978 /* Make sure the contact is still member */
979 if (tp_intset_is_member (members
, handle
)) {
980 self
->priv
->members
= g_list_prepend (self
->priv
->members
,
981 g_object_ref (contact
));
982 g_signal_emit_by_name (chat
, "members-changed",
983 contact
, NULL
, 0, NULL
, TRUE
);
987 tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat
));
988 check_almost_ready (EMPATHY_TP_CHAT (chat
));
991 static EmpathyContact
*
992 chat_lookup_contact (EmpathyTpChat
*self
,
998 for (l
= self
->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 self
->priv
->members
= g_list_delete_link (self
->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 EmpathyTpChat
*self
= (EmpathyTpChat
*) 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 ((TpChannel
*) self
);
1072 handle
= empathy_contact_get_handle (new);
1074 old
= chat_lookup_contact (self
, rename_data
->old_handle
, TRUE
);
1076 /* Make sure the contact is still member */
1077 if (tp_intset_is_member (members
, handle
)) {
1078 self
->priv
->members
= g_list_prepend (self
->priv
->members
,
1079 g_object_ref (new));
1082 g_signal_emit_by_name (self
, "member-renamed",
1083 old
, new, rename_data
->reason
,
1084 rename_data
->message
);
1085 g_object_unref (old
);
1089 if (self
->priv
->user
== old
) {
1090 /* We change our nick */
1091 tp_clear_object (&self
->priv
->user
);
1092 self
->priv
->user
= g_object_ref (new);
1095 tp_chat_update_remote_contact (self
);
1096 check_almost_ready (self
);
1101 tp_chat_group_members_changed_cb (TpChannel
*channel
,
1105 GArray
*local_pending
,
1106 GArray
*remote_pending
,
1109 EmpathyTpChat
*self
)
1111 EmpathyContact
*contact
;
1112 EmpathyContact
*actor_contact
= NULL
;
1114 ContactRenameData
*rename_data
;
1115 TpHandle old_handle
;
1116 TpConnection
*connection
= tp_channel_borrow_connection (
1117 (TpChannel
*) self
);
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 if (removed
->len
!= 1 || added
->len
!= 1) {
1123 g_warning ("RENAMED with %u added, %u removed (expected 1, 1)",
1124 added
->len
, removed
->len
);
1128 old_handle
= g_array_index (removed
, guint
, 0);
1130 rename_data
= contact_rename_data_new (old_handle
, reason
, message
);
1131 empathy_tp_contact_factory_get_from_handles (connection
,
1132 added
->len
, (TpHandle
*) added
->data
,
1133 tp_chat_got_renamed_contacts_cb
,
1134 rename_data
, (GDestroyNotify
) contact_rename_data_free
,
1140 actor_contact
= chat_lookup_contact (self
, actor
, FALSE
);
1141 if (actor_contact
== NULL
) {
1142 /* FIXME: handle this a tad more gracefully: perhaps
1143 * the actor was a server op. We could use the
1144 * contact-ids detail of MembersChangedDetailed.
1146 DEBUG ("actor %u not a channel member", actor
);
1150 /* Remove contacts that are not members anymore */
1151 for (i
= 0; i
< removed
->len
; i
++) {
1152 contact
= chat_lookup_contact (self
,
1153 g_array_index (removed
, TpHandle
, i
), TRUE
);
1155 if (contact
!= NULL
) {
1156 g_signal_emit_by_name (self
, "members-changed", contact
,
1157 actor_contact
, reason
, message
,
1159 g_object_unref (contact
);
1163 /* Request added contacts */
1164 if (added
->len
> 0) {
1165 empathy_tp_contact_factory_get_from_handles (connection
,
1166 added
->len
, (TpHandle
*) added
->data
,
1167 tp_chat_got_added_contacts_cb
, NULL
, NULL
,
1171 tp_chat_update_remote_contact (self
);
1173 if (actor_contact
!= NULL
) {
1174 g_object_unref (actor_contact
);
1179 tp_chat_got_remote_contact_cb (TpConnection
*connection
,
1180 EmpathyContact
*contact
,
1181 const GError
*error
,
1185 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1188 DEBUG ("Error: %s", error
->message
);
1189 empathy_tp_chat_leave (self
, "");
1193 self
->priv
->remote_contact
= g_object_ref (contact
);
1194 g_object_notify (chat
, "remote-contact");
1196 check_almost_ready (self
);
1200 tp_chat_got_self_contact_cb (TpConnection
*connection
,
1201 EmpathyContact
*contact
,
1202 const GError
*error
,
1206 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1209 DEBUG ("Error: %s", error
->message
);
1210 empathy_tp_chat_leave (self
, "");
1214 self
->priv
->user
= g_object_ref (contact
);
1215 empathy_contact_set_is_user (self
->priv
->user
, TRUE
);
1216 check_almost_ready (self
);
1220 tp_chat_get_property (GObject
*object
,
1225 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
1229 g_value_set_object (value
, self
->priv
->account
);
1231 case PROP_REMOTE_CONTACT
:
1232 g_value_set_object (value
, self
->priv
->remote_contact
);
1234 case PROP_N_MESSAGES_SENDING
:
1235 g_value_set_uint (value
,
1236 g_hash_table_size (self
->priv
->messages_being_sent
));
1239 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1245 tp_chat_set_property (GObject
*object
,
1247 const GValue
*value
,
1250 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
1254 self
->priv
->account
= g_value_dup_object (value
);
1257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1267 static const TpProxyFeature
*
1268 tp_chat_list_features (TpProxyClass
*cls G_GNUC_UNUSED
)
1270 static TpProxyFeature features
[N_FEAT
+ 1] = { { 0 } };
1271 static GQuark need
[2] = {0, 0};
1273 if (G_LIKELY (features
[0].name
!= 0))
1276 features
[FEAT_READY
].name
= EMPATHY_TP_CHAT_FEATURE_READY
;
1277 need
[0] = TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES
;
1278 features
[FEAT_READY
].depends_on
= need
;
1279 features
[FEAT_READY
].prepare_async
=
1280 tp_chat_prepare_ready_async
;
1282 /* assert that the terminator at the end is there */
1283 g_assert (features
[N_FEAT
].name
== 0);
1289 empathy_tp_chat_class_init (EmpathyTpChatClass
*klass
)
1291 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1292 TpProxyClass
*proxy_class
= TP_PROXY_CLASS (klass
);
1294 object_class
->dispose
= tp_chat_dispose
;
1295 object_class
->finalize
= tp_chat_finalize
;
1296 object_class
->get_property
= tp_chat_get_property
;
1297 object_class
->set_property
= tp_chat_set_property
;
1299 proxy_class
->list_features
= tp_chat_list_features
;
1301 g_object_class_install_property (object_class
,
1303 g_param_spec_object ("account",
1305 "the account associated with the chat",
1308 G_PARAM_CONSTRUCT_ONLY
|
1309 G_PARAM_STATIC_STRINGS
));
1311 g_object_class_install_property (object_class
,
1312 PROP_REMOTE_CONTACT
,
1313 g_param_spec_object ("remote-contact",
1314 "The remote contact",
1315 "The remote contact if there is no group iface on the channel",
1316 EMPATHY_TYPE_CONTACT
,
1319 g_object_class_install_property (object_class
,
1320 PROP_N_MESSAGES_SENDING
,
1321 g_param_spec_uint ("n-messages-sending",
1322 "Num Messages Sending",
1323 "The number of messages being sent",
1328 signals
[MESSAGE_RECEIVED
] =
1329 g_signal_new ("message-received-empathy",
1330 G_TYPE_FROM_CLASS (klass
),
1334 g_cclosure_marshal_VOID__OBJECT
,
1336 1, EMPATHY_TYPE_MESSAGE
);
1338 signals
[SEND_ERROR
] =
1339 g_signal_new ("send-error",
1340 G_TYPE_FROM_CLASS (klass
),
1344 _empathy_marshal_VOID__STRING_UINT_STRING
,
1346 3, G_TYPE_STRING
, G_TYPE_UINT
, G_TYPE_STRING
);
1348 signals
[CHAT_STATE_CHANGED
] =
1349 g_signal_new ("chat-state-changed-empathy",
1350 G_TYPE_FROM_CLASS (klass
),
1354 _empathy_marshal_VOID__OBJECT_UINT
,
1356 2, EMPATHY_TYPE_CONTACT
, G_TYPE_UINT
);
1358 signals
[PROPERTY_CHANGED
] =
1359 g_signal_new ("property-changed",
1360 G_TYPE_FROM_CLASS (klass
),
1364 _empathy_marshal_VOID__STRING_BOXED
,
1366 2, G_TYPE_STRING
, G_TYPE_VALUE
);
1368 signals
[MESSAGE_ACKNOWLEDGED
] =
1369 g_signal_new ("message-acknowledged",
1370 G_TYPE_FROM_CLASS (klass
),
1374 g_cclosure_marshal_VOID__OBJECT
,
1376 1, EMPATHY_TYPE_MESSAGE
);
1378 g_type_class_add_private (object_class
, sizeof (EmpathyTpChatPrivate
));
1382 empathy_tp_chat_init (EmpathyTpChat
*self
)
1384 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
1385 EMPATHY_TYPE_TP_CHAT
, EmpathyTpChatPrivate
);
1387 self
->priv
->messages_queue
= g_queue_new ();
1388 self
->priv
->pending_messages_queue
= g_queue_new ();
1389 self
->priv
->messages_being_sent
= g_hash_table_new_full (
1390 g_str_hash
, g_str_equal
, g_free
, NULL
);
1394 tp_chat_iface_init (EmpathyContactListIface
*iface
)
1396 iface
->add
= tp_chat_add
;
1397 iface
->remove
= tp_chat_remove
;
1398 iface
->get_members
= tp_chat_get_members
;
1402 empathy_tp_chat_new (
1403 TpSimpleClientFactory
*factory
,
1406 const gchar
*object_path
,
1407 const GHashTable
*immutable_properties
)
1409 TpProxy
*conn_proxy
= (TpProxy
*) conn
;
1411 g_return_val_if_fail (TP_IS_ACCOUNT (account
), NULL
);
1412 g_return_val_if_fail (TP_IS_CONNECTION (conn
), NULL
);
1413 g_return_val_if_fail (immutable_properties
!= NULL
, NULL
);
1415 return g_object_new (EMPATHY_TYPE_TP_CHAT
,
1419 "dbus-daemon", conn_proxy
->dbus_daemon
,
1420 "bus-name", conn_proxy
->bus_name
,
1421 "object-path", object_path
,
1422 "channel-properties", immutable_properties
,
1427 empathy_tp_chat_get_id (EmpathyTpChat
*self
)
1431 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1433 id
= tp_channel_get_identifier ((TpChannel
*) self
);
1434 if (!EMP_STR_EMPTY (id
))
1436 else if (self
->priv
->remote_contact
)
1437 return empathy_contact_get_id (self
->priv
->remote_contact
);
1444 empathy_tp_chat_get_remote_contact (EmpathyTpChat
*self
)
1446 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1448 return self
->priv
->remote_contact
;
1452 empathy_tp_chat_get_account (EmpathyTpChat
*self
)
1454 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1456 return self
->priv
->account
;
1460 empathy_tp_chat_send (EmpathyTpChat
*self
,
1463 gchar
*message_body
;
1465 g_return_if_fail (EMPATHY_IS_TP_CHAT (self
));
1466 g_return_if_fail (TP_IS_CLIENT_MESSAGE (message
));
1468 message_body
= tp_message_to_text (message
, NULL
);
1470 DEBUG ("Sending message: %s", message_body
);
1472 tp_text_channel_send_message_async (TP_TEXT_CHANNEL (self
),
1473 message
, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY
,
1474 message_send_cb
, self
);
1476 g_free (message_body
);
1480 empathy_tp_chat_get_pending_messages (EmpathyTpChat
*self
)
1482 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1484 return self
->priv
->pending_messages_queue
->head
;
1488 empathy_tp_chat_acknowledge_message (EmpathyTpChat
*self
,
1489 EmpathyMessage
*message
) {
1492 g_return_if_fail (EMPATHY_IS_TP_CHAT (self
));
1494 if (!empathy_message_is_incoming (message
))
1497 tp_msg
= empathy_message_get_tp_message (message
);
1498 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self
),
1499 tp_msg
, NULL
, NULL
);
1503 * empathy_tp_chat_can_add_contact:
1505 * Returns: %TRUE if empathy_contact_list_add() will work for this channel.
1506 * That is if this chat is a 1-to-1 channel that can be upgraded to
1507 * a MUC using the Conference interface or if the channel is a MUC.
1510 empathy_tp_chat_can_add_contact (EmpathyTpChat
*self
)
1512 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), FALSE
);
1514 return self
->priv
->can_upgrade_to_muc
||
1515 tp_proxy_has_interface_by_id (self
,
1516 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
);;
1520 tp_channel_leave_async_cb (GObject
*source_object
,
1524 GError
*error
= NULL
;
1526 if (!tp_channel_leave_finish (TP_CHANNEL (source_object
), res
, &error
)) {
1527 DEBUG ("Could not leave channel properly: (%s); closing the channel",
1529 g_error_free (error
);
1534 empathy_tp_chat_leave (EmpathyTpChat
*self
,
1535 const gchar
*message
)
1537 TpChannel
*channel
= (TpChannel
*) self
;
1539 DEBUG ("Leaving channel %s with message \"%s\"",
1540 tp_channel_get_identifier (channel
), message
);
1542 tp_channel_leave_async (channel
, TP_CHANNEL_GROUP_CHANGE_REASON_NONE
,
1543 message
, tp_channel_leave_async_cb
, self
);
1547 add_members_cb (TpChannel
*proxy
,
1548 const GError
*error
,
1550 GObject
*weak_object
)
1552 EmpathyTpChat
*self
= (EmpathyTpChat
*) weak_object
;
1554 if (error
!= NULL
) {
1555 DEBUG ("Failed to join chat (%s): %s",
1556 tp_channel_get_identifier ((TpChannel
*) self
), error
->message
);
1561 empathy_tp_chat_join (EmpathyTpChat
*self
)
1563 TpHandle self_handle
;
1566 self_handle
= tp_channel_group_get_self_handle ((TpChannel
*) self
);
1568 members
= g_array_sized_new (FALSE
, FALSE
, sizeof (TpHandle
), 1);
1569 g_array_append_val (members
, self_handle
);
1571 tp_cli_channel_interface_group_call_add_members ((TpChannel
*) self
, -1, members
,
1572 "", add_members_cb
, NULL
, NULL
, G_OBJECT (self
));
1574 g_array_free (members
, TRUE
);
1578 empathy_tp_chat_is_invited (EmpathyTpChat
*self
,
1581 TpHandle self_handle
;
1583 if (!tp_proxy_has_interface (self
, TP_IFACE_CHANNEL_INTERFACE_GROUP
))
1586 self_handle
= tp_channel_group_get_self_handle ((TpChannel
*) self
);
1587 if (self_handle
== 0)
1590 return tp_channel_group_get_local_pending_info ((TpChannel
*) self
, self_handle
,
1591 inviter
, NULL
, NULL
);
1595 empathy_tp_chat_get_chat_state (EmpathyTpChat
*self
,
1596 EmpathyContact
*contact
)
1598 return tp_channel_get_chat_state ((TpChannel
*) self
,
1599 empathy_contact_get_handle (contact
));
1603 empathy_tp_chat_get_self_contact (EmpathyTpChat
*self
)
1605 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1607 return self
->priv
->user
;
1611 empathy_tp_chat_get_feature_ready (void)
1613 return g_quark_from_static_string ("empathy-tp-chat-feature-ready");
1617 tp_chat_prepare_ready_async (TpProxy
*proxy
,
1618 const TpProxyFeature
*feature
,
1619 GAsyncReadyCallback callback
,
1622 EmpathyTpChat
*self
= (EmpathyTpChat
*) proxy
;
1623 TpChannel
*channel
= (TpChannel
*) proxy
;
1624 TpConnection
*connection
;
1626 g_assert (self
->priv
->ready_result
== NULL
);
1627 self
->priv
->ready_result
= g_simple_async_result_new (G_OBJECT (self
),
1628 callback
, user_data
, tp_chat_prepare_ready_async
);
1630 connection
= tp_channel_borrow_connection (channel
);
1632 if (tp_proxy_has_interface_by_id (self
,
1633 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
1634 const TpIntSet
*members
;
1638 /* Get self contact from the group's self handle */
1639 handle
= tp_channel_group_get_self_handle (channel
);
1640 empathy_tp_contact_factory_get_from_handle (connection
,
1641 handle
, tp_chat_got_self_contact_cb
,
1642 NULL
, NULL
, G_OBJECT (self
));
1644 /* Get initial member contacts */
1645 members
= tp_channel_group_get_members (channel
);
1646 handles
= tp_intset_to_array (members
);
1647 empathy_tp_contact_factory_get_from_handles (connection
,
1648 handles
->len
, (TpHandle
*) handles
->data
,
1649 tp_chat_got_added_contacts_cb
, NULL
, NULL
, G_OBJECT (self
));
1651 self
->priv
->can_upgrade_to_muc
= FALSE
;
1653 tp_g_signal_connect_object (self
, "group-members-changed",
1654 G_CALLBACK (tp_chat_group_members_changed_cb
), self
, 0);
1656 TpCapabilities
*caps
;
1661 /* Get the self contact from the connection's self handle */
1662 handle
= tp_connection_get_self_handle (connection
);
1663 empathy_tp_contact_factory_get_from_handle (connection
,
1664 handle
, tp_chat_got_self_contact_cb
,
1665 NULL
, NULL
, G_OBJECT (self
));
1667 /* Get the remote contact */
1668 handle
= tp_channel_get_handle (channel
, NULL
);
1669 empathy_tp_contact_factory_get_from_handle (connection
,
1670 handle
, tp_chat_got_remote_contact_cb
,
1671 NULL
, NULL
, G_OBJECT (self
));
1673 caps
= tp_connection_get_capabilities (connection
);
1674 g_assert (caps
!= NULL
);
1676 classes
= tp_capabilities_get_channel_classes (caps
);
1678 for (i
= 0; i
< classes
->len
; i
++) {
1679 GValueArray
*array
= g_ptr_array_index (classes
, i
);
1680 const char **oprops
= g_value_get_boxed (
1681 g_value_array_get_nth (array
, 1));
1683 if (tp_strv_contains (oprops
, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS
)) {
1684 self
->priv
->can_upgrade_to_muc
= TRUE
;
1690 if (tp_proxy_has_interface_by_id (self
,
1691 TP_IFACE_QUARK_PROPERTIES_INTERFACE
)) {
1692 tp_cli_properties_interface_call_list_properties (channel
, -1,
1693 tp_chat_list_properties_cb
,
1696 tp_cli_properties_interface_connect_to_properties_changed (channel
,
1697 tp_chat_properties_changed_cb
,
1699 G_OBJECT (self
), NULL
);
1700 tp_cli_properties_interface_connect_to_property_flags_changed (channel
,
1701 tp_chat_property_flags_changed_cb
,
1703 G_OBJECT (self
), NULL
);