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 TpChannelPasswordFlags password_flags
;
53 /* TRUE if we fetched the password flag of the channel or if it's not needed
54 * (channel doesn't implement the Password interface) */
55 gboolean got_password_flags
;
56 gboolean can_upgrade_to_muc
;
57 gboolean got_sms_channel
;
60 GHashTable
*messages_being_sent
;
62 /* GSimpleAsyncResult used when preparing EMPATHY_TP_CHAT_FEATURE_CORE */
63 GSimpleAsyncResult
*ready_result
;
66 static void tp_chat_iface_init (EmpathyContactListIface
*iface
);
74 PROP_N_MESSAGES_SENDING
,
86 static guint signals
[LAST_SIGNAL
];
88 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat
, empathy_tp_chat
, TP_TYPE_TEXT_CHANNEL
,
89 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST
,
93 tp_chat_set_delivery_status (EmpathyTpChat
*self
,
95 EmpathyDeliveryStatus delivery_status
)
97 TpDeliveryReportingSupportFlags flags
=
98 tp_text_channel_get_delivery_reporting_support (
99 TP_TEXT_CHANNEL (self
));
101 /* channel must support receiving failures and successes */
102 if (!tp_str_empty (token
) &&
103 flags
& TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES
&&
104 flags
& TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES
) {
106 DEBUG ("Delivery status (%s) = %u", token
, delivery_status
);
108 switch (delivery_status
) {
109 case EMPATHY_DELIVERY_STATUS_NONE
:
110 g_hash_table_remove (self
->priv
->messages_being_sent
,
115 g_hash_table_insert (self
->priv
->messages_being_sent
,
117 GUINT_TO_POINTER (delivery_status
));
121 g_object_notify (G_OBJECT (self
), "n-messages-sending");
125 static void tp_chat_prepare_ready_async (TpProxy
*proxy
,
126 const TpProxyFeature
*feature
,
127 GAsyncReadyCallback callback
,
131 tp_chat_async_cb (TpChannel
*proxy
,
134 GObject
*weak_object
)
137 DEBUG ("Error %s: %s", (gchar
*) user_data
, error
->message
);
142 create_conference_cb (GObject
*source
,
143 GAsyncResult
*result
,
146 GError
*error
= NULL
;
148 if (!tp_account_channel_request_create_channel_finish (
149 TP_ACCOUNT_CHANNEL_REQUEST (source
), result
, &error
)) {
150 DEBUG ("Failed to create conference channel: %s", error
->message
);
151 g_error_free (error
);
156 tp_chat_add (EmpathyContactList
*list
,
157 EmpathyContact
*contact
,
158 const gchar
*message
)
160 EmpathyTpChat
*self
= (EmpathyTpChat
*) list
;
161 TpChannel
*channel
= (TpChannel
*) self
;
163 if (tp_proxy_has_interface_by_id (self
,
164 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
166 GArray handles
= {(gchar
*) &handle
, 1};
168 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
169 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
171 handle
= empathy_contact_get_handle (contact
);
172 tp_cli_channel_interface_group_call_add_members (channel
,
173 -1, &handles
, NULL
, NULL
, NULL
, NULL
, NULL
);
174 } else if (self
->priv
->can_upgrade_to_muc
) {
175 TpAccountChannelRequest
*req
;
177 const char *object_path
;
178 GPtrArray channels
= { (gpointer
*) &object_path
, 1 };
179 const char *invitees
[2] = { NULL
, };
181 invitees
[0] = empathy_contact_get_id (contact
);
182 object_path
= tp_proxy_get_object_path (self
);
185 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
186 TP_IFACE_CHANNEL_TYPE_TEXT
,
187 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
189 TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS
,
190 TP_ARRAY_TYPE_OBJECT_PATH_LIST
, &channels
,
191 TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS
,
192 G_TYPE_STRV
, invitees
,
193 /* FIXME: InvitationMessage ? */
196 req
= tp_account_channel_request_new (self
->priv
->account
, props
,
197 TP_USER_ACTION_TIME_NOT_USER_ACTION
);
199 /* Although this is a MUC, it's anonymous, so CreateChannel is
201 tp_account_channel_request_create_channel_async (req
, EMPATHY_CHAT_BUS_NAME
,
202 NULL
, create_conference_cb
, NULL
);
204 g_object_unref (req
);
205 g_hash_table_unref (props
);
207 g_warning ("Cannot add to this channel");
212 tp_chat_remove (EmpathyContactList
*list
,
213 EmpathyContact
*contact
,
214 const gchar
*message
)
216 EmpathyTpChat
*self
= (EmpathyTpChat
*) list
;
218 GArray handles
= {(gchar
*) &handle
, 1};
220 g_return_if_fail (EMPATHY_IS_TP_CHAT (list
));
221 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
223 handle
= empathy_contact_get_handle (contact
);
224 tp_cli_channel_interface_group_call_remove_members ((TpChannel
*) self
, -1,
231 tp_chat_get_members (EmpathyContactList
*list
)
233 EmpathyTpChat
*self
= (EmpathyTpChat
*) list
;
234 GList
*members
= NULL
;
236 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list
), NULL
);
238 if (self
->priv
->members
) {
239 members
= g_list_copy (self
->priv
->members
);
240 g_list_foreach (members
, (GFunc
) g_object_ref
, NULL
);
242 members
= g_list_prepend (members
, g_object_ref (self
->priv
->user
));
243 if (self
->priv
->remote_contact
!= NULL
)
244 members
= g_list_prepend (members
, g_object_ref (self
->priv
->remote_contact
));
251 check_ready (EmpathyTpChat
*self
)
253 if (self
->priv
->ready_result
== NULL
)
256 if (g_queue_get_length (self
->priv
->messages_queue
) > 0)
261 g_simple_async_result_complete (self
->priv
->ready_result
);
262 tp_clear_object (&self
->priv
->ready_result
);
266 tp_chat_emit_queued_messages (EmpathyTpChat
*self
)
268 EmpathyMessage
*message
;
270 /* Check if we can now emit some queued messages */
271 while ((message
= g_queue_peek_head (self
->priv
->messages_queue
)) != NULL
) {
272 if (empathy_message_get_sender (message
) == NULL
) {
276 DEBUG ("Queued message ready");
277 g_queue_pop_head (self
->priv
->messages_queue
);
278 g_queue_push_tail (self
->priv
->pending_messages_queue
, message
);
279 g_signal_emit (self
, signals
[MESSAGE_RECEIVED
], 0, message
);
286 tp_chat_got_sender_cb (TpConnection
*connection
,
287 EmpathyContact
*contact
,
292 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
295 DEBUG ("Error: %s", error
->message
);
296 /* Do not block the message queue, just drop this message */
297 g_queue_remove (self
->priv
->messages_queue
, message
);
299 empathy_message_set_sender (message
, contact
);
302 tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (self
));
306 tp_chat_build_message (EmpathyTpChat
*self
,
310 EmpathyMessage
*message
;
313 message
= empathy_message_new_from_tp_message (msg
, incoming
);
314 /* FIXME: this is actually a lie for incoming messages. */
315 empathy_message_set_receiver (message
, self
->priv
->user
);
317 g_queue_push_tail (self
->priv
->messages_queue
, message
);
319 sender
= tp_signalled_message_get_sender (msg
);
320 g_assert (sender
!= NULL
);
322 if (tp_contact_get_handle (sender
) == 0) {
323 empathy_message_set_sender (message
, self
->priv
->user
);
324 tp_chat_emit_queued_messages (self
);
326 TpConnection
*connection
= tp_channel_borrow_connection (
329 empathy_tp_contact_factory_get_from_handle (connection
,
330 tp_contact_get_handle (sender
),
331 tp_chat_got_sender_cb
,
332 message
, NULL
, G_OBJECT (self
));
337 handle_delivery_report (EmpathyTpChat
*self
,
340 TpDeliveryStatus delivery_status
;
341 const GHashTable
*header
;
342 TpChannelTextSendError delivery_error
;
345 const gchar
*message_body
= NULL
;
346 const gchar
*delivery_dbus_error
;
347 const gchar
*delivery_token
= NULL
;
349 header
= tp_message_peek (message
, 0);
353 delivery_token
= tp_asv_get_string (header
, "delivery-token");
354 delivery_status
= tp_asv_get_uint32 (header
, "delivery-status", &valid
);
358 } else if (delivery_status
== TP_DELIVERY_STATUS_ACCEPTED
) {
359 DEBUG ("Accepted %s", delivery_token
);
360 tp_chat_set_delivery_status (self
, delivery_token
,
361 EMPATHY_DELIVERY_STATUS_ACCEPTED
);
363 } else if (delivery_status
== TP_DELIVERY_STATUS_DELIVERED
) {
364 DEBUG ("Delivered %s", delivery_token
);
365 tp_chat_set_delivery_status (self
, delivery_token
,
366 EMPATHY_DELIVERY_STATUS_NONE
);
368 } else if (delivery_status
!= TP_DELIVERY_STATUS_PERMANENTLY_FAILED
) {
372 delivery_error
= tp_asv_get_uint32 (header
, "delivery-error", &valid
);
374 delivery_error
= TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
;
376 delivery_dbus_error
= tp_asv_get_string (header
, "delivery-dbus-error");
378 /* TODO: ideally we should use tp-glib API giving us the echoed message as a
379 * TpMessage. (fdo #35884) */
380 echo
= tp_asv_get_boxed (header
, "delivery-echo",
381 TP_ARRAY_TYPE_MESSAGE_PART_LIST
);
382 if (echo
!= NULL
&& echo
->len
>= 1) {
383 const GHashTable
*echo_body
;
385 echo_body
= g_ptr_array_index (echo
, 1);
386 if (echo_body
!= NULL
)
387 message_body
= tp_asv_get_string (echo_body
, "content");
390 tp_chat_set_delivery_status (self
, delivery_token
,
391 EMPATHY_DELIVERY_STATUS_NONE
);
392 g_signal_emit (self
, signals
[SEND_ERROR
], 0, message_body
,
393 delivery_error
, delivery_dbus_error
);
396 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self
),
397 message
, NULL
, NULL
);
401 handle_incoming_message (EmpathyTpChat
*self
,
407 if (tp_message_is_delivery_report (message
)) {
408 handle_delivery_report (self
, message
);
412 message_body
= tp_message_to_text (message
, NULL
);
414 DEBUG ("Message %s (channel %s): %s",
415 pending
? "pending" : "received",
416 tp_proxy_get_object_path (self
), message_body
);
418 if (message_body
== NULL
) {
419 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
421 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self
),
422 message
, NULL
, NULL
);
426 tp_chat_build_message (self
, message
, TRUE
);
428 g_free (message_body
);
432 message_received_cb (TpTextChannel
*channel
,
436 handle_incoming_message (self
, message
, FALSE
);
440 find_pending_message_func (gconstpointer a
,
443 EmpathyMessage
*msg
= (EmpathyMessage
*) a
;
444 TpMessage
*message
= (TpMessage
*) b
;
446 if (empathy_message_get_tp_message (msg
) == message
)
453 pending_message_removed_cb (TpTextChannel
*channel
,
459 m
= g_queue_find_custom (self
->priv
->pending_messages_queue
, message
,
460 find_pending_message_func
);
465 g_signal_emit (self
, signals
[MESSAGE_ACKNOWLEDGED
], 0, m
->data
);
467 g_object_unref (m
->data
);
468 g_queue_delete_link (self
->priv
->pending_messages_queue
, m
);
472 message_sent_cb (TpTextChannel
*channel
,
474 TpMessageSendingFlags flags
,
480 message_body
= tp_message_to_text (message
, NULL
);
482 DEBUG ("Message sent: %s", message_body
);
484 tp_chat_build_message (self
, message
, FALSE
);
486 g_free (message_body
);
489 static TpChannelTextSendError
490 error_to_text_send_error (GError
*error
)
492 if (error
->domain
!= TP_ERRORS
)
493 return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
;
495 switch (error
->code
) {
496 case TP_ERROR_OFFLINE
:
497 return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE
;
498 case TP_ERROR_INVALID_HANDLE
:
499 return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT
;
500 case TP_ERROR_PERMISSION_DENIED
:
501 return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED
;
502 case TP_ERROR_NOT_IMPLEMENTED
:
503 return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED
;
506 return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN
;
510 message_send_cb (GObject
*source
,
511 GAsyncResult
*result
,
514 EmpathyTpChat
*self
= user_data
;
515 TpTextChannel
*channel
= (TpTextChannel
*) source
;
517 GError
*error
= NULL
;
519 if (!tp_text_channel_send_message_finish (channel
, result
, &token
, &error
)) {
520 DEBUG ("Error: %s", error
->message
);
522 /* FIXME: we should use the body of the message as first argument of the
523 * signal but can't easily get it as we just get a user_data pointer. Once
524 * we'll have rebased EmpathyTpChat on top of TpTextChannel we'll be able
525 * to use the user_data pointer to pass the message and fix this. */
526 g_signal_emit (self
, signals
[SEND_ERROR
], 0,
527 NULL
, error_to_text_send_error (error
), NULL
);
529 g_error_free (error
);
532 tp_chat_set_delivery_status (self
, token
,
533 EMPATHY_DELIVERY_STATUS_SENDING
);
539 TpChannelChatState state
;
543 tp_chat_state_changed_got_contact_cb (TpConnection
*connection
,
544 EmpathyContact
*contact
,
549 TpChannelChatState state
;
552 DEBUG ("Error: %s", error
->message
);
556 state
= GPOINTER_TO_UINT (user_data
);
557 DEBUG ("Chat state changed for %s (%d): %d",
558 empathy_contact_get_alias (contact
),
559 empathy_contact_get_handle (contact
), state
);
561 g_signal_emit (chat
, signals
[CHAT_STATE_CHANGED
], 0, contact
, state
);
565 tp_chat_state_changed_cb (TpChannel
*channel
,
567 TpChannelChatState state
,
570 TpConnection
*connection
= tp_channel_borrow_connection (
573 empathy_tp_contact_factory_get_from_handle (connection
, handle
,
574 tp_chat_state_changed_got_contact_cb
, GUINT_TO_POINTER (state
),
575 NULL
, G_OBJECT (self
));
579 list_pending_messages (EmpathyTpChat
*self
)
583 messages
= tp_text_channel_get_pending_messages (
584 TP_TEXT_CHANNEL (self
));
586 for (l
= messages
; l
!= NULL
; l
= g_list_next (l
)) {
587 TpMessage
*message
= l
->data
;
589 handle_incoming_message (self
, message
, FALSE
);
592 g_list_free (messages
);
596 tp_chat_property_flags_changed_cb (TpProxy
*proxy
,
597 const GPtrArray
*properties
,
601 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
604 if (!self
->priv
->had_properties_list
|| !properties
) {
608 for (i
= 0; i
< properties
->len
; i
++) {
609 GValueArray
*prop_struct
;
610 EmpathyTpChatProperty
*property
;
614 prop_struct
= g_ptr_array_index (properties
, i
);
615 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
616 flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 1));
618 for (j
= 0; j
< self
->priv
->properties
->len
; j
++) {
619 property
= g_ptr_array_index (self
->priv
->properties
, j
);
620 if (property
->id
== id
) {
621 property
->flags
= flags
;
622 DEBUG ("property %s flags changed: %d",
623 property
->name
, property
->flags
);
631 tp_chat_properties_changed_cb (TpProxy
*proxy
,
632 const GPtrArray
*properties
,
636 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
639 if (!self
->priv
->had_properties_list
|| !properties
) {
643 for (i
= 0; i
< properties
->len
; i
++) {
644 GValueArray
*prop_struct
;
645 EmpathyTpChatProperty
*property
;
649 prop_struct
= g_ptr_array_index (properties
, i
);
650 id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
651 src_value
= g_value_get_boxed (g_value_array_get_nth (prop_struct
, 1));
653 for (j
= 0; j
< self
->priv
->properties
->len
; j
++) {
654 property
= g_ptr_array_index (self
->priv
->properties
, j
);
655 if (property
->id
== id
) {
656 if (property
->value
) {
657 g_value_copy (src_value
, property
->value
);
659 property
->value
= tp_g_value_slice_dup (src_value
);
662 DEBUG ("property %s changed", property
->name
);
663 g_signal_emit (chat
, signals
[PROPERTY_CHANGED
], 0,
664 property
->name
, property
->value
);
672 tp_chat_get_properties_cb (TpProxy
*proxy
,
673 const GPtrArray
*properties
,
679 DEBUG ("Error getting properties: %s", error
->message
);
683 tp_chat_properties_changed_cb (proxy
, properties
, user_data
, chat
);
687 tp_chat_list_properties_cb (TpProxy
*proxy
,
688 const GPtrArray
*properties
,
693 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
697 self
->priv
->had_properties_list
= TRUE
;
700 DEBUG ("Error listing properties: %s", error
->message
);
704 ids
= g_array_sized_new (FALSE
, FALSE
, sizeof (guint
), properties
->len
);
705 self
->priv
->properties
= g_ptr_array_sized_new (properties
->len
);
706 for (i
= 0; i
< properties
->len
; i
++) {
707 GValueArray
*prop_struct
;
708 EmpathyTpChatProperty
*property
;
710 prop_struct
= g_ptr_array_index (properties
, i
);
711 property
= g_slice_new0 (EmpathyTpChatProperty
);
712 property
->id
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 0));
713 property
->name
= g_value_dup_string (g_value_array_get_nth (prop_struct
, 1));
714 property
->flags
= g_value_get_uint (g_value_array_get_nth (prop_struct
, 3));
716 DEBUG ("Adding property name=%s id=%d flags=%d",
717 property
->name
, property
->id
, property
->flags
);
718 g_ptr_array_add (self
->priv
->properties
, property
);
719 if (property
->flags
& TP_PROPERTY_FLAG_READ
) {
720 g_array_append_val (ids
, property
->id
);
724 tp_cli_properties_interface_call_get_properties (proxy
, -1,
726 tp_chat_get_properties_cb
,
730 g_array_free (ids
, TRUE
);
734 empathy_tp_chat_set_property (EmpathyTpChat
*self
,
738 EmpathyTpChatProperty
*property
;
741 if (!self
->priv
->had_properties_list
) {
745 for (i
= 0; i
< self
->priv
->properties
->len
; i
++) {
746 property
= g_ptr_array_index (self
->priv
->properties
, i
);
747 if (!tp_strdiff (property
->name
, name
)) {
748 GPtrArray
*properties
;
751 GValue dest_value
= {0, };
753 if (!(property
->flags
& TP_PROPERTY_FLAG_WRITE
)) {
757 g_value_init (&id
, G_TYPE_UINT
);
758 g_value_init (&dest_value
, G_TYPE_VALUE
);
759 g_value_set_uint (&id
, property
->id
);
760 g_value_set_boxed (&dest_value
, value
);
762 prop
= g_value_array_new (2);
763 g_value_array_append (prop
, &id
);
764 g_value_array_append (prop
, &dest_value
);
766 properties
= g_ptr_array_sized_new (1);
767 g_ptr_array_add (properties
, prop
);
769 DEBUG ("Set property %s", name
);
770 tp_cli_properties_interface_call_set_properties (self
, -1,
772 (tp_cli_properties_interface_callback_for_set_properties
)
774 "Seting property", NULL
,
777 g_ptr_array_free (properties
, TRUE
);
778 g_value_array_free (prop
);
785 EmpathyTpChatProperty
*
786 empathy_tp_chat_get_property (EmpathyTpChat
*self
,
789 EmpathyTpChatProperty
*property
;
792 if (!self
->priv
->had_properties_list
) {
796 for (i
= 0; i
< self
->priv
->properties
->len
; i
++) {
797 property
= g_ptr_array_index (self
->priv
->properties
, i
);
798 if (!tp_strdiff (property
->name
, name
)) {
807 empathy_tp_chat_get_properties (EmpathyTpChat
*self
)
809 return self
->priv
->properties
;
813 tp_chat_dispose (GObject
*object
)
815 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
817 tp_clear_object (&self
->priv
->account
);
818 tp_clear_object (&self
->priv
->remote_contact
);
819 tp_clear_object (&self
->priv
->user
);
821 g_queue_foreach (self
->priv
->messages_queue
, (GFunc
) g_object_unref
, NULL
);
822 g_queue_clear (self
->priv
->messages_queue
);
824 g_queue_foreach (self
->priv
->pending_messages_queue
,
825 (GFunc
) g_object_unref
, NULL
);
826 g_queue_clear (self
->priv
->pending_messages_queue
);
828 tp_clear_object (&self
->priv
->ready_result
);
830 if (G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose
)
831 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->dispose (object
);
835 tp_chat_finalize (GObject
*object
)
837 EmpathyTpChat
*self
= (EmpathyTpChat
*) object
;
840 DEBUG ("Finalize: %p", object
);
842 if (self
->priv
->properties
) {
843 for (i
= 0; i
< self
->priv
->properties
->len
; i
++) {
844 EmpathyTpChatProperty
*property
;
846 property
= g_ptr_array_index (self
->priv
->properties
, i
);
847 g_free (property
->name
);
848 if (property
->value
) {
849 tp_g_value_slice_free (property
->value
);
851 g_slice_free (EmpathyTpChatProperty
, property
);
853 g_ptr_array_free (self
->priv
->properties
, TRUE
);
856 g_queue_free (self
->priv
->messages_queue
);
857 g_queue_free (self
->priv
->pending_messages_queue
);
858 g_hash_table_destroy (self
->priv
->messages_being_sent
);
860 G_OBJECT_CLASS (empathy_tp_chat_parent_class
)->finalize (object
);
864 check_almost_ready (EmpathyTpChat
*self
)
866 if (self
->priv
->ready_result
== NULL
)
869 if (self
->priv
->user
== NULL
)
872 if (!self
->priv
->got_password_flags
)
875 if (!self
->priv
->got_sms_channel
)
878 /* We need either the members (room) or the remote contact (private chat).
879 * If the chat is protected by a password we can't get these information so
880 * consider the chat as ready so it can be presented to the user. */
881 if (!empathy_tp_chat_password_needed (self
) && self
->priv
->members
== NULL
&&
882 self
->priv
->remote_contact
== NULL
)
885 g_assert (tp_proxy_is_prepared (self
,
886 TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES
));
888 tp_g_signal_connect_object (self
, "message-received",
889 G_CALLBACK (message_received_cb
), self
, 0);
890 tp_g_signal_connect_object (self
, "pending-message-removed",
891 G_CALLBACK (pending_message_removed_cb
), self
, 0);
893 list_pending_messages (self
);
895 tp_g_signal_connect_object (self
, "message-sent",
896 G_CALLBACK (message_sent_cb
), self
, 0);
898 tp_g_signal_connect_object (self
, "chat-state-changed",
899 G_CALLBACK (tp_chat_state_changed_cb
), self
, 0);
905 tp_chat_update_remote_contact (EmpathyTpChat
*self
)
907 TpChannel
*channel
= (TpChannel
*) self
;
908 EmpathyContact
*contact
= NULL
;
909 TpHandle self_handle
;
910 TpHandleType handle_type
;
913 /* If this is a named chatroom, never pretend it is a private chat */
914 tp_channel_get_handle (channel
, &handle_type
);
915 if (handle_type
== TP_HANDLE_TYPE_ROOM
) {
919 /* This is an MSN chat, but it's the new style where 1-1 chats don't
920 * have the group interface. If it has the conference interface, then
921 * it is indeed a MUC. */
922 if (tp_proxy_has_interface_by_id (self
,
923 TP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE
)) {
927 /* This is an MSN-like chat where anyone can join the chat at anytime.
928 * If there is only one non-self contact member, we are in a private
929 * chat and we set the "remote-contact" property to that contact. If
930 * there are more, set the "remote-contact" property to NULL and the
931 * UI will display a contact list. */
932 self_handle
= tp_channel_group_get_self_handle (channel
);
933 for (l
= self
->priv
->members
; l
; l
= l
->next
) {
934 /* Skip self contact if member */
935 if (empathy_contact_get_handle (l
->data
) == self_handle
) {
939 /* We have more than one remote contact, break */
940 if (contact
!= NULL
) {
945 /* If we didn't find yet a remote contact, keep this one */
949 if (self
->priv
->remote_contact
== contact
) {
953 DEBUG ("Changing remote contact from %p to %p",
954 self
->priv
->remote_contact
, contact
);
956 if (self
->priv
->remote_contact
) {
957 g_object_unref (self
->priv
->remote_contact
);
960 self
->priv
->remote_contact
= contact
? g_object_ref (contact
) : NULL
;
961 g_object_notify (G_OBJECT (self
), "remote-contact");
965 tp_chat_got_added_contacts_cb (TpConnection
*connection
,
967 EmpathyContact
* const * contacts
,
969 const TpHandle
*failed
,
974 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
976 const TpIntSet
*members
;
978 EmpathyContact
*contact
;
981 DEBUG ("Error: %s", error
->message
);
985 members
= tp_channel_group_get_members ((TpChannel
*) self
);
986 for (i
= 0; i
< n_contacts
; i
++) {
987 contact
= contacts
[i
];
988 handle
= empathy_contact_get_handle (contact
);
990 /* Make sure the contact is still member */
991 if (tp_intset_is_member (members
, handle
)) {
992 self
->priv
->members
= g_list_prepend (self
->priv
->members
,
993 g_object_ref (contact
));
994 g_signal_emit_by_name (chat
, "members-changed",
995 contact
, NULL
, 0, NULL
, TRUE
);
999 tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat
));
1000 check_almost_ready (EMPATHY_TP_CHAT (chat
));
1003 static EmpathyContact
*
1004 chat_lookup_contact (EmpathyTpChat
*self
,
1010 for (l
= self
->priv
->members
; l
; l
= l
->next
) {
1011 EmpathyContact
*c
= l
->data
;
1013 if (empathy_contact_get_handle (c
) != handle
) {
1018 /* Caller takes the reference. */
1019 self
->priv
->members
= g_list_delete_link (self
->priv
->members
, l
);
1032 TpHandle old_handle
;
1035 } ContactRenameData
;
1037 static ContactRenameData
*
1038 contact_rename_data_new (TpHandle handle
,
1040 const gchar
* message
)
1042 ContactRenameData
*data
= g_new (ContactRenameData
, 1);
1043 data
->old_handle
= handle
;
1044 data
->reason
= reason
;
1045 data
->message
= g_strdup (message
);
1051 contact_rename_data_free (ContactRenameData
* data
)
1053 g_free (data
->message
);
1058 tp_chat_got_renamed_contacts_cb (TpConnection
*connection
,
1060 EmpathyContact
* const * contacts
,
1062 const TpHandle
*failed
,
1063 const GError
*error
,
1067 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1068 const TpIntSet
*members
;
1070 EmpathyContact
*old
= NULL
, *new = NULL
;
1071 ContactRenameData
*rename_data
= (ContactRenameData
*) user_data
;
1074 DEBUG ("Error: %s", error
->message
);
1078 /* renamed members can only be delivered one at a time */
1079 g_warn_if_fail (n_contacts
== 1);
1083 members
= tp_channel_group_get_members ((TpChannel
*) self
);
1084 handle
= empathy_contact_get_handle (new);
1086 old
= chat_lookup_contact (self
, rename_data
->old_handle
, TRUE
);
1088 /* Make sure the contact is still member */
1089 if (tp_intset_is_member (members
, handle
)) {
1090 self
->priv
->members
= g_list_prepend (self
->priv
->members
,
1091 g_object_ref (new));
1094 g_signal_emit_by_name (self
, "member-renamed",
1095 old
, new, rename_data
->reason
,
1096 rename_data
->message
);
1097 g_object_unref (old
);
1101 if (self
->priv
->user
== old
) {
1102 /* We change our nick */
1103 tp_clear_object (&self
->priv
->user
);
1104 self
->priv
->user
= g_object_ref (new);
1107 tp_chat_update_remote_contact (self
);
1108 check_almost_ready (self
);
1113 tp_chat_group_members_changed_cb (TpChannel
*channel
,
1117 GArray
*local_pending
,
1118 GArray
*remote_pending
,
1121 EmpathyTpChat
*self
)
1123 EmpathyContact
*contact
;
1124 EmpathyContact
*actor_contact
= NULL
;
1126 ContactRenameData
*rename_data
;
1127 TpHandle old_handle
;
1128 TpConnection
*connection
= tp_channel_borrow_connection (
1129 (TpChannel
*) self
);
1131 /* Contact renamed */
1132 if (reason
== TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED
) {
1133 /* there can only be a single 'added' and a single 'removed' handle */
1134 if (removed
->len
!= 1 || added
->len
!= 1) {
1135 g_warning ("RENAMED with %u added, %u removed (expected 1, 1)",
1136 added
->len
, removed
->len
);
1140 old_handle
= g_array_index (removed
, guint
, 0);
1142 rename_data
= contact_rename_data_new (old_handle
, reason
, message
);
1143 empathy_tp_contact_factory_get_from_handles (connection
,
1144 added
->len
, (TpHandle
*) added
->data
,
1145 tp_chat_got_renamed_contacts_cb
,
1146 rename_data
, (GDestroyNotify
) contact_rename_data_free
,
1152 actor_contact
= chat_lookup_contact (self
, actor
, FALSE
);
1153 if (actor_contact
== NULL
) {
1154 /* FIXME: handle this a tad more gracefully: perhaps
1155 * the actor was a server op. We could use the
1156 * contact-ids detail of MembersChangedDetailed.
1158 DEBUG ("actor %u not a channel member", actor
);
1162 /* Remove contacts that are not members anymore */
1163 for (i
= 0; i
< removed
->len
; i
++) {
1164 contact
= chat_lookup_contact (self
,
1165 g_array_index (removed
, TpHandle
, i
), TRUE
);
1167 if (contact
!= NULL
) {
1168 g_signal_emit_by_name (self
, "members-changed", contact
,
1169 actor_contact
, reason
, message
,
1171 g_object_unref (contact
);
1175 /* Request added contacts */
1176 if (added
->len
> 0) {
1177 empathy_tp_contact_factory_get_from_handles (connection
,
1178 added
->len
, (TpHandle
*) added
->data
,
1179 tp_chat_got_added_contacts_cb
, NULL
, NULL
,
1183 tp_chat_update_remote_contact (self
);
1185 if (actor_contact
!= NULL
) {
1186 g_object_unref (actor_contact
);
1191 tp_chat_got_remote_contact_cb (TpConnection
*connection
,
1192 EmpathyContact
*contact
,
1193 const GError
*error
,
1197 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1200 DEBUG ("Error: %s", error
->message
);
1201 empathy_tp_chat_leave (self
, "");
1205 self
->priv
->remote_contact
= g_object_ref (contact
);
1206 g_object_notify (chat
, "remote-contact");
1208 check_almost_ready (self
);
1212 tp_chat_got_self_contact_cb (TpConnection
*connection
,
1213 EmpathyContact
*contact
,
1214 const GError
*error
,
1218 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1221 DEBUG ("Error: %s", error
->message
);
1222 empathy_tp_chat_leave (self
, "");
1226 self
->priv
->user
= g_object_ref (contact
);
1227 empathy_contact_set_is_user (self
->priv
->user
, TRUE
);
1228 check_almost_ready (self
);
1232 password_flags_changed_cb (TpChannel
*channel
,
1236 GObject
*weak_object
)
1238 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (weak_object
);
1239 gboolean was_needed
, needed
;
1241 was_needed
= empathy_tp_chat_password_needed (self
);
1243 self
->priv
->password_flags
|= added
;
1244 self
->priv
->password_flags
^= removed
;
1246 needed
= empathy_tp_chat_password_needed (self
);
1248 if (was_needed
!= needed
)
1249 g_object_notify (G_OBJECT (self
), "password-needed");
1253 got_password_flags_cb (TpChannel
*proxy
,
1254 guint password_flags
,
1255 const GError
*error
,
1257 GObject
*weak_object
)
1259 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (weak_object
);
1261 self
->priv
->got_password_flags
= TRUE
;
1262 self
->priv
->password_flags
= password_flags
;
1264 check_almost_ready (EMPATHY_TP_CHAT (self
));
1268 sms_channel_changed_cb (TpChannel
*channel
,
1269 gboolean sms_channel
,
1273 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1275 self
->priv
->sms_channel
= sms_channel
;
1277 g_object_notify (G_OBJECT (chat
), "sms-channel");
1281 get_sms_channel_cb (TpProxy
*channel
,
1282 const GValue
*value
,
1283 const GError
*in_error
,
1287 EmpathyTpChat
*self
= (EmpathyTpChat
*) chat
;
1289 if (in_error
!= NULL
) {
1290 DEBUG ("Failed to get SMSChannel: %s", in_error
->message
);
1294 g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value
));
1296 self
->priv
->sms_channel
= g_value_get_boolean (value
);
1297 self
->priv
->got_sms_channel
= TRUE
;
1299 check_almost_ready (EMPATHY_TP_CHAT (chat
));
1303 tp_chat_get_property (GObject
*object
,
1308 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
1312 g_value_set_object (value
, self
->priv
->account
);
1314 case PROP_REMOTE_CONTACT
:
1315 g_value_set_object (value
, self
->priv
->remote_contact
);
1317 case PROP_PASSWORD_NEEDED
:
1318 g_value_set_boolean (value
, empathy_tp_chat_password_needed (self
));
1320 case PROP_SMS_CHANNEL
:
1321 g_value_set_boolean (value
, self
->priv
->sms_channel
);
1323 case PROP_N_MESSAGES_SENDING
:
1324 g_value_set_uint (value
,
1325 g_hash_table_size (self
->priv
->messages_being_sent
));
1328 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1334 tp_chat_set_property (GObject
*object
,
1336 const GValue
*value
,
1339 EmpathyTpChat
*self
= EMPATHY_TP_CHAT (object
);
1343 self
->priv
->account
= g_value_dup_object (value
);
1346 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1356 static const TpProxyFeature
*
1357 tp_chat_list_features (TpProxyClass
*cls G_GNUC_UNUSED
)
1359 static TpProxyFeature features
[N_FEAT
+ 1] = { { 0 } };
1360 static GQuark need
[2] = {0, 0};
1362 if (G_LIKELY (features
[0].name
!= 0))
1365 features
[FEAT_READY
].name
= EMPATHY_TP_CHAT_FEATURE_READY
;
1366 need
[0] = TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES
;
1367 features
[FEAT_READY
].depends_on
= need
;
1368 features
[FEAT_READY
].prepare_async
=
1369 tp_chat_prepare_ready_async
;
1371 /* assert that the terminator at the end is there */
1372 g_assert (features
[N_FEAT
].name
== 0);
1378 empathy_tp_chat_class_init (EmpathyTpChatClass
*klass
)
1380 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1381 TpProxyClass
*proxy_class
= TP_PROXY_CLASS (klass
);
1383 object_class
->dispose
= tp_chat_dispose
;
1384 object_class
->finalize
= tp_chat_finalize
;
1385 object_class
->get_property
= tp_chat_get_property
;
1386 object_class
->set_property
= tp_chat_set_property
;
1388 proxy_class
->list_features
= tp_chat_list_features
;
1390 g_object_class_install_property (object_class
,
1392 g_param_spec_object ("account",
1394 "the account associated with the chat",
1397 G_PARAM_CONSTRUCT_ONLY
|
1398 G_PARAM_STATIC_STRINGS
));
1400 g_object_class_install_property (object_class
,
1401 PROP_REMOTE_CONTACT
,
1402 g_param_spec_object ("remote-contact",
1403 "The remote contact",
1404 "The remote contact if there is no group iface on the channel",
1405 EMPATHY_TYPE_CONTACT
,
1408 g_object_class_install_property (object_class
,
1409 PROP_PASSWORD_NEEDED
,
1410 g_param_spec_boolean ("password-needed",
1412 "TRUE if a password is needed to join the channel",
1416 g_object_class_install_property (object_class
,
1418 g_param_spec_boolean ("sms-channel",
1420 "TRUE if channel is for sending SMSes",
1424 g_object_class_install_property (object_class
,
1425 PROP_N_MESSAGES_SENDING
,
1426 g_param_spec_uint ("n-messages-sending",
1427 "Num Messages Sending",
1428 "The number of messages being sent",
1433 signals
[MESSAGE_RECEIVED
] =
1434 g_signal_new ("message-received-empathy",
1435 G_TYPE_FROM_CLASS (klass
),
1439 g_cclosure_marshal_VOID__OBJECT
,
1441 1, EMPATHY_TYPE_MESSAGE
);
1443 signals
[SEND_ERROR
] =
1444 g_signal_new ("send-error",
1445 G_TYPE_FROM_CLASS (klass
),
1449 _empathy_marshal_VOID__STRING_UINT_STRING
,
1451 3, G_TYPE_STRING
, G_TYPE_UINT
, G_TYPE_STRING
);
1453 signals
[CHAT_STATE_CHANGED
] =
1454 g_signal_new ("chat-state-changed-empathy",
1455 G_TYPE_FROM_CLASS (klass
),
1459 _empathy_marshal_VOID__OBJECT_UINT
,
1461 2, EMPATHY_TYPE_CONTACT
, G_TYPE_UINT
);
1463 signals
[PROPERTY_CHANGED
] =
1464 g_signal_new ("property-changed",
1465 G_TYPE_FROM_CLASS (klass
),
1469 _empathy_marshal_VOID__STRING_BOXED
,
1471 2, G_TYPE_STRING
, G_TYPE_VALUE
);
1473 signals
[MESSAGE_ACKNOWLEDGED
] =
1474 g_signal_new ("message-acknowledged",
1475 G_TYPE_FROM_CLASS (klass
),
1479 g_cclosure_marshal_VOID__OBJECT
,
1481 1, EMPATHY_TYPE_MESSAGE
);
1483 g_type_class_add_private (object_class
, sizeof (EmpathyTpChatPrivate
));
1487 empathy_tp_chat_init (EmpathyTpChat
*self
)
1489 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
1490 EMPATHY_TYPE_TP_CHAT
, EmpathyTpChatPrivate
);
1492 self
->priv
->messages_queue
= g_queue_new ();
1493 self
->priv
->pending_messages_queue
= g_queue_new ();
1494 self
->priv
->messages_being_sent
= g_hash_table_new_full (
1495 g_str_hash
, g_str_equal
, g_free
, NULL
);
1499 tp_chat_iface_init (EmpathyContactListIface
*iface
)
1501 iface
->add
= tp_chat_add
;
1502 iface
->remove
= tp_chat_remove
;
1503 iface
->get_members
= tp_chat_get_members
;
1507 empathy_tp_chat_new (TpAccount
*account
,
1509 const gchar
*object_path
,
1510 const GHashTable
*immutable_properties
)
1512 TpProxy
*conn_proxy
= (TpProxy
*) conn
;
1514 g_return_val_if_fail (TP_IS_ACCOUNT (account
), NULL
);
1515 g_return_val_if_fail (TP_IS_CONNECTION (conn
), NULL
);
1516 g_return_val_if_fail (immutable_properties
!= NULL
, NULL
);
1518 return g_object_new (EMPATHY_TYPE_TP_CHAT
,
1521 "dbus-daemon", conn_proxy
->dbus_daemon
,
1522 "bus-name", conn_proxy
->bus_name
,
1523 "object-path", object_path
,
1524 "channel-properties", immutable_properties
,
1529 empathy_tp_chat_get_id (EmpathyTpChat
*self
)
1533 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1535 id
= tp_channel_get_identifier ((TpChannel
*) self
);
1536 if (!EMP_STR_EMPTY (id
))
1538 else if (self
->priv
->remote_contact
)
1539 return empathy_contact_get_id (self
->priv
->remote_contact
);
1546 empathy_tp_chat_get_remote_contact (EmpathyTpChat
*self
)
1548 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1550 return self
->priv
->remote_contact
;
1554 empathy_tp_chat_get_account (EmpathyTpChat
*self
)
1556 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1558 return self
->priv
->account
;
1562 empathy_tp_chat_send (EmpathyTpChat
*self
,
1565 gchar
*message_body
;
1567 g_return_if_fail (EMPATHY_IS_TP_CHAT (self
));
1568 g_return_if_fail (TP_IS_CLIENT_MESSAGE (message
));
1570 message_body
= tp_message_to_text (message
, NULL
);
1572 DEBUG ("Sending message: %s", message_body
);
1574 tp_text_channel_send_message_async (TP_TEXT_CHANNEL (self
),
1575 message
, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY
,
1576 message_send_cb
, self
);
1578 g_free (message_body
);
1582 empathy_tp_chat_get_pending_messages (EmpathyTpChat
*self
)
1584 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1586 return self
->priv
->pending_messages_queue
->head
;
1590 empathy_tp_chat_acknowledge_message (EmpathyTpChat
*self
,
1591 EmpathyMessage
*message
) {
1594 g_return_if_fail (EMPATHY_IS_TP_CHAT (self
));
1596 if (!empathy_message_is_incoming (message
))
1599 tp_msg
= empathy_message_get_tp_message (message
);
1600 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self
),
1601 tp_msg
, NULL
, NULL
);
1605 empathy_tp_chat_acknowledge_messages (EmpathyTpChat
*self
,
1606 const GSList
*messages
) {
1608 GList
*messages_to_ack
= NULL
;
1610 g_return_if_fail (EMPATHY_IS_TP_CHAT (self
));
1612 if (messages
== NULL
)
1615 for (l
= messages
; l
!= NULL
; l
= g_slist_next (l
)) {
1616 EmpathyMessage
*message
= EMPATHY_MESSAGE (l
->data
);
1618 if (empathy_message_is_incoming (message
)) {
1619 TpMessage
*tp_msg
= empathy_message_get_tp_message (message
);
1620 messages_to_ack
= g_list_append (messages_to_ack
, tp_msg
);
1624 if (messages_to_ack
!= NULL
) {
1625 tp_text_channel_ack_messages_async (TP_TEXT_CHANNEL (self
),
1626 messages_to_ack
, NULL
, NULL
);
1629 g_list_free (messages_to_ack
);
1633 empathy_tp_chat_acknowledge_all_messages (EmpathyTpChat
*self
)
1635 empathy_tp_chat_acknowledge_messages (self
,
1636 (GSList
*) empathy_tp_chat_get_pending_messages (self
));
1640 empathy_tp_chat_password_needed (EmpathyTpChat
*self
)
1642 return self
->priv
->password_flags
& TP_CHANNEL_PASSWORD_FLAG_PROVIDE
;
1646 provide_password_cb (TpChannel
*channel
,
1648 const GError
*error
,
1650 GObject
*weak_object
)
1652 GSimpleAsyncResult
*result
= user_data
;
1654 if (error
!= NULL
) {
1655 g_simple_async_result_set_from_error (result
, error
);
1657 else if (!correct
) {
1658 /* The current D-Bus API is a bit weird so re-use the
1659 * AuthenticationFailed error */
1660 g_simple_async_result_set_error (result
, TP_ERRORS
,
1661 TP_ERROR_AUTHENTICATION_FAILED
, "Wrong password");
1664 g_simple_async_result_complete (result
);
1665 g_object_unref (result
);
1669 empathy_tp_chat_provide_password_async (EmpathyTpChat
*self
,
1670 const gchar
*password
,
1671 GAsyncReadyCallback callback
,
1674 GSimpleAsyncResult
*result
;
1676 result
= g_simple_async_result_new (G_OBJECT (self
),
1677 callback
, user_data
,
1678 empathy_tp_chat_provide_password_finish
);
1680 tp_cli_channel_interface_password_call_provide_password
1681 ((TpChannel
*) self
, -1, password
, provide_password_cb
, result
,
1682 NULL
, G_OBJECT (self
));
1686 empathy_tp_chat_provide_password_finish (EmpathyTpChat
*self
,
1687 GAsyncResult
*result
,
1690 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result
),
1694 g_return_val_if_fail (g_simple_async_result_is_valid (result
,
1695 G_OBJECT (self
), empathy_tp_chat_provide_password_finish
), FALSE
);
1701 * empathy_tp_chat_can_add_contact:
1703 * Returns: %TRUE if empathy_contact_list_add() will work for this channel.
1704 * That is if this chat is a 1-to-1 channel that can be upgraded to
1705 * a MUC using the Conference interface or if the channel is a MUC.
1708 empathy_tp_chat_can_add_contact (EmpathyTpChat
*self
)
1710 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), FALSE
);
1712 return self
->priv
->can_upgrade_to_muc
||
1713 tp_proxy_has_interface_by_id (self
,
1714 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
);;
1718 tp_channel_leave_async_cb (GObject
*source_object
,
1722 GError
*error
= NULL
;
1724 if (!tp_channel_leave_finish (TP_CHANNEL (source_object
), res
, &error
)) {
1725 DEBUG ("Could not leave channel properly: (%s); closing the channel",
1727 g_error_free (error
);
1732 empathy_tp_chat_leave (EmpathyTpChat
*self
,
1733 const gchar
*message
)
1735 TpChannel
*channel
= (TpChannel
*) self
;
1737 DEBUG ("Leaving channel %s with message \"%s\"",
1738 tp_channel_get_identifier (channel
), message
);
1740 tp_channel_leave_async (channel
, TP_CHANNEL_GROUP_CHANGE_REASON_NONE
,
1741 message
, tp_channel_leave_async_cb
, self
);
1745 add_members_cb (TpChannel
*proxy
,
1746 const GError
*error
,
1748 GObject
*weak_object
)
1750 EmpathyTpChat
*self
= (EmpathyTpChat
*) weak_object
;
1752 if (error
!= NULL
) {
1753 DEBUG ("Failed to join chat (%s): %s",
1754 tp_channel_get_identifier ((TpChannel
*) self
), error
->message
);
1759 empathy_tp_chat_join (EmpathyTpChat
*self
)
1761 TpHandle self_handle
;
1764 self_handle
= tp_channel_group_get_self_handle ((TpChannel
*) self
);
1766 members
= g_array_sized_new (FALSE
, FALSE
, sizeof (TpHandle
), 1);
1767 g_array_append_val (members
, self_handle
);
1769 tp_cli_channel_interface_group_call_add_members ((TpChannel
*) self
, -1, members
,
1770 "", add_members_cb
, NULL
, NULL
, G_OBJECT (self
));
1772 g_array_free (members
, TRUE
);
1776 empathy_tp_chat_is_invited (EmpathyTpChat
*self
,
1779 TpHandle self_handle
;
1781 if (!tp_proxy_has_interface (self
, TP_IFACE_CHANNEL_INTERFACE_GROUP
))
1784 self_handle
= tp_channel_group_get_self_handle ((TpChannel
*) self
);
1785 if (self_handle
== 0)
1788 return tp_channel_group_get_local_pending_info ((TpChannel
*) self
, self_handle
,
1789 inviter
, NULL
, NULL
);
1793 empathy_tp_chat_get_chat_state (EmpathyTpChat
*self
,
1794 EmpathyContact
*contact
)
1796 return tp_channel_get_chat_state ((TpChannel
*) self
,
1797 empathy_contact_get_handle (contact
));
1801 empathy_tp_chat_get_self_contact (EmpathyTpChat
*self
)
1803 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), NULL
);
1805 return self
->priv
->user
;
1810 empathy_tp_chat_is_sms_channel (EmpathyTpChat
*self
)
1812 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self
), FALSE
);
1814 return self
->priv
->sms_channel
;
1818 empathy_tp_chat_get_feature_ready (void)
1820 return g_quark_from_static_string ("empathy-tp-chat-feature-ready");
1824 conn_prepared_cb (GObject
*source
,
1825 GAsyncResult
*result
,
1828 EmpathyTpChat
*self
= user_data
;
1829 TpChannel
*channel
= (TpChannel
*) self
;
1830 GError
*error
= NULL
;
1832 TpConnection
*connection
= (TpConnection
*) source
;
1834 if (!tp_proxy_prepare_finish (source
, result
, &error
)) {
1835 g_simple_async_result_set_from_error (self
->priv
->ready_result
, error
);
1836 g_error_free (error
);
1837 g_simple_async_result_complete (self
->priv
->ready_result
);
1838 tp_clear_object (&self
->priv
->ready_result
);
1842 if (tp_proxy_has_interface_by_id (self
,
1843 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
)) {
1844 const TpIntSet
*members
;
1847 /* Get self contact from the group's self handle */
1848 handle
= tp_channel_group_get_self_handle (channel
);
1849 empathy_tp_contact_factory_get_from_handle (connection
,
1850 handle
, tp_chat_got_self_contact_cb
,
1851 NULL
, NULL
, G_OBJECT (self
));
1853 /* Get initial member contacts */
1854 members
= tp_channel_group_get_members (channel
);
1855 handles
= tp_intset_to_array (members
);
1856 empathy_tp_contact_factory_get_from_handles (connection
,
1857 handles
->len
, (TpHandle
*) handles
->data
,
1858 tp_chat_got_added_contacts_cb
, NULL
, NULL
, G_OBJECT (self
));
1860 self
->priv
->can_upgrade_to_muc
= FALSE
;
1862 tp_g_signal_connect_object (self
, "group-members-changed",
1863 G_CALLBACK (tp_chat_group_members_changed_cb
), self
, 0);
1865 TpCapabilities
*caps
;
1869 /* Get the self contact from the connection's self handle */
1870 handle
= tp_connection_get_self_handle (connection
);
1871 empathy_tp_contact_factory_get_from_handle (connection
,
1872 handle
, tp_chat_got_self_contact_cb
,
1873 NULL
, NULL
, G_OBJECT (self
));
1875 /* Get the remote contact */
1876 handle
= tp_channel_get_handle (channel
, NULL
);
1877 empathy_tp_contact_factory_get_from_handle (connection
,
1878 handle
, tp_chat_got_remote_contact_cb
,
1879 NULL
, NULL
, G_OBJECT (self
));
1881 caps
= tp_connection_get_capabilities (connection
);
1882 g_assert (caps
!= NULL
);
1884 classes
= tp_capabilities_get_channel_classes (caps
);
1886 for (i
= 0; i
< classes
->len
; i
++) {
1887 GValueArray
*array
= g_ptr_array_index (classes
, i
);
1888 const char **oprops
= g_value_get_boxed (
1889 g_value_array_get_nth (array
, 1));
1891 if (tp_strv_contains (oprops
, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS
)) {
1892 self
->priv
->can_upgrade_to_muc
= TRUE
;
1898 if (tp_proxy_has_interface_by_id (self
,
1899 TP_IFACE_QUARK_PROPERTIES_INTERFACE
)) {
1900 tp_cli_properties_interface_call_list_properties (channel
, -1,
1901 tp_chat_list_properties_cb
,
1904 tp_cli_properties_interface_connect_to_properties_changed (channel
,
1905 tp_chat_properties_changed_cb
,
1907 G_OBJECT (self
), NULL
);
1908 tp_cli_properties_interface_connect_to_property_flags_changed (channel
,
1909 tp_chat_property_flags_changed_cb
,
1911 G_OBJECT (self
), NULL
);
1914 /* Check if the chat is password protected */
1915 if (tp_proxy_has_interface_by_id (self
,
1916 TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD
)) {
1917 self
->priv
->got_password_flags
= FALSE
;
1919 tp_cli_channel_interface_password_connect_to_password_flags_changed
1920 (channel
, password_flags_changed_cb
, self
, NULL
,
1921 G_OBJECT (self
), NULL
);
1923 tp_cli_channel_interface_password_call_get_password_flags
1924 (channel
, -1, got_password_flags_cb
, self
, NULL
, G_OBJECT (self
));
1926 /* No Password interface, so no need to fetch the password flags */
1927 self
->priv
->got_password_flags
= TRUE
;
1930 /* Check if the chat is for SMS */
1931 if (tp_proxy_has_interface_by_id (channel
,
1932 TP_IFACE_QUARK_CHANNEL_INTERFACE_SMS
)) {
1933 tp_cli_channel_interface_sms_connect_to_sms_channel_changed (
1935 sms_channel_changed_cb
, self
, NULL
, G_OBJECT (self
), NULL
);
1937 tp_cli_dbus_properties_call_get (channel
, -1,
1938 TP_IFACE_CHANNEL_INTERFACE_SMS
, "SMSChannel",
1939 get_sms_channel_cb
, self
, NULL
, G_OBJECT (self
));
1941 /* if there's no SMS support, then we're not waiting for it */
1942 self
->priv
->got_sms_channel
= TRUE
;
1947 tp_chat_prepare_ready_async (TpProxy
*proxy
,
1948 const TpProxyFeature
*feature
,
1949 GAsyncReadyCallback callback
,
1952 EmpathyTpChat
*self
= (EmpathyTpChat
*) proxy
;
1953 TpChannel
*channel
= (TpChannel
*) proxy
;
1954 TpConnection
*connection
;
1955 GQuark conn_features
[] = { TP_CONNECTION_FEATURE_CAPABILITIES
, 0 };
1957 g_assert (self
->priv
->ready_result
== NULL
);
1958 self
->priv
->ready_result
= g_simple_async_result_new (G_OBJECT (self
),
1959 callback
, user_data
, tp_chat_prepare_ready_async
);
1961 connection
= tp_channel_borrow_connection (channel
);
1963 tp_proxy_prepare_async (connection
, conn_features
,
1964 conn_prepared_cb
, self
);