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 {
42 gboolean dispose_has_run;
45 EmpathyContact *remote_contact;
47 /* Queue of messages not signalled yet */
48 GQueue *messages_queue;
49 /* Queue of messages signalled but not acked yet */
50 GQueue *pending_messages_queue;
51 gboolean had_properties_list;
52 GPtrArray *properties;
53 TpChannelPasswordFlags password_flags;
54 /* TRUE if we fetched the password flag of the channel or if it's not needed
55 * (channel doesn't implement the Password interface) */
56 gboolean got_password_flags;
58 gboolean can_upgrade_to_muc;
59 gboolean got_sms_channel;
62 GHashTable *messages_being_sent;
65 static void tp_chat_iface_init (EmpathyContactListIface *iface);
74 PROP_N_MESSAGES_SENDING,
87 static guint signals[LAST_SIGNAL];
89 G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, TP_TYPE_TEXT_CHANNEL,
90 G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
94 tp_chat_set_delivery_status (EmpathyTpChat *self,
96 EmpathyDeliveryStatus delivery_status)
98 TpDeliveryReportingSupportFlags flags =
99 tp_text_channel_get_delivery_reporting_support (
100 TP_TEXT_CHANNEL (self));
102 /* channel must support receiving failures and successes */
103 if (!tp_str_empty (token) &&
104 flags & TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES &&
105 flags & TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES) {
107 DEBUG ("Delivery status (%s) = %u", token, delivery_status);
109 switch (delivery_status) {
110 case EMPATHY_DELIVERY_STATUS_NONE:
111 g_hash_table_remove (self->priv->messages_being_sent,
116 g_hash_table_insert (self->priv->messages_being_sent,
118 GUINT_TO_POINTER (delivery_status));
122 g_object_notify (G_OBJECT (self), "n-messages-sending");
127 tp_chat_invalidated_cb (TpProxy *proxy,
133 DEBUG ("Channel invalidated: %s", message);
134 g_signal_emit (self, signals[DESTROY], 0);
138 tp_chat_async_cb (TpChannel *proxy,
141 GObject *weak_object)
144 DEBUG ("Error %s: %s", (gchar *) user_data, error->message);
149 create_conference_cb (GObject *source,
150 GAsyncResult *result,
153 GError *error = NULL;
155 if (!tp_account_channel_request_create_channel_finish (
156 TP_ACCOUNT_CHANNEL_REQUEST (source), result, &error)) {
157 DEBUG ("Failed to create conference channel: %s", error->message);
158 g_error_free (error);
163 tp_chat_add (EmpathyContactList *list,
164 EmpathyContact *contact,
165 const gchar *message)
167 EmpathyTpChat *self = (EmpathyTpChat *) list;
168 TpChannel *channel = (TpChannel *) self;
170 if (tp_proxy_has_interface_by_id (self,
171 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
173 GArray handles = {(gchar *) &handle, 1};
175 g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
176 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
178 handle = empathy_contact_get_handle (contact);
179 tp_cli_channel_interface_group_call_add_members (channel,
180 -1, &handles, NULL, NULL, NULL, NULL, NULL);
181 } else if (self->priv->can_upgrade_to_muc) {
182 TpAccountChannelRequest *req;
184 const char *object_path;
185 GPtrArray channels = { (gpointer *) &object_path, 1 };
186 const char *invitees[2] = { NULL, };
188 invitees[0] = empathy_contact_get_id (contact);
189 object_path = tp_proxy_get_object_path (self);
192 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
193 TP_IFACE_CHANNEL_TYPE_TEXT,
194 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
196 TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS,
197 TP_ARRAY_TYPE_OBJECT_PATH_LIST, &channels,
198 TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS,
199 G_TYPE_STRV, invitees,
200 /* FIXME: InvitationMessage ? */
203 req = tp_account_channel_request_new (self->priv->account, props,
204 TP_USER_ACTION_TIME_NOT_USER_ACTION);
206 /* Although this is a MUC, it's anonymous, so CreateChannel is
208 tp_account_channel_request_create_channel_async (req, EMPATHY_CHAT_BUS_NAME,
209 NULL, create_conference_cb, NULL);
211 g_object_unref (req);
212 g_hash_table_unref (props);
214 g_warning ("Cannot add to this channel");
219 tp_chat_remove (EmpathyContactList *list,
220 EmpathyContact *contact,
221 const gchar *message)
223 EmpathyTpChat *self = (EmpathyTpChat *) list;
225 GArray handles = {(gchar *) &handle, 1};
227 g_return_if_fail (EMPATHY_IS_TP_CHAT (list));
228 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
230 handle = empathy_contact_get_handle (contact);
231 tp_cli_channel_interface_group_call_remove_members ((TpChannel *) self, -1,
238 tp_chat_get_members (EmpathyContactList *list)
240 EmpathyTpChat *self = (EmpathyTpChat *) list;
241 GList *members = NULL;
243 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (list), NULL);
245 if (self->priv->members) {
246 members = g_list_copy (self->priv->members);
247 g_list_foreach (members, (GFunc) g_object_ref, NULL);
249 members = g_list_prepend (members, g_object_ref (self->priv->user));
250 if (self->priv->remote_contact != NULL)
251 members = g_list_prepend (members, g_object_ref (self->priv->remote_contact));
258 check_ready (EmpathyTpChat *self)
260 if (self->priv->ready)
263 if (g_queue_get_length (self->priv->messages_queue) > 0)
268 self->priv->ready = TRUE;
269 g_object_notify (G_OBJECT (self), "ready");
273 tp_chat_emit_queued_messages (EmpathyTpChat *self)
275 EmpathyMessage *message;
277 /* Check if we can now emit some queued messages */
278 while ((message = g_queue_peek_head (self->priv->messages_queue)) != NULL) {
279 if (empathy_message_get_sender (message) == NULL) {
283 DEBUG ("Queued message ready");
284 g_queue_pop_head (self->priv->messages_queue);
285 g_queue_push_tail (self->priv->pending_messages_queue, message);
286 g_signal_emit (self, signals[MESSAGE_RECEIVED], 0, message);
293 tp_chat_got_sender_cb (TpConnection *connection,
294 EmpathyContact *contact,
299 EmpathyTpChat *self = (EmpathyTpChat *) chat;
302 DEBUG ("Error: %s", error->message);
303 /* Do not block the message queue, just drop this message */
304 g_queue_remove (self->priv->messages_queue, message);
306 empathy_message_set_sender (message, contact);
309 tp_chat_emit_queued_messages (EMPATHY_TP_CHAT (self));
313 tp_chat_build_message (EmpathyTpChat *self,
317 EmpathyMessage *message;
320 message = empathy_message_new_from_tp_message (msg, incoming);
321 /* FIXME: this is actually a lie for incoming messages. */
322 empathy_message_set_receiver (message, self->priv->user);
324 g_queue_push_tail (self->priv->messages_queue, message);
326 sender = tp_signalled_message_get_sender (msg);
327 g_assert (sender != NULL);
329 if (tp_contact_get_handle (sender) == 0) {
330 empathy_message_set_sender (message, self->priv->user);
331 tp_chat_emit_queued_messages (self);
333 TpConnection *connection = tp_channel_borrow_connection (
336 empathy_tp_contact_factory_get_from_handle (connection,
337 tp_contact_get_handle (sender),
338 tp_chat_got_sender_cb,
339 message, NULL, G_OBJECT (self));
344 handle_delivery_report (EmpathyTpChat *self,
347 TpDeliveryStatus delivery_status;
348 const GHashTable *header;
349 TpChannelTextSendError delivery_error;
352 const gchar *message_body = NULL;
353 const gchar *delivery_dbus_error;
354 const gchar *delivery_token = NULL;
356 header = tp_message_peek (message, 0);
360 delivery_token = tp_asv_get_string (header, "delivery-token");
361 delivery_status = tp_asv_get_uint32 (header, "delivery-status", &valid);
365 } else if (delivery_status == TP_DELIVERY_STATUS_ACCEPTED) {
366 DEBUG ("Accepted %s", delivery_token);
367 tp_chat_set_delivery_status (self, delivery_token,
368 EMPATHY_DELIVERY_STATUS_ACCEPTED);
370 } else if (delivery_status == TP_DELIVERY_STATUS_DELIVERED) {
371 DEBUG ("Delivered %s", delivery_token);
372 tp_chat_set_delivery_status (self, delivery_token,
373 EMPATHY_DELIVERY_STATUS_NONE);
375 } else if (delivery_status != TP_DELIVERY_STATUS_PERMANENTLY_FAILED) {
379 delivery_error = tp_asv_get_uint32 (header, "delivery-error", &valid);
381 delivery_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
383 delivery_dbus_error = tp_asv_get_string (header, "delivery-dbus-error");
385 /* TODO: ideally we should use tp-glib API giving us the echoed message as a
386 * TpMessage. (fdo #35884) */
387 echo = tp_asv_get_boxed (header, "delivery-echo",
388 TP_ARRAY_TYPE_MESSAGE_PART_LIST);
389 if (echo != NULL && echo->len >= 1) {
390 const GHashTable *echo_body;
392 echo_body = g_ptr_array_index (echo, 1);
393 if (echo_body != NULL)
394 message_body = tp_asv_get_string (echo_body, "content");
397 tp_chat_set_delivery_status (self, delivery_token,
398 EMPATHY_DELIVERY_STATUS_NONE);
399 g_signal_emit (self, signals[SEND_ERROR], 0, message_body,
400 delivery_error, delivery_dbus_error);
403 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self),
404 message, NULL, NULL);
408 handle_incoming_message (EmpathyTpChat *self,
414 if (tp_message_is_delivery_report (message)) {
415 handle_delivery_report (self, message);
419 message_body = tp_message_to_text (message, NULL);
421 DEBUG ("Message %s (channel %s): %s",
422 pending ? "pending" : "received",
423 tp_proxy_get_object_path (self), message_body);
425 if (message_body == NULL) {
426 DEBUG ("Empty message with NonTextContent, ignoring and acking.");
428 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self),
429 message, NULL, NULL);
433 tp_chat_build_message (self, message, TRUE);
435 g_free (message_body);
439 message_received_cb (TpTextChannel *channel,
443 handle_incoming_message (self, message, FALSE);
447 find_pending_message_func (gconstpointer a,
450 EmpathyMessage *msg = (EmpathyMessage *) a;
451 TpMessage *message = (TpMessage *) b;
453 if (empathy_message_get_tp_message (msg) == message)
460 pending_message_removed_cb (TpTextChannel *channel,
466 m = g_queue_find_custom (self->priv->pending_messages_queue, message,
467 find_pending_message_func);
472 g_signal_emit (self, signals[MESSAGE_ACKNOWLEDGED], 0, m->data);
474 g_object_unref (m->data);
475 g_queue_delete_link (self->priv->pending_messages_queue, m);
479 message_sent_cb (TpTextChannel *channel,
481 TpMessageSendingFlags flags,
487 message_body = tp_message_to_text (message, NULL);
489 DEBUG ("Message sent: %s", message_body);
491 tp_chat_build_message (self, message, FALSE);
493 g_free (message_body);
496 static TpChannelTextSendError
497 error_to_text_send_error (GError *error)
499 if (error->domain != TP_ERRORS)
500 return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
502 switch (error->code) {
503 case TP_ERROR_OFFLINE:
504 return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE;
505 case TP_ERROR_INVALID_HANDLE:
506 return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT;
507 case TP_ERROR_PERMISSION_DENIED:
508 return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED;
509 case TP_ERROR_NOT_IMPLEMENTED:
510 return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED;
513 return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
517 message_send_cb (GObject *source,
518 GAsyncResult *result,
521 EmpathyTpChat *self = user_data;
522 TpTextChannel *channel = (TpTextChannel *) source;
524 GError *error = NULL;
526 if (!tp_text_channel_send_message_finish (channel, result, &token, &error)) {
527 DEBUG ("Error: %s", error->message);
529 /* FIXME: we should use the body of the message as first argument of the
530 * signal but can't easily get it as we just get a user_data pointer. Once
531 * we'll have rebased EmpathyTpChat on top of TpTextChannel we'll be able
532 * to use the user_data pointer to pass the message and fix this. */
533 g_signal_emit (self, signals[SEND_ERROR], 0,
534 NULL, error_to_text_send_error (error), NULL);
536 g_error_free (error);
539 tp_chat_set_delivery_status (self, token,
540 EMPATHY_DELIVERY_STATUS_SENDING);
546 TpChannelChatState state;
550 tp_chat_state_changed_got_contact_cb (TpConnection *connection,
551 EmpathyContact *contact,
556 TpChannelChatState state;
559 DEBUG ("Error: %s", error->message);
563 state = GPOINTER_TO_UINT (user_data);
564 DEBUG ("Chat state changed for %s (%d): %d",
565 empathy_contact_get_alias (contact),
566 empathy_contact_get_handle (contact), state);
568 g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
572 tp_chat_state_changed_cb (TpChannel *channel,
574 TpChannelChatState state,
577 TpConnection *connection = tp_channel_borrow_connection (
580 empathy_tp_contact_factory_get_from_handle (connection, handle,
581 tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state),
582 NULL, G_OBJECT (self));
586 list_pending_messages (EmpathyTpChat *self)
590 messages = tp_text_channel_get_pending_messages (
591 TP_TEXT_CHANNEL (self));
593 for (l = messages; l != NULL; l = g_list_next (l)) {
594 TpMessage *message = l->data;
596 handle_incoming_message (self, message, FALSE);
599 g_list_free (messages);
603 tp_chat_property_flags_changed_cb (TpProxy *proxy,
604 const GPtrArray *properties,
608 EmpathyTpChat *self = (EmpathyTpChat *) chat;
611 if (!self->priv->had_properties_list || !properties) {
615 for (i = 0; i < properties->len; i++) {
616 GValueArray *prop_struct;
617 EmpathyTpChatProperty *property;
621 prop_struct = g_ptr_array_index (properties, i);
622 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
623 flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 1));
625 for (j = 0; j < self->priv->properties->len; j++) {
626 property = g_ptr_array_index (self->priv->properties, j);
627 if (property->id == id) {
628 property->flags = flags;
629 DEBUG ("property %s flags changed: %d",
630 property->name, property->flags);
638 tp_chat_properties_changed_cb (TpProxy *proxy,
639 const GPtrArray *properties,
643 EmpathyTpChat *self = (EmpathyTpChat *) chat;
646 if (!self->priv->had_properties_list || !properties) {
650 for (i = 0; i < properties->len; i++) {
651 GValueArray *prop_struct;
652 EmpathyTpChatProperty *property;
656 prop_struct = g_ptr_array_index (properties, i);
657 id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
658 src_value = g_value_get_boxed (g_value_array_get_nth (prop_struct, 1));
660 for (j = 0; j < self->priv->properties->len; j++) {
661 property = g_ptr_array_index (self->priv->properties, j);
662 if (property->id == id) {
663 if (property->value) {
664 g_value_copy (src_value, property->value);
666 property->value = tp_g_value_slice_dup (src_value);
669 DEBUG ("property %s changed", property->name);
670 g_signal_emit (chat, signals[PROPERTY_CHANGED], 0,
671 property->name, property->value);
679 tp_chat_get_properties_cb (TpProxy *proxy,
680 const GPtrArray *properties,
686 DEBUG ("Error getting properties: %s", error->message);
690 tp_chat_properties_changed_cb (proxy, properties, user_data, chat);
694 tp_chat_list_properties_cb (TpProxy *proxy,
695 const GPtrArray *properties,
700 EmpathyTpChat *self = (EmpathyTpChat *) chat;
704 self->priv->had_properties_list = TRUE;
707 DEBUG ("Error listing properties: %s", error->message);
711 ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), properties->len);
712 self->priv->properties = g_ptr_array_sized_new (properties->len);
713 for (i = 0; i < properties->len; i++) {
714 GValueArray *prop_struct;
715 EmpathyTpChatProperty *property;
717 prop_struct = g_ptr_array_index (properties, i);
718 property = g_slice_new0 (EmpathyTpChatProperty);
719 property->id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
720 property->name = g_value_dup_string (g_value_array_get_nth (prop_struct, 1));
721 property->flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 3));
723 DEBUG ("Adding property name=%s id=%d flags=%d",
724 property->name, property->id, property->flags);
725 g_ptr_array_add (self->priv->properties, property);
726 if (property->flags & TP_PROPERTY_FLAG_READ) {
727 g_array_append_val (ids, property->id);
731 tp_cli_properties_interface_call_get_properties (proxy, -1,
733 tp_chat_get_properties_cb,
737 g_array_free (ids, TRUE);
741 empathy_tp_chat_set_property (EmpathyTpChat *self,
745 EmpathyTpChatProperty *property;
748 if (!self->priv->had_properties_list) {
752 for (i = 0; i < self->priv->properties->len; i++) {
753 property = g_ptr_array_index (self->priv->properties, i);
754 if (!tp_strdiff (property->name, name)) {
755 GPtrArray *properties;
758 GValue dest_value = {0, };
760 if (!(property->flags & TP_PROPERTY_FLAG_WRITE)) {
764 g_value_init (&id, G_TYPE_UINT);
765 g_value_init (&dest_value, G_TYPE_VALUE);
766 g_value_set_uint (&id, property->id);
767 g_value_set_boxed (&dest_value, value);
769 prop = g_value_array_new (2);
770 g_value_array_append (prop, &id);
771 g_value_array_append (prop, &dest_value);
773 properties = g_ptr_array_sized_new (1);
774 g_ptr_array_add (properties, prop);
776 DEBUG ("Set property %s", name);
777 tp_cli_properties_interface_call_set_properties (self, -1,
779 (tp_cli_properties_interface_callback_for_set_properties)
781 "Seting property", NULL,
784 g_ptr_array_free (properties, TRUE);
785 g_value_array_free (prop);
792 EmpathyTpChatProperty *
793 empathy_tp_chat_get_property (EmpathyTpChat *self,
796 EmpathyTpChatProperty *property;
799 if (!self->priv->had_properties_list) {
803 for (i = 0; i < self->priv->properties->len; i++) {
804 property = g_ptr_array_index (self->priv->properties, i);
805 if (!tp_strdiff (property->name, name)) {
814 empathy_tp_chat_get_properties (EmpathyTpChat *self)
816 return self->priv->properties;
820 tp_chat_dispose (GObject *object)
822 EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
824 if (self->priv->dispose_has_run)
827 self->priv->dispose_has_run = TRUE;
829 tp_clear_object (&self->priv->account);
831 if (self->priv->remote_contact != NULL)
832 g_object_unref (self->priv->remote_contact);
833 self->priv->remote_contact = NULL;
835 if (self->priv->user != NULL)
836 g_object_unref (self->priv->user);
837 self->priv->user = NULL;
839 g_queue_foreach (self->priv->messages_queue, (GFunc) g_object_unref, NULL);
840 g_queue_clear (self->priv->messages_queue);
842 g_queue_foreach (self->priv->pending_messages_queue,
843 (GFunc) g_object_unref, NULL);
844 g_queue_clear (self->priv->pending_messages_queue);
846 if (G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose)
847 G_OBJECT_CLASS (empathy_tp_chat_parent_class)->dispose (object);
851 tp_chat_finalize (GObject *object)
853 EmpathyTpChat *self = (EmpathyTpChat *) object;
856 DEBUG ("Finalize: %p", object);
858 if (self->priv->properties) {
859 for (i = 0; i < self->priv->properties->len; i++) {
860 EmpathyTpChatProperty *property;
862 property = g_ptr_array_index (self->priv->properties, i);
863 g_free (property->name);
864 if (property->value) {
865 tp_g_value_slice_free (property->value);
867 g_slice_free (EmpathyTpChatProperty, property);
869 g_ptr_array_free (self->priv->properties, TRUE);
872 g_queue_free (self->priv->messages_queue);
873 g_queue_free (self->priv->pending_messages_queue);
874 g_hash_table_destroy (self->priv->messages_being_sent);
876 G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
880 check_almost_ready (EmpathyTpChat *self)
882 if (self->priv->ready)
885 if (self->priv->user == NULL)
888 if (!self->priv->got_password_flags)
891 if (!self->priv->got_sms_channel)
894 /* We need either the members (room) or the remote contact (private chat).
895 * If the chat is protected by a password we can't get these information so
896 * consider the chat as ready so it can be presented to the user. */
897 if (!empathy_tp_chat_password_needed (self) && self->priv->members == NULL &&
898 self->priv->remote_contact == NULL)
901 /* We use the default factory so this feature should have been prepared */
902 g_assert (tp_proxy_is_prepared (self,
903 TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES));
905 tp_g_signal_connect_object (self, "message-received",
906 G_CALLBACK (message_received_cb), self, 0);
907 tp_g_signal_connect_object (self, "pending-message-removed",
908 G_CALLBACK (pending_message_removed_cb), self, 0);
910 list_pending_messages (self);
912 tp_g_signal_connect_object (self, "message-sent",
913 G_CALLBACK (message_sent_cb), self, 0);
915 tp_g_signal_connect_object (self, "chat-state-changed",
916 G_CALLBACK (tp_chat_state_changed_cb), self, 0);
922 tp_chat_update_remote_contact (EmpathyTpChat *self)
924 TpChannel *channel = (TpChannel *) self;
925 EmpathyContact *contact = NULL;
926 TpHandle self_handle;
927 TpHandleType handle_type;
930 /* If this is a named chatroom, never pretend it is a private chat */
931 tp_channel_get_handle (channel, &handle_type);
932 if (handle_type == TP_HANDLE_TYPE_ROOM) {
936 /* This is an MSN chat, but it's the new style where 1-1 chats don't
937 * have the group interface. If it has the conference interface, then
938 * it is indeed a MUC. */
939 if (tp_proxy_has_interface_by_id (self,
940 TP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE)) {
944 /* This is an MSN-like chat where anyone can join the chat at anytime.
945 * If there is only one non-self contact member, we are in a private
946 * chat and we set the "remote-contact" property to that contact. If
947 * there are more, set the "remote-contact" property to NULL and the
948 * UI will display a contact list. */
949 self_handle = tp_channel_group_get_self_handle (channel);
950 for (l = self->priv->members; l; l = l->next) {
951 /* Skip self contact if member */
952 if (empathy_contact_get_handle (l->data) == self_handle) {
956 /* We have more than one remote contact, break */
957 if (contact != NULL) {
962 /* If we didn't find yet a remote contact, keep this one */
966 if (self->priv->remote_contact == contact) {
970 DEBUG ("Changing remote contact from %p to %p",
971 self->priv->remote_contact, contact);
973 if (self->priv->remote_contact) {
974 g_object_unref (self->priv->remote_contact);
977 self->priv->remote_contact = contact ? g_object_ref (contact) : NULL;
978 g_object_notify (G_OBJECT (self), "remote-contact");
982 tp_chat_got_added_contacts_cb (TpConnection *connection,
984 EmpathyContact * const * contacts,
986 const TpHandle *failed,
991 EmpathyTpChat *self = (EmpathyTpChat *) chat;
993 const TpIntSet *members;
995 EmpathyContact *contact;
998 DEBUG ("Error: %s", error->message);
1002 members = tp_channel_group_get_members ((TpChannel *) self);
1003 for (i = 0; i < n_contacts; i++) {
1004 contact = contacts[i];
1005 handle = empathy_contact_get_handle (contact);
1007 /* Make sure the contact is still member */
1008 if (tp_intset_is_member (members, handle)) {
1009 self->priv->members = g_list_prepend (self->priv->members,
1010 g_object_ref (contact));
1011 g_signal_emit_by_name (chat, "members-changed",
1012 contact, NULL, 0, NULL, TRUE);
1016 tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat));
1017 check_almost_ready (EMPATHY_TP_CHAT (chat));
1020 static EmpathyContact *
1021 chat_lookup_contact (EmpathyTpChat *self,
1027 for (l = self->priv->members; l; l = l->next) {
1028 EmpathyContact *c = l->data;
1030 if (empathy_contact_get_handle (c) != handle) {
1035 /* Caller takes the reference. */
1036 self->priv->members = g_list_delete_link (self->priv->members, l);
1049 TpHandle old_handle;
1052 } ContactRenameData;
1054 static ContactRenameData *
1055 contact_rename_data_new (TpHandle handle,
1057 const gchar* message)
1059 ContactRenameData *data = g_new (ContactRenameData, 1);
1060 data->old_handle = handle;
1061 data->reason = reason;
1062 data->message = g_strdup (message);
1068 contact_rename_data_free (ContactRenameData* data)
1070 g_free (data->message);
1075 tp_chat_got_renamed_contacts_cb (TpConnection *connection,
1077 EmpathyContact * const * contacts,
1079 const TpHandle *failed,
1080 const GError *error,
1084 EmpathyTpChat *self = (EmpathyTpChat *) chat;
1085 const TpIntSet *members;
1087 EmpathyContact *old = NULL, *new = NULL;
1088 ContactRenameData *rename_data = (ContactRenameData *) user_data;
1091 DEBUG ("Error: %s", error->message);
1095 /* renamed members can only be delivered one at a time */
1096 g_warn_if_fail (n_contacts == 1);
1100 members = tp_channel_group_get_members ((TpChannel *) self);
1101 handle = empathy_contact_get_handle (new);
1103 old = chat_lookup_contact (self, rename_data->old_handle, TRUE);
1105 /* Make sure the contact is still member */
1106 if (tp_intset_is_member (members, handle)) {
1107 self->priv->members = g_list_prepend (self->priv->members,
1108 g_object_ref (new));
1111 g_signal_emit_by_name (self, "member-renamed",
1112 old, new, rename_data->reason,
1113 rename_data->message);
1114 g_object_unref (old);
1118 if (self->priv->user == old) {
1119 /* We change our nick */
1120 tp_clear_object (&self->priv->user);
1121 self->priv->user = g_object_ref (new);
1124 tp_chat_update_remote_contact (self);
1125 check_almost_ready (self);
1130 tp_chat_group_members_changed_cb (TpChannel *channel,
1134 GArray *local_pending,
1135 GArray *remote_pending,
1138 EmpathyTpChat *self)
1140 EmpathyContact *contact;
1141 EmpathyContact *actor_contact = NULL;
1143 ContactRenameData *rename_data;
1144 TpHandle old_handle;
1145 TpConnection *connection = tp_channel_borrow_connection (
1146 (TpChannel *) self);
1148 /* Contact renamed */
1149 if (reason == TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED) {
1150 /* there can only be a single 'added' and a single 'removed' handle */
1151 if (removed->len != 1 || added->len != 1) {
1152 g_warning ("RENAMED with %u added, %u removed (expected 1, 1)",
1153 added->len, removed->len);
1157 old_handle = g_array_index (removed, guint, 0);
1159 rename_data = contact_rename_data_new (old_handle, reason, message);
1160 empathy_tp_contact_factory_get_from_handles (connection,
1161 added->len, (TpHandle *) added->data,
1162 tp_chat_got_renamed_contacts_cb,
1163 rename_data, (GDestroyNotify) contact_rename_data_free,
1169 actor_contact = chat_lookup_contact (self, actor, FALSE);
1170 if (actor_contact == NULL) {
1171 /* FIXME: handle this a tad more gracefully: perhaps
1172 * the actor was a server op. We could use the
1173 * contact-ids detail of MembersChangedDetailed.
1175 DEBUG ("actor %u not a channel member", actor);
1179 /* Remove contacts that are not members anymore */
1180 for (i = 0; i < removed->len; i++) {
1181 contact = chat_lookup_contact (self,
1182 g_array_index (removed, TpHandle, i), TRUE);
1184 if (contact != NULL) {
1185 g_signal_emit_by_name (self, "members-changed", contact,
1186 actor_contact, reason, message,
1188 g_object_unref (contact);
1192 /* Request added contacts */
1193 if (added->len > 0) {
1194 empathy_tp_contact_factory_get_from_handles (connection,
1195 added->len, (TpHandle *) added->data,
1196 tp_chat_got_added_contacts_cb, NULL, NULL,
1200 tp_chat_update_remote_contact (self);
1202 if (actor_contact != NULL) {
1203 g_object_unref (actor_contact);
1208 tp_chat_got_remote_contact_cb (TpConnection *connection,
1209 EmpathyContact *contact,
1210 const GError *error,
1214 EmpathyTpChat *self = (EmpathyTpChat *) chat;
1217 DEBUG ("Error: %s", error->message);
1218 empathy_tp_chat_leave (self, "");
1222 self->priv->remote_contact = g_object_ref (contact);
1223 g_object_notify (chat, "remote-contact");
1225 check_almost_ready (self);
1229 tp_chat_got_self_contact_cb (TpConnection *connection,
1230 EmpathyContact *contact,
1231 const GError *error,
1235 EmpathyTpChat *self = (EmpathyTpChat *) chat;
1238 DEBUG ("Error: %s", error->message);
1239 empathy_tp_chat_leave (self, "");
1243 self->priv->user = g_object_ref (contact);
1244 empathy_contact_set_is_user (self->priv->user, TRUE);
1245 check_almost_ready (self);
1249 password_flags_changed_cb (TpChannel *channel,
1253 GObject *weak_object)
1255 EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
1256 gboolean was_needed, needed;
1258 was_needed = empathy_tp_chat_password_needed (self);
1260 self->priv->password_flags |= added;
1261 self->priv->password_flags ^= removed;
1263 needed = empathy_tp_chat_password_needed (self);
1265 if (was_needed != needed)
1266 g_object_notify (G_OBJECT (self), "password-needed");
1270 got_password_flags_cb (TpChannel *proxy,
1271 guint password_flags,
1272 const GError *error,
1274 GObject *weak_object)
1276 EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
1278 self->priv->got_password_flags = TRUE;
1279 self->priv->password_flags = password_flags;
1281 check_almost_ready (EMPATHY_TP_CHAT (self));
1285 sms_channel_changed_cb (TpChannel *channel,
1286 gboolean sms_channel,
1290 EmpathyTpChat *self = (EmpathyTpChat *) chat;
1292 self->priv->sms_channel = sms_channel;
1294 g_object_notify (G_OBJECT (chat), "sms-channel");
1298 get_sms_channel_cb (TpProxy *channel,
1299 const GValue *value,
1300 const GError *in_error,
1304 EmpathyTpChat *self = (EmpathyTpChat *) chat;
1306 if (in_error != NULL) {
1307 DEBUG ("Failed to get SMSChannel: %s", in_error->message);
1311 g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value));
1313 self->priv->sms_channel = g_value_get_boolean (value);
1314 self->priv->got_sms_channel = TRUE;
1316 check_almost_ready (EMPATHY_TP_CHAT (chat));
1320 tp_chat_constructor (GType type,
1322 GObjectConstructParam *props)
1325 EmpathyTpChat *self;
1328 TpConnection *connection;
1331 object = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
1332 self = (EmpathyTpChat *) object;
1333 channel = (TpChannel *) object;
1335 connection = tp_channel_borrow_connection (channel);
1337 tp_g_signal_connect_object (self, "invalidated",
1338 G_CALLBACK (tp_chat_invalidated_cb),
1341 g_assert (tp_proxy_is_prepared (connection,
1342 TP_CONNECTION_FEATURE_CAPABILITIES));
1344 if (tp_proxy_has_interface_by_id (self,
1345 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)) {
1346 const TpIntSet *members;
1349 /* Get self contact from the group's self handle */
1350 handle = tp_channel_group_get_self_handle (channel);
1351 empathy_tp_contact_factory_get_from_handle (connection,
1352 handle, tp_chat_got_self_contact_cb,
1353 NULL, NULL, object);
1355 /* Get initial member contacts */
1356 members = tp_channel_group_get_members (channel);
1357 handles = tp_intset_to_array (members);
1358 empathy_tp_contact_factory_get_from_handles (connection,
1359 handles->len, (TpHandle *) handles->data,
1360 tp_chat_got_added_contacts_cb, NULL, NULL, object);
1362 self->priv->can_upgrade_to_muc = FALSE;
1364 tp_g_signal_connect_object (self, "group-members-changed",
1365 G_CALLBACK (tp_chat_group_members_changed_cb), self, 0);
1367 TpCapabilities *caps;
1371 /* Get the self contact from the connection's self handle */
1372 handle = tp_connection_get_self_handle (connection);
1373 empathy_tp_contact_factory_get_from_handle (connection,
1374 handle, tp_chat_got_self_contact_cb,
1375 NULL, NULL, object);
1377 /* Get the remote contact */
1378 handle = tp_channel_get_handle (channel, NULL);
1379 empathy_tp_contact_factory_get_from_handle (connection,
1380 handle, tp_chat_got_remote_contact_cb,
1381 NULL, NULL, object);
1383 caps = tp_connection_get_capabilities (connection);
1384 g_assert (caps != NULL);
1386 classes = tp_capabilities_get_channel_classes (caps);
1388 for (i = 0; i < classes->len; i++) {
1389 GValueArray *array = g_ptr_array_index (classes, i);
1390 const char **oprops = g_value_get_boxed (
1391 g_value_array_get_nth (array, 1));
1393 if (tp_strv_contains (oprops, TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS)) {
1394 self->priv->can_upgrade_to_muc = TRUE;
1400 if (tp_proxy_has_interface_by_id (self,
1401 TP_IFACE_QUARK_PROPERTIES_INTERFACE)) {
1402 tp_cli_properties_interface_call_list_properties (channel, -1,
1403 tp_chat_list_properties_cb,
1404 NULL, NULL, object);
1405 tp_cli_properties_interface_connect_to_properties_changed (channel,
1406 tp_chat_properties_changed_cb,
1407 NULL, NULL, object, NULL);
1408 tp_cli_properties_interface_connect_to_property_flags_changed (channel,
1409 tp_chat_property_flags_changed_cb,
1410 NULL, NULL, object, NULL);
1413 /* Check if the chat is password protected */
1414 if (tp_proxy_has_interface_by_id (self,
1415 TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD)) {
1416 self->priv->got_password_flags = FALSE;
1418 tp_cli_channel_interface_password_connect_to_password_flags_changed
1419 (channel, password_flags_changed_cb, self, NULL,
1422 tp_cli_channel_interface_password_call_get_password_flags
1423 (channel, -1, got_password_flags_cb, self, NULL, object);
1425 /* No Password interface, so no need to fetch the password flags */
1426 self->priv->got_password_flags = TRUE;
1429 /* Check if the chat is for SMS */
1430 if (tp_proxy_has_interface_by_id (self,
1431 TP_IFACE_QUARK_CHANNEL_INTERFACE_SMS)) {
1432 tp_cli_channel_interface_sms_connect_to_sms_channel_changed (
1433 channel, sms_channel_changed_cb, self, NULL, object, NULL);
1435 tp_cli_dbus_properties_call_get (self, -1,
1436 TP_IFACE_CHANNEL_INTERFACE_SMS, "SMSChannel",
1437 get_sms_channel_cb, self, NULL, object);
1439 /* if there's no SMS support, then we're not waiting for it */
1440 self->priv->got_sms_channel = TRUE;
1447 tp_chat_get_property (GObject *object,
1452 EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
1456 g_value_set_object (value, self->priv->account);
1458 case PROP_REMOTE_CONTACT:
1459 g_value_set_object (value, self->priv->remote_contact);
1462 g_value_set_boolean (value, self->priv->ready);
1464 case PROP_PASSWORD_NEEDED:
1465 g_value_set_boolean (value, empathy_tp_chat_password_needed (self));
1467 case PROP_SMS_CHANNEL:
1468 g_value_set_boolean (value, self->priv->sms_channel);
1470 case PROP_N_MESSAGES_SENDING:
1471 g_value_set_uint (value,
1472 g_hash_table_size (self->priv->messages_being_sent));
1475 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1481 tp_chat_set_property (GObject *object,
1483 const GValue *value,
1486 EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
1490 self->priv->account = g_value_dup_object (value);
1493 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1499 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
1501 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1503 object_class->dispose = tp_chat_dispose;
1504 object_class->finalize = tp_chat_finalize;
1505 object_class->constructor = tp_chat_constructor;
1506 object_class->get_property = tp_chat_get_property;
1507 object_class->set_property = tp_chat_set_property;
1509 g_object_class_install_property (object_class,
1511 g_param_spec_object ("account",
1513 "the account associated with the chat",
1516 G_PARAM_CONSTRUCT_ONLY |
1517 G_PARAM_STATIC_STRINGS));
1519 g_object_class_install_property (object_class,
1520 PROP_REMOTE_CONTACT,
1521 g_param_spec_object ("remote-contact",
1522 "The remote contact",
1523 "The remote contact if there is no group iface on the channel",
1524 EMPATHY_TYPE_CONTACT,
1527 g_object_class_install_property (object_class,
1529 g_param_spec_boolean ("ready",
1530 "Is the object ready",
1531 "This object can't be used until this becomes true",
1535 g_object_class_install_property (object_class,
1536 PROP_PASSWORD_NEEDED,
1537 g_param_spec_boolean ("password-needed",
1539 "TRUE if a password is needed to join the channel",
1543 g_object_class_install_property (object_class,
1545 g_param_spec_boolean ("sms-channel",
1547 "TRUE if channel is for sending SMSes",
1551 g_object_class_install_property (object_class,
1552 PROP_N_MESSAGES_SENDING,
1553 g_param_spec_uint ("n-messages-sending",
1554 "Num Messages Sending",
1555 "The number of messages being sent",
1560 signals[MESSAGE_RECEIVED] =
1561 g_signal_new ("message-received-empathy",
1562 G_TYPE_FROM_CLASS (klass),
1566 g_cclosure_marshal_VOID__OBJECT,
1568 1, EMPATHY_TYPE_MESSAGE);
1570 signals[SEND_ERROR] =
1571 g_signal_new ("send-error",
1572 G_TYPE_FROM_CLASS (klass),
1576 _empathy_marshal_VOID__STRING_UINT_STRING,
1578 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
1580 signals[CHAT_STATE_CHANGED] =
1581 g_signal_new ("chat-state-changed-empathy",
1582 G_TYPE_FROM_CLASS (klass),
1586 _empathy_marshal_VOID__OBJECT_UINT,
1588 2, EMPATHY_TYPE_CONTACT, G_TYPE_UINT);
1590 signals[PROPERTY_CHANGED] =
1591 g_signal_new ("property-changed",
1592 G_TYPE_FROM_CLASS (klass),
1596 _empathy_marshal_VOID__STRING_BOXED,
1598 2, G_TYPE_STRING, G_TYPE_VALUE);
1601 g_signal_new ("destroy",
1602 G_TYPE_FROM_CLASS (klass),
1606 g_cclosure_marshal_VOID__VOID,
1610 signals[MESSAGE_ACKNOWLEDGED] =
1611 g_signal_new ("message-acknowledged",
1612 G_TYPE_FROM_CLASS (klass),
1616 g_cclosure_marshal_VOID__OBJECT,
1618 1, EMPATHY_TYPE_MESSAGE);
1620 g_type_class_add_private (object_class, sizeof (EmpathyTpChatPrivate));
1624 empathy_tp_chat_init (EmpathyTpChat *self)
1626 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
1627 EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPrivate);
1629 self->priv->messages_queue = g_queue_new ();
1630 self->priv->pending_messages_queue = g_queue_new ();
1631 self->priv->messages_being_sent = g_hash_table_new_full (
1632 g_str_hash, g_str_equal, g_free, NULL);
1636 tp_chat_iface_init (EmpathyContactListIface *iface)
1638 iface->add = tp_chat_add;
1639 iface->remove = tp_chat_remove;
1640 iface->get_members = tp_chat_get_members;
1644 empathy_tp_chat_new (TpAccount *account,
1646 const gchar *object_path,
1647 const GHashTable *immutable_properties)
1649 TpProxy *conn_proxy = (TpProxy *) conn;
1651 g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
1652 g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
1653 g_return_val_if_fail (immutable_properties != NULL, NULL);
1655 return g_object_new (EMPATHY_TYPE_TP_CHAT,
1658 "dbus-daemon", conn_proxy->dbus_daemon,
1659 "bus-name", conn_proxy->bus_name,
1660 "object-path", object_path,
1661 "channel-properties", immutable_properties,
1666 empathy_tp_chat_get_id (EmpathyTpChat *self)
1670 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
1672 id = tp_channel_get_identifier ((TpChannel *) self);
1673 if (!EMP_STR_EMPTY (id))
1675 else if (self->priv->remote_contact)
1676 return empathy_contact_get_id (self->priv->remote_contact);
1683 empathy_tp_chat_get_remote_contact (EmpathyTpChat *self)
1685 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
1686 g_return_val_if_fail (self->priv->ready, NULL);
1688 return self->priv->remote_contact;
1692 empathy_tp_chat_get_account (EmpathyTpChat *self)
1694 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
1696 return self->priv->account;
1700 empathy_tp_chat_is_ready (EmpathyTpChat *self)
1702 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), FALSE);
1704 return self->priv->ready;
1708 empathy_tp_chat_send (EmpathyTpChat *self,
1711 gchar *message_body;
1713 g_return_if_fail (EMPATHY_IS_TP_CHAT (self));
1714 g_return_if_fail (TP_IS_CLIENT_MESSAGE (message));
1715 g_return_if_fail (self->priv->ready);
1717 message_body = tp_message_to_text (message, NULL);
1719 DEBUG ("Sending message: %s", message_body);
1721 tp_text_channel_send_message_async (TP_TEXT_CHANNEL (self),
1722 message, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY,
1723 message_send_cb, self);
1725 g_free (message_body);
1729 empathy_tp_chat_set_state (EmpathyTpChat *self,
1730 TpChannelChatState state)
1732 g_return_if_fail (EMPATHY_IS_TP_CHAT (self));
1733 g_return_if_fail (self->priv->ready);
1735 if (tp_proxy_has_interface_by_id (self,
1736 TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE)) {
1737 DEBUG ("Set state: %d", state);
1738 tp_cli_channel_interface_chat_state_call_set_chat_state ((TpChannel *) self, -1,
1741 "setting chat state",
1749 empathy_tp_chat_get_pending_messages (EmpathyTpChat *self)
1751 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
1752 g_return_val_if_fail (self->priv->ready, NULL);
1754 return self->priv->pending_messages_queue->head;
1758 empathy_tp_chat_acknowledge_message (EmpathyTpChat *self,
1759 EmpathyMessage *message) {
1762 g_return_if_fail (EMPATHY_IS_TP_CHAT (self));
1763 g_return_if_fail (self->priv->ready);
1765 if (!empathy_message_is_incoming (message))
1768 tp_msg = empathy_message_get_tp_message (message);
1769 tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (self),
1770 tp_msg, NULL, NULL);
1774 empathy_tp_chat_acknowledge_messages (EmpathyTpChat *self,
1775 const GSList *messages) {
1777 GList *messages_to_ack = NULL;
1779 g_return_if_fail (EMPATHY_IS_TP_CHAT (self));
1780 g_return_if_fail (self->priv->ready);
1782 if (messages == NULL)
1785 for (l = messages; l != NULL; l = g_slist_next (l)) {
1786 EmpathyMessage *message = EMPATHY_MESSAGE (l->data);
1788 if (empathy_message_is_incoming (message)) {
1789 TpMessage *tp_msg = empathy_message_get_tp_message (message);
1790 messages_to_ack = g_list_append (messages_to_ack, tp_msg);
1794 if (messages_to_ack != NULL) {
1795 tp_text_channel_ack_messages_async (TP_TEXT_CHANNEL (self),
1796 messages_to_ack, NULL, NULL);
1799 g_list_free (messages_to_ack);
1803 empathy_tp_chat_acknowledge_all_messages (EmpathyTpChat *self)
1805 empathy_tp_chat_acknowledge_messages (self,
1806 (GSList *) empathy_tp_chat_get_pending_messages (self));
1810 empathy_tp_chat_password_needed (EmpathyTpChat *self)
1812 return self->priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
1816 provide_password_cb (TpChannel *channel,
1818 const GError *error,
1820 GObject *weak_object)
1822 GSimpleAsyncResult *result = user_data;
1824 if (error != NULL) {
1825 g_simple_async_result_set_from_error (result, error);
1827 else if (!correct) {
1828 /* The current D-Bus API is a bit weird so re-use the
1829 * AuthenticationFailed error */
1830 g_simple_async_result_set_error (result, TP_ERRORS,
1831 TP_ERROR_AUTHENTICATION_FAILED, "Wrong password");
1834 g_simple_async_result_complete (result);
1835 g_object_unref (result);
1839 empathy_tp_chat_provide_password_async (EmpathyTpChat *self,
1840 const gchar *password,
1841 GAsyncReadyCallback callback,
1844 GSimpleAsyncResult *result;
1846 result = g_simple_async_result_new (G_OBJECT (self),
1847 callback, user_data,
1848 empathy_tp_chat_provide_password_finish);
1850 tp_cli_channel_interface_password_call_provide_password
1851 ((TpChannel *) self, -1, password, provide_password_cb, result,
1852 NULL, G_OBJECT (self));
1856 empathy_tp_chat_provide_password_finish (EmpathyTpChat *self,
1857 GAsyncResult *result,
1860 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1864 g_return_val_if_fail (g_simple_async_result_is_valid (result,
1865 G_OBJECT (self), empathy_tp_chat_provide_password_finish), FALSE);
1871 * empathy_tp_chat_can_add_contact:
1873 * Returns: %TRUE if empathy_contact_list_add() will work for this channel.
1874 * That is if this chat is a 1-to-1 channel that can be upgraded to
1875 * a MUC using the Conference interface or if the channel is a MUC.
1878 empathy_tp_chat_can_add_contact (EmpathyTpChat *self)
1880 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), FALSE);
1882 return self->priv->can_upgrade_to_muc ||
1883 tp_proxy_has_interface_by_id (self,
1884 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP);;
1888 tp_channel_leave_async_cb (GObject *source_object,
1892 GError *error = NULL;
1894 if (!tp_channel_leave_finish (TP_CHANNEL (source_object), res, &error)) {
1895 DEBUG ("Could not leave channel properly: (%s); closing the channel",
1897 g_error_free (error);
1902 empathy_tp_chat_leave (EmpathyTpChat *self,
1903 const gchar *message)
1905 TpChannel *channel = (TpChannel *) self;
1907 DEBUG ("Leaving channel %s with message \"%s\"",
1908 tp_channel_get_identifier (channel), message);
1910 tp_channel_leave_async (channel, TP_CHANNEL_GROUP_CHANGE_REASON_NONE,
1911 message, tp_channel_leave_async_cb, self);
1915 add_members_cb (TpChannel *proxy,
1916 const GError *error,
1918 GObject *weak_object)
1920 EmpathyTpChat *self = (EmpathyTpChat *) weak_object;
1922 if (error != NULL) {
1923 DEBUG ("Failed to join chat (%s): %s",
1924 tp_channel_get_identifier ((TpChannel *) self), error->message);
1929 empathy_tp_chat_join (EmpathyTpChat *self)
1931 TpHandle self_handle;
1934 self_handle = tp_channel_group_get_self_handle ((TpChannel *) self);
1936 members = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1);
1937 g_array_append_val (members, self_handle);
1939 tp_cli_channel_interface_group_call_add_members ((TpChannel *) self, -1, members,
1940 "", add_members_cb, NULL, NULL, G_OBJECT (self));
1942 g_array_free (members, TRUE);
1946 empathy_tp_chat_is_invited (EmpathyTpChat *self,
1949 TpHandle self_handle;
1951 if (!tp_proxy_has_interface (self, TP_IFACE_CHANNEL_INTERFACE_GROUP))
1954 self_handle = tp_channel_group_get_self_handle ((TpChannel *) self);
1955 if (self_handle == 0)
1958 return tp_channel_group_get_local_pending_info ((TpChannel *) self, self_handle,
1959 inviter, NULL, NULL);
1963 empathy_tp_chat_get_chat_state (EmpathyTpChat *self,
1964 EmpathyContact *contact)
1966 return tp_channel_get_chat_state ((TpChannel *) self,
1967 empathy_contact_get_handle (contact));
1971 empathy_tp_chat_get_self_contact (EmpathyTpChat *self)
1973 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
1975 return self->priv->user;
1979 empathy_tp_chat_is_sms_channel (EmpathyTpChat *self)
1981 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), FALSE);
1983 return self->priv->sms_channel;