1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program 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 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
23 #include <libtelepathy/tp-helpers.h>
24 #include <libtelepathy/tp-chan-type-text-gen.h>
25 #include <libtelepathy/tp-chan-iface-chat-state-gen.h>
27 #include "empathy-tp-chat.h"
28 #include "empathy-contact-manager.h"
29 #include "empathy-contact-list.h"
30 #include "empathy-session.h"
31 #include "empathy-marshal.h"
32 #include "gossip-debug.h"
33 #include "gossip-time.h"
35 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
36 EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv))
38 #define DEBUG_DOMAIN "TpChat"
40 struct _EmpathyTpChatPriv {
41 EmpathyContactList *list;
43 DBusGProxy *text_iface;
44 DBusGProxy *chat_state_iface;
47 static void empathy_tp_chat_class_init (EmpathyTpChatClass *klass);
48 static void empathy_tp_chat_init (EmpathyTpChat *chat);
49 static void tp_chat_finalize (GObject *object);
50 static void tp_chat_destroy_cb (TpChan *text_chan,
52 static void tp_chat_received_cb (DBusGProxy *text_iface,
60 static void tp_chat_sent_cb (DBusGProxy *text_iface,
65 static void tp_chat_state_changed_cb (DBusGProxy *chat_state_iface,
67 EmpathyTpChatState state,
69 static void tp_chat_emit_message (EmpathyTpChat *chat,
73 const gchar *message_body);
82 static guint signals[LAST_SIGNAL];
84 G_DEFINE_TYPE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT);
87 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
89 GObjectClass *object_class = G_OBJECT_CLASS (klass);
91 object_class->finalize = tp_chat_finalize;
93 signals[MESSAGE_RECEIVED] =
94 g_signal_new ("message-received",
95 G_TYPE_FROM_CLASS (klass),
99 g_cclosure_marshal_VOID__OBJECT,
101 1, GOSSIP_TYPE_MESSAGE);
103 signals[CHAT_STATE_CHANGED] =
104 g_signal_new ("chat-state-changed",
105 G_TYPE_FROM_CLASS (klass),
109 empathy_marshal_VOID__OBJECT_UINT,
111 2, GOSSIP_TYPE_CONTACT, G_TYPE_UINT);
114 g_signal_new ("destroy",
115 G_TYPE_FROM_CLASS (klass),
119 g_cclosure_marshal_VOID__VOID,
123 g_type_class_add_private (object_class, sizeof (EmpathyTpChatPriv));
127 empathy_tp_chat_init (EmpathyTpChat *chat)
133 tp_chat_finalize (GObject *object)
135 EmpathyTpChatPriv *priv;
137 GError *error = NULL;
139 chat = EMPATHY_TP_CHAT (object);
140 priv = GET_PRIV (chat);
143 gossip_debug (DEBUG_DOMAIN, "Closing channel...");
145 g_signal_handlers_disconnect_by_func (priv->tp_chan,
149 if (!tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error)) {
150 gossip_debug (DEBUG_DOMAIN,
151 "Error closing text channel: %s",
152 error ? error->message : "No error given");
153 g_clear_error (&error);
155 g_object_unref (priv->tp_chan);
159 g_object_unref (priv->list);
162 G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
166 empathy_tp_chat_new (McAccount *account,
169 EmpathyTpChatPriv *priv;
171 EmpathyContactManager *manager;
173 g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
174 g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
176 chat = g_object_new (EMPATHY_TYPE_TP_CHAT, NULL);
177 priv = GET_PRIV (chat);
179 manager = empathy_session_get_contact_manager ();
180 priv->list = empathy_contact_manager_get_list (manager, account);
181 priv->tp_chan = g_object_ref (tp_chan);
182 g_object_ref (priv->list);
184 priv->text_iface = tp_chan_get_interface (tp_chan,
185 TELEPATHY_CHAN_IFACE_TEXT_QUARK);
186 priv->chat_state_iface = tp_chan_get_interface (tp_chan,
187 TELEPATHY_CHAN_IFACE_CHAT_STATE_QUARK);
189 g_signal_connect (priv->tp_chan, "destroy",
190 G_CALLBACK (tp_chat_destroy_cb),
192 dbus_g_proxy_connect_signal (priv->text_iface, "Received",
193 G_CALLBACK (tp_chat_received_cb),
195 dbus_g_proxy_connect_signal (priv->text_iface, "Sent",
196 G_CALLBACK (tp_chat_sent_cb),
199 if (priv->chat_state_iface != NULL) {
200 dbus_g_proxy_connect_signal (priv->chat_state_iface,
202 G_CALLBACK (tp_chat_state_changed_cb),
210 empathy_tp_chat_new_with_contact (GossipContact *contact)
217 const gchar *bus_name;
220 g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
222 mc = empathy_session_get_mission_control ();
223 account = gossip_contact_get_account (contact);
225 if (mission_control_get_connection_status (mc, account, NULL) != 0) {
226 /* The account is not connected, nothing to do. */
230 tp_conn = mission_control_get_connection (mc, account, NULL);
231 g_return_val_if_fail (tp_conn != NULL, NULL);
232 bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
233 handle = gossip_contact_get_handle (contact);
235 text_chan = tp_conn_new_channel (tp_get_bus (),
238 TP_IFACE_CHANNEL_TYPE_TEXT,
239 TP_HANDLE_TYPE_CONTACT,
243 chat = empathy_tp_chat_new (account, text_chan);
245 g_object_unref (tp_conn);
246 g_object_unref (text_chan);
252 empathy_tp_chat_request_pending (EmpathyTpChat *chat)
254 EmpathyTpChatPriv *priv;
255 GPtrArray *messages_list;
257 GError *error = NULL;
259 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
261 priv = GET_PRIV (chat);
263 /* If we do this call async, don't forget to ignore Received signal
264 * until we get the answer */
265 if (!tp_chan_type_text_list_pending_messages (priv->text_iface,
269 gossip_debug (DEBUG_DOMAIN,
270 "Error retrieving pending messages: %s",
271 error ? error->message : "No error given");
272 g_clear_error (&error);
276 for (i = 0; i < messages_list->len; i++) {
277 GValueArray *message_struct;
278 const gchar *message_body;
285 message_struct = g_ptr_array_index (messages_list, i);
287 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
288 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
289 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
290 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
291 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
292 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
294 gossip_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
296 tp_chat_emit_message (chat,
302 g_value_array_free (message_struct);
305 g_ptr_array_free (messages_list, TRUE);
309 empathy_tp_chat_send (EmpathyTpChat *chat,
310 GossipMessage *message)
312 EmpathyTpChatPriv *priv;
313 const gchar *message_body;
314 GossipMessageType message_type;
315 GError *error = NULL;
317 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
318 g_return_if_fail (GOSSIP_IS_MESSAGE (message));
320 priv = GET_PRIV (chat);
322 message_body = gossip_message_get_body (message);
323 message_type = gossip_message_get_type (message);
325 gossip_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
326 if (!tp_chan_type_text_send (priv->text_iface,
330 gossip_debug (DEBUG_DOMAIN,
332 error ? error->message : "No error given");
333 g_clear_error (&error);
338 empathy_tp_chat_send_state (EmpathyTpChat *chat,
339 EmpathyTpChatState state)
341 EmpathyTpChatPriv *priv;
342 GError *error = NULL;
344 g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
346 priv = GET_PRIV (chat);
348 if (priv->chat_state_iface) {
349 gossip_debug (DEBUG_DOMAIN, "Set state: %d", state);
350 if (!tp_chan_iface_chat_state_set_chat_state (priv->chat_state_iface,
353 gossip_debug (DEBUG_DOMAIN,
354 "Set Chat State Error: %s",
355 error ? error->message : "No error given");
356 g_clear_error (&error);
362 tp_chat_destroy_cb (TpChan *text_chan,
365 EmpathyTpChatPriv *priv;
367 priv = GET_PRIV (chat);
369 gossip_debug (DEBUG_DOMAIN, "Channel destroyed");
371 g_object_unref (priv->tp_chan);
372 priv->tp_chan = NULL;
373 priv->text_iface = NULL;
374 priv->chat_state_iface = NULL;
376 g_signal_emit (chat, signals[DESTROY], 0);
380 tp_chat_received_cb (DBusGProxy *text_iface,
389 EmpathyTpChatPriv *priv;
392 priv = GET_PRIV (chat);
394 gossip_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
396 tp_chat_emit_message (chat,
402 message_ids = g_array_new (FALSE, FALSE, sizeof (guint));
403 g_array_append_val (message_ids, message_id);
404 tp_chan_type_text_acknowledge_pending_messages (priv->text_iface,
406 g_array_free (message_ids, TRUE);
410 tp_chat_sent_cb (DBusGProxy *text_iface,
416 gossip_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
418 tp_chat_emit_message (chat,
426 tp_chat_state_changed_cb (DBusGProxy *chat_state_iface,
428 EmpathyTpChatState state,
431 EmpathyTpChatPriv *priv;
432 GossipContact *contact;
434 priv = GET_PRIV (chat);
436 contact = empathy_contact_list_get_from_handle (priv->list, handle);
438 g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
440 g_object_unref (contact);
444 tp_chat_emit_message (EmpathyTpChat *chat,
448 const gchar *message_body)
450 EmpathyTpChatPriv *priv;
451 GossipMessage *message;
452 GossipContact *sender;
454 priv = GET_PRIV (chat);
456 if (from_handle == 0) {
457 sender = empathy_contact_list_get_own (priv->list);
458 g_object_ref (sender);
460 sender = empathy_contact_list_get_from_handle (priv->list,
464 message = gossip_message_new (message_body);
465 gossip_message_set_type (message, type);
466 gossip_message_set_sender (message, sender);
467 gossip_message_set_timestamp (message, (GossipTime) timestamp);
469 g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
471 g_object_unref (message);
472 g_object_unref (sender);