]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-chat.c
[darcs-to-svn @ Adding salut profile]
[empathy.git] / libempathy / empathy-tp-chat.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #include <config.h>
22
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>
26
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"
34
35 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
36                        EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv))
37
38 #define DEBUG_DOMAIN "TpChat"
39
40 struct _EmpathyTpChatPriv {
41         EmpathyContactList *list;
42         TpChan             *tp_chan;
43         DBusGProxy         *text_iface;
44         DBusGProxy         *chat_state_iface;
45 };
46
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,
51                                         EmpathyTpChat      *chat);
52 static void tp_chat_received_cb        (DBusGProxy         *text_iface,
53                                         guint               message_id,
54                                         guint               timestamp,
55                                         guint               from_handle,
56                                         guint               message_type,
57                                         guint               message_flags,
58                                         gchar              *message_body,
59                                         EmpathyTpChat      *chat);
60 static void tp_chat_sent_cb            (DBusGProxy         *text_iface,
61                                         guint               timestamp,
62                                         guint               message_type,
63                                         gchar              *message_body,
64                                         EmpathyTpChat      *chat);
65 static void tp_chat_state_changed_cb   (DBusGProxy         *chat_state_iface,
66                                         guint               handle,
67                                         EmpathyTpChatState  state,
68                                         EmpathyTpChat      *chat);
69 static void tp_chat_emit_message       (EmpathyTpChat      *chat,
70                                         guint               type,
71                                         guint               timestamp,
72                                         guint               from_handle,
73                                         const gchar        *message_body);
74
75 enum {
76         MESSAGE_RECEIVED,
77         CHAT_STATE_CHANGED,
78         DESTROY,
79         LAST_SIGNAL
80 };
81
82 static guint signals[LAST_SIGNAL];
83
84 G_DEFINE_TYPE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT);
85
86 static void
87 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
88 {
89         GObjectClass *object_class = G_OBJECT_CLASS (klass);
90
91         object_class->finalize = tp_chat_finalize;
92
93         signals[MESSAGE_RECEIVED] =
94                 g_signal_new ("message-received",
95                               G_TYPE_FROM_CLASS (klass),
96                               G_SIGNAL_RUN_LAST,
97                               0,
98                               NULL, NULL,
99                               g_cclosure_marshal_VOID__OBJECT,
100                               G_TYPE_NONE,
101                               1, GOSSIP_TYPE_MESSAGE);
102
103         signals[CHAT_STATE_CHANGED] =
104                 g_signal_new ("chat-state-changed",
105                               G_TYPE_FROM_CLASS (klass),
106                               G_SIGNAL_RUN_LAST,
107                               0,
108                               NULL, NULL,
109                               empathy_marshal_VOID__OBJECT_UINT,
110                               G_TYPE_NONE,
111                               2, GOSSIP_TYPE_CONTACT, G_TYPE_UINT);
112
113         signals[DESTROY] =
114                 g_signal_new ("destroy",
115                               G_TYPE_FROM_CLASS (klass),
116                               G_SIGNAL_RUN_LAST,
117                               0,
118                               NULL, NULL,
119                               g_cclosure_marshal_VOID__VOID,
120                               G_TYPE_NONE,
121                               0);
122
123         g_type_class_add_private (object_class, sizeof (EmpathyTpChatPriv));
124 }
125
126 static void
127 empathy_tp_chat_init (EmpathyTpChat *chat)
128 {
129 }
130
131
132 static void
133 tp_chat_finalize (GObject *object)
134 {
135         EmpathyTpChatPriv *priv;
136         EmpathyTpChat     *chat;
137         GError            *error = NULL;
138
139         chat = EMPATHY_TP_CHAT (object);
140         priv = GET_PRIV (chat);
141
142         if (priv->tp_chan) {
143                 gossip_debug (DEBUG_DOMAIN, "Closing channel...");
144
145                 g_signal_handlers_disconnect_by_func (priv->tp_chan,
146                                                       tp_chat_destroy_cb,
147                                                       object);
148
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);
154                 }
155                 g_object_unref (priv->tp_chan);
156         }
157
158         if (priv->list) {
159                 g_object_unref (priv->list);
160         }
161
162         G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
163 }
164
165 EmpathyTpChat *
166 empathy_tp_chat_new (McAccount *account,
167                      TpChan    *tp_chan)
168 {
169         EmpathyTpChatPriv     *priv;
170         EmpathyTpChat         *chat;
171         EmpathyContactManager *manager;
172
173         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
174         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
175
176         chat = g_object_new (EMPATHY_TYPE_TP_CHAT, NULL);
177         priv = GET_PRIV (chat);
178
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);
183
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);
188
189         g_signal_connect (priv->tp_chan, "destroy",
190                           G_CALLBACK (tp_chat_destroy_cb),
191                           chat);
192         dbus_g_proxy_connect_signal (priv->text_iface, "Received",
193                                      G_CALLBACK (tp_chat_received_cb),
194                                      chat, NULL);
195         dbus_g_proxy_connect_signal (priv->text_iface, "Sent",
196                                      G_CALLBACK (tp_chat_sent_cb),
197                                      chat, NULL);
198
199         if (priv->chat_state_iface != NULL) {
200                 dbus_g_proxy_connect_signal (priv->chat_state_iface,
201                                              "ChatStateChanged",
202                                              G_CALLBACK (tp_chat_state_changed_cb),
203                                              chat, NULL);
204         }
205
206         return chat;
207 }
208
209 EmpathyTpChat *
210 empathy_tp_chat_new_with_contact (GossipContact *contact)
211 {
212         EmpathyTpChat  *chat;
213         MissionControl *mc;
214         McAccount      *account;
215         TpConn         *tp_conn;
216         TpChan         *text_chan;
217         const gchar    *bus_name;
218         guint           handle;
219
220         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
221
222         mc = empathy_session_get_mission_control ();
223         account = gossip_contact_get_account (contact);
224
225         if (mission_control_get_connection_status (mc, account, NULL) != 0) {
226                 /* The account is not connected, nothing to do. */
227                 return NULL;
228         }
229
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);
234
235         text_chan = tp_conn_new_channel (tp_get_bus (),
236                                          tp_conn,
237                                          bus_name,
238                                          TP_IFACE_CHANNEL_TYPE_TEXT,
239                                          TP_HANDLE_TYPE_CONTACT,
240                                          handle,
241                                          TRUE);
242
243         chat = empathy_tp_chat_new (account, text_chan);
244
245         g_object_unref (tp_conn);
246         g_object_unref (text_chan);
247
248         return chat;
249 }
250
251 void
252 empathy_tp_chat_request_pending (EmpathyTpChat *chat)
253 {
254         EmpathyTpChatPriv *priv;
255         GPtrArray         *messages_list;
256         guint              i;
257         GError            *error = NULL;
258
259         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
260
261         priv = GET_PRIV (chat);
262
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,
266                                                       TRUE,
267                                                       &messages_list,
268                                                       &error)) {
269                 gossip_debug (DEBUG_DOMAIN, 
270                               "Error retrieving pending messages: %s",
271                               error ? error->message : "No error given");
272                 g_clear_error (&error);
273                 return;
274         }
275
276         for (i = 0; i < messages_list->len; i++) {
277                 GValueArray *message_struct;
278                 const gchar *message_body;
279                 guint        message_id;
280                 guint        timestamp;
281                 guint        from_handle;
282                 guint        message_type;
283                 guint        message_flags;
284
285                 message_struct = g_ptr_array_index (messages_list, i);
286
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));
293
294                 gossip_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
295
296                 tp_chat_emit_message (chat,
297                                       message_type,
298                                       timestamp,
299                                       from_handle,
300                                       message_body);
301
302                 g_value_array_free (message_struct);
303         }
304
305         g_ptr_array_free (messages_list, TRUE);
306 }
307
308 void
309 empathy_tp_chat_send (EmpathyTpChat *chat,
310                       GossipMessage *message)
311 {
312         EmpathyTpChatPriv *priv;
313         const gchar       *message_body;
314         GossipMessageType  message_type;
315         GError            *error = NULL;
316
317         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
318         g_return_if_fail (GOSSIP_IS_MESSAGE (message));
319
320         priv = GET_PRIV (chat);
321
322         message_body = gossip_message_get_body (message);
323         message_type = gossip_message_get_type (message);
324
325         gossip_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
326         if (!tp_chan_type_text_send (priv->text_iface,
327                                      message_type,
328                                      message_body,
329                                      &error)) {
330                 gossip_debug (DEBUG_DOMAIN, 
331                               "Send Error: %s", 
332                               error ? error->message : "No error given");
333                 g_clear_error (&error);
334         }
335 }
336
337 void
338 empathy_tp_chat_send_state (EmpathyTpChat      *chat,
339                             EmpathyTpChatState  state)
340 {
341         EmpathyTpChatPriv *priv;
342         GError            *error = NULL;
343
344         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
345
346         priv = GET_PRIV (chat);
347
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,
351                                                               state,
352                                                               &error)) {
353                         gossip_debug (DEBUG_DOMAIN,
354                                       "Set Chat State Error: %s",
355                                       error ? error->message : "No error given");
356                         g_clear_error (&error);
357                 }
358         }
359 }
360
361 static void
362 tp_chat_destroy_cb (TpChan        *text_chan,
363                     EmpathyTpChat *chat)
364 {
365         EmpathyTpChatPriv *priv;
366
367         priv = GET_PRIV (chat);
368
369         gossip_debug (DEBUG_DOMAIN, "Channel destroyed");
370
371         g_object_unref  (priv->tp_chan);
372         priv->tp_chan = NULL;
373         priv->text_iface = NULL;
374         priv->chat_state_iface = NULL;
375
376         g_signal_emit (chat, signals[DESTROY], 0);
377 }
378
379 static void
380 tp_chat_received_cb (DBusGProxy    *text_iface,
381                      guint          message_id,
382                      guint          timestamp,
383                      guint          from_handle,
384                      guint          message_type,
385                      guint          message_flags,
386                      gchar         *message_body,
387                      EmpathyTpChat *chat)
388 {
389         EmpathyTpChatPriv *priv;
390         GArray            *message_ids;
391
392         priv = GET_PRIV (chat);
393
394         gossip_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
395
396         tp_chat_emit_message (chat,
397                               message_type,
398                               timestamp,
399                               from_handle,
400                               message_body);
401
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,
405                                                         message_ids, NULL);
406         g_array_free (message_ids, TRUE);
407 }
408
409 static void
410 tp_chat_sent_cb (DBusGProxy    *text_iface,
411                  guint          timestamp,
412                  guint          message_type,
413                  gchar         *message_body,
414                  EmpathyTpChat *chat)
415 {
416         gossip_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
417
418         tp_chat_emit_message (chat,
419                               message_type,
420                               timestamp,
421                               0,
422                               message_body);
423 }
424
425 static void
426 tp_chat_state_changed_cb (DBusGProxy         *chat_state_iface,
427                           guint               handle,
428                           EmpathyTpChatState  state,
429                           EmpathyTpChat      *chat)
430 {
431         EmpathyTpChatPriv *priv;
432         GossipContact     *contact;
433
434         priv = GET_PRIV (chat);
435
436         contact = empathy_contact_list_get_from_handle (priv->list, handle);
437
438         g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
439
440         g_object_unref (contact);
441 }
442
443 static void
444 tp_chat_emit_message (EmpathyTpChat *chat,
445                       guint          type,
446                       guint          timestamp,
447                       guint          from_handle,
448                       const gchar   *message_body)
449 {
450         EmpathyTpChatPriv *priv;
451         GossipMessage     *message;
452         GossipContact     *sender;
453
454         priv = GET_PRIV (chat);
455
456         if (from_handle == 0) {
457                 sender = empathy_contact_list_get_own (priv->list);
458                 g_object_ref (sender);
459         } else {
460                 sender = empathy_contact_list_get_from_handle (priv->list,
461                                                                from_handle);
462         }
463
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);
468
469         g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
470
471         g_object_unref (message);
472         g_object_unref (sender);
473 }
474