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