]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-chat.c
d63e82551918b699e0975dd6a1333cf7496a2fac
[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 <string.h>
26
27 #include <libtelepathy/tp-chan-type-text-gen.h>
28 #include <libtelepathy/tp-chan-iface-chat-state-gen.h>
29 #include <libtelepathy/tp-conn.h>
30 #include <libtelepathy/tp-helpers.h>
31 #include <libtelepathy/tp-props-iface.h>
32
33 #include "empathy-tp-chat.h"
34 #include "empathy-contact-manager.h"
35 #include "empathy-tp-contact-list.h"
36 #include "empathy-marshal.h"
37 #include "gossip-debug.h"
38 #include "gossip-time.h"
39 #include "gossip-utils.h"
40
41 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
42                        EMPATHY_TYPE_TP_CHAT, EmpathyTpChatPriv))
43
44 #define DEBUG_DOMAIN "TpChat"
45
46 struct _EmpathyTpChatPriv {
47         EmpathyTpContactList  *list;
48         EmpathyContactManager *manager;
49         McAccount             *account;
50         gchar                 *id;
51         MissionControl        *mc;
52
53         TpChan                *tp_chan;
54         DBusGProxy            *props_iface;
55         DBusGProxy            *text_iface;
56         DBusGProxy            *chat_state_iface;
57 };
58
59 static void      empathy_tp_chat_class_init    (EmpathyTpChatClass        *klass);
60 static void      empathy_tp_chat_init          (EmpathyTpChat             *chat);
61 static void      tp_chat_finalize              (GObject                   *object);
62 static GObject * tp_chat_constructor           (GType                      type,
63                                                 guint                      n_props,
64                                                 GObjectConstructParam     *props);
65 static void      tp_chat_get_property          (GObject                   *object,
66                                                 guint                      param_id,
67                                                 GValue                    *value,
68                                                 GParamSpec                *pspec);
69 static void      tp_chat_set_property          (GObject                   *object,
70                                                 guint                      param_id,
71                                                 const GValue              *value,
72                                                 GParamSpec                *pspec);
73 static void      tp_chat_destroy_cb            (TpChan                    *text_chan,
74                                                 EmpathyTpChat             *chat);
75 static void      tp_chat_closed_cb             (TpChan                    *text_chan,
76                                                 EmpathyTpChat             *chat);
77 static void      tp_chat_received_cb           (DBusGProxy                *text_iface,
78                                                 guint                      message_id,
79                                                 guint                      timestamp,
80                                                 guint                      from_handle,
81                                                 guint                      message_type,
82                                                 guint                      message_flags,
83                                                 gchar                     *message_body,
84                                                 EmpathyTpChat             *chat);
85 static void      tp_chat_sent_cb               (DBusGProxy                *text_iface,
86                                                 guint                      timestamp,
87                                                 guint                      message_type,
88                                                 gchar                     *message_body,
89                                                 EmpathyTpChat             *chat);
90 static void      tp_chat_state_changed_cb      (DBusGProxy                *chat_state_iface,
91                                                 guint                      handle,
92                                                 TelepathyChannelChatState  state,
93                                                 EmpathyTpChat             *chat);
94 static void      tp_chat_emit_message          (EmpathyTpChat             *chat,
95                                                 guint                      type,
96                                                 guint                      timestamp,
97                                                 guint                      from_handle,
98                                                 const gchar               *message_body);
99 static void      tp_chat_properties_ready_cb   (TpPropsIface              *props_iface,
100                                                 EmpathyTpChat             *chat);
101 static void      tp_chat_properties_changed_cb (TpPropsIface              *props_iface,
102                                                 guint                      prop_id,
103                                                 TpPropsChanged             flag,
104                                                 EmpathyTpChat             *chat);
105 enum {
106         PROP_0,
107         PROP_ACCOUNT,
108         PROP_TP_CHAN,
109
110         PROP_ANONYMOUS,
111         PROP_INVITE_ONLY,
112         PROP_LIMIT,
113         PROP_LIMITED,
114         PROP_MODERATED,
115         PROP_NAME,
116         PROP_DESCRIPTION,
117         PROP_PASSWORD,
118         PROP_PASSWORD_REQUIRED,
119         PROP_PERSISTENT,
120         PROP_PRIVATE,
121         PROP_SUBJECT,
122         PROP_SUBJECT_CONTACT,
123         PROP_SUBJECT_TIMESTAMP
124 };
125
126 enum {
127         MESSAGE_RECEIVED,
128         CHAT_STATE_CHANGED,
129         DESTROY,
130         LAST_SIGNAL
131 };
132
133 static guint signals[LAST_SIGNAL];
134
135 G_DEFINE_TYPE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT);
136
137 static void
138 empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
139 {
140         GObjectClass *object_class = G_OBJECT_CLASS (klass);
141
142         object_class->finalize = tp_chat_finalize;
143         object_class->constructor = tp_chat_constructor;
144         object_class->get_property = tp_chat_get_property;
145         object_class->set_property = tp_chat_set_property;
146
147         /* Construct-only properties */
148         g_object_class_install_property (object_class,
149                                          PROP_ACCOUNT,
150                                          g_param_spec_object ("account",
151                                                               "channel Account",
152                                                               "The account associated with the channel",
153                                                               MC_TYPE_ACCOUNT,
154                                                               G_PARAM_READWRITE |
155                                                               G_PARAM_CONSTRUCT_ONLY));
156         g_object_class_install_property (object_class,
157                                          PROP_TP_CHAN,
158                                          g_param_spec_object ("tp-chan",
159                                                               "telepathy channel",
160                                                               "The text channel for the chat",
161                                                               TELEPATHY_CHAN_TYPE,
162                                                               G_PARAM_READWRITE |
163                                                               G_PARAM_CONSTRUCT_ONLY));
164
165         /* Properties of Text Channel */
166         g_object_class_install_property (object_class,
167                                          PROP_ANONYMOUS,
168                                          g_param_spec_boolean ("anonymous",
169                                                                "anonymous",
170                                                                "anonymous",
171                                                                FALSE,
172                                                                G_PARAM_READWRITE));
173         g_object_class_install_property (object_class,
174                                          PROP_INVITE_ONLY,
175                                          g_param_spec_boolean ("invite-only",
176                                                                "invite-only",
177                                                                "invite-only",
178                                                                FALSE,
179                                                                G_PARAM_READWRITE));
180         g_object_class_install_property (object_class,
181                                          PROP_LIMIT,
182                                          g_param_spec_uint ("limit",
183                                                             "limit",
184                                                             "limit",
185                                                             0,
186                                                             G_MAXUINT,
187                                                             0,
188                                                             G_PARAM_READWRITE));
189         g_object_class_install_property (object_class,
190                                          PROP_LIMITED,
191                                          g_param_spec_boolean ("limited",
192                                                                "limited",
193                                                                "limited",
194                                                                FALSE,
195                                                                G_PARAM_READWRITE));
196         g_object_class_install_property (object_class,
197                                          PROP_MODERATED,
198                                          g_param_spec_boolean ("moderated",
199                                                                "moderated",
200                                                                "moderated",
201                                                                FALSE,
202                                                                G_PARAM_READWRITE));
203         g_object_class_install_property (object_class,
204                                          PROP_NAME,
205                                          g_param_spec_string ("name",
206                                                               "name",
207                                                               "name",
208                                                               NULL,
209                                                               G_PARAM_READWRITE));
210         g_object_class_install_property (object_class,
211                                          PROP_DESCRIPTION,
212                                          g_param_spec_string ("description",
213                                                               "description",
214                                                               "description",
215                                                               NULL,
216                                                               G_PARAM_READWRITE));
217         g_object_class_install_property (object_class,
218                                          PROP_PASSWORD,
219                                          g_param_spec_string ("password",
220                                                               "password",
221                                                               "password",
222                                                               NULL,
223                                                               G_PARAM_READWRITE));
224         g_object_class_install_property (object_class,
225                                          PROP_PASSWORD_REQUIRED,
226                                          g_param_spec_boolean ("password-required",
227                                                                "password-required",
228                                                                "password-required",
229                                                                FALSE,
230                                                                G_PARAM_READWRITE));
231         g_object_class_install_property (object_class,
232                                          PROP_PERSISTENT,
233                                          g_param_spec_boolean ("persistent",
234                                                                "persistent",
235                                                                "persistent",
236                                                                FALSE,
237                                                                G_PARAM_READWRITE));
238         g_object_class_install_property (object_class,
239                                          PROP_PRIVATE,
240                                          g_param_spec_boolean ("private",
241                                                                "private",
242                                                                "private"
243                                                                "private",
244                                                                FALSE,
245                                                                G_PARAM_READWRITE));
246         g_object_class_install_property (object_class,
247                                          PROP_SUBJECT,
248                                          g_param_spec_string ("subject",
249                                                               "subject",
250                                                               "subject",
251                                                               NULL,
252                                                               G_PARAM_READWRITE));
253         g_object_class_install_property (object_class,
254                                          PROP_SUBJECT_CONTACT,
255                                          g_param_spec_uint ("subject-contact",
256                                                             "subject-contact",
257                                                             "subject-contact",
258                                                             0,
259                                                             G_MAXUINT,
260                                                             0,
261                                                             G_PARAM_READWRITE));
262         g_object_class_install_property (object_class,
263                                          PROP_SUBJECT_TIMESTAMP,
264                                          g_param_spec_uint ("subject-timestamp",
265                                                             "subject-timestamp",
266                                                             "subject-timestamp",
267                                                             0,
268                                                             G_MAXUINT,
269                                                             0,
270                                                             G_PARAM_READWRITE));
271
272         /* Signals */
273         signals[MESSAGE_RECEIVED] =
274                 g_signal_new ("message-received",
275                               G_TYPE_FROM_CLASS (klass),
276                               G_SIGNAL_RUN_LAST,
277                               0,
278                               NULL, NULL,
279                               g_cclosure_marshal_VOID__OBJECT,
280                               G_TYPE_NONE,
281                               1, GOSSIP_TYPE_MESSAGE);
282
283         signals[CHAT_STATE_CHANGED] =
284                 g_signal_new ("chat-state-changed",
285                               G_TYPE_FROM_CLASS (klass),
286                               G_SIGNAL_RUN_LAST,
287                               0,
288                               NULL, NULL,
289                               empathy_marshal_VOID__OBJECT_UINT,
290                               G_TYPE_NONE,
291                               2, GOSSIP_TYPE_CONTACT, G_TYPE_UINT);
292
293         signals[DESTROY] =
294                 g_signal_new ("destroy",
295                               G_TYPE_FROM_CLASS (klass),
296                               G_SIGNAL_RUN_LAST,
297                               0,
298                               NULL, NULL,
299                               g_cclosure_marshal_VOID__VOID,
300                               G_TYPE_NONE,
301                               0);
302
303         g_type_class_add_private (object_class, sizeof (EmpathyTpChatPriv));
304 }
305
306 static void
307 empathy_tp_chat_init (EmpathyTpChat *chat)
308 {
309 }
310
311
312 static void
313 tp_chat_finalize (GObject *object)
314 {
315         EmpathyTpChatPriv *priv;
316         EmpathyTpChat     *chat;
317         GError            *error = NULL;
318
319         chat = EMPATHY_TP_CHAT (object);
320         priv = GET_PRIV (chat);
321
322         if (priv->tp_chan) {
323                 gossip_debug (DEBUG_DOMAIN, "Closing channel...");
324
325                 g_signal_handlers_disconnect_by_func (priv->tp_chan,
326                                                       tp_chat_destroy_cb,
327                                                       object);
328
329                 if (!tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error)) {
330                         gossip_debug (DEBUG_DOMAIN, 
331                                       "Error closing text channel: %s",
332                                       error ? error->message : "No error given");
333                         g_clear_error (&error);
334                 }
335                 g_object_unref (priv->tp_chan);
336         }
337
338         if (priv->manager) {
339                 g_object_unref (priv->manager);
340         }
341         if (priv->list) {
342                 g_object_unref (priv->list);
343         }
344         if (priv->account) {
345                 g_object_unref (priv->account);
346         }
347         if (priv->mc) {
348                 g_object_unref (priv->mc);
349         }
350         g_free (priv->id);
351
352         G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
353 }
354
355 static GObject *
356 tp_chat_constructor (GType                  type,
357                      guint                  n_props,
358                      GObjectConstructParam *props)
359 {
360         GObject           *chat;
361         EmpathyTpChatPriv *priv;
362
363         chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
364
365         priv = GET_PRIV (chat);
366
367         priv->manager = empathy_contact_manager_new ();
368         priv->list = empathy_contact_manager_get_list (priv->manager, priv->account);
369         priv->mc = gossip_mission_control_new ();
370         g_object_ref (priv->list);
371
372         priv->text_iface = tp_chan_get_interface (priv->tp_chan,
373                                                   TELEPATHY_CHAN_IFACE_TEXT_QUARK);
374         priv->chat_state_iface = tp_chan_get_interface (priv->tp_chan,
375                                                         TELEPATHY_CHAN_IFACE_CHAT_STATE_QUARK);
376         priv->props_iface = tp_chan_get_interface (priv->tp_chan,
377                                                    TELEPATHY_PROPS_IFACE_QUARK);
378
379         g_signal_connect (priv->tp_chan, "destroy",
380                           G_CALLBACK (tp_chat_destroy_cb),
381                           chat);
382         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed",
383                                      G_CALLBACK (tp_chat_closed_cb),
384                                      chat, NULL);
385         dbus_g_proxy_connect_signal (priv->text_iface, "Received",
386                                      G_CALLBACK (tp_chat_received_cb),
387                                      chat, NULL);
388         dbus_g_proxy_connect_signal (priv->text_iface, "Sent",
389                                      G_CALLBACK (tp_chat_sent_cb),
390                                      chat, NULL);
391
392         if (priv->chat_state_iface != NULL) {
393                 dbus_g_proxy_connect_signal (priv->chat_state_iface,
394                                              "ChatStateChanged",
395                                              G_CALLBACK (tp_chat_state_changed_cb),
396                                              chat, NULL);
397         }
398         if (priv->props_iface != NULL) {
399                 tp_props_iface_set_mapping (TELEPATHY_PROPS_IFACE (priv->props_iface),
400                                             "anonymous", PROP_ANONYMOUS,
401                                             "invite-only", PROP_INVITE_ONLY,
402                                             "limit", PROP_LIMIT,
403                                             "limited", PROP_LIMITED,
404                                             "moderated", PROP_MODERATED,
405                                             "name", PROP_NAME,
406                                             "description", PROP_DESCRIPTION,
407                                             "password", PROP_PASSWORD,
408                                             "password-required", PROP_PASSWORD_REQUIRED,
409                                             "persistent", PROP_PERSISTENT,
410                                             "private", PROP_PRIVATE,
411                                             "subject", PROP_SUBJECT,
412                                             "subject-contact", PROP_SUBJECT_CONTACT,
413                                             "subject-timestamp", PROP_SUBJECT_TIMESTAMP,
414                                             NULL);
415                 g_signal_connect (priv->props_iface, "properties-ready",
416                                   G_CALLBACK (tp_chat_properties_ready_cb),
417                                   chat);
418                 g_signal_connect (priv->props_iface, "properties-changed",
419                                   G_CALLBACK (tp_chat_properties_changed_cb),
420                                   chat);
421         }
422
423         return chat;
424 }
425
426 static void
427 tp_chat_get_property (GObject    *object,
428                       guint       param_id,
429                       GValue     *value,
430                       GParamSpec *pspec)
431 {
432         EmpathyTpChatPriv *priv;
433         EmpathyTpChat     *chat;
434
435         priv = GET_PRIV (object);
436         chat = EMPATHY_TP_CHAT (object);
437
438         if (param_id >= PROP_ANONYMOUS &&
439             param_id <= PROP_SUBJECT_TIMESTAMP) {
440                 if (priv->props_iface) {
441                         tp_props_iface_get_value (TELEPATHY_PROPS_IFACE (priv->props_iface),
442                                                   param_id,
443                                                   value);
444                 }
445
446                 return;
447         }
448
449         switch (param_id) {
450         case PROP_ACCOUNT:
451                 g_value_set_object (value, priv->account);
452                 break;
453         case PROP_TP_CHAN:
454                 g_value_set_object (value, priv->tp_chan);
455                 break;
456         default:
457                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
458                 break;
459         };
460 }
461
462 static void
463 tp_chat_set_property (GObject      *object,
464                       guint         param_id,
465                       const GValue *value,
466                       GParamSpec   *pspec)
467 {
468         EmpathyTpChatPriv *priv;
469         EmpathyTpChat     *chat;
470
471         priv = GET_PRIV (object);
472         chat = EMPATHY_TP_CHAT (object);
473
474         if (param_id >= PROP_ANONYMOUS &&
475             param_id <= PROP_SUBJECT_TIMESTAMP) {
476                 if (priv->props_iface) {
477                         tp_props_iface_set_value (TELEPATHY_PROPS_IFACE (priv->props_iface),
478                                                   param_id,
479                                                   value);
480                 }
481
482                 return;
483         }
484
485         switch (param_id) {
486         case PROP_ACCOUNT:
487                 priv->account = g_object_ref (g_value_get_object (value));
488                 break;
489         case PROP_TP_CHAN:
490                 priv->tp_chan = g_object_ref (g_value_get_object (value));
491                 break;
492         default:
493                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
494                 break;
495         };
496 }
497
498 EmpathyTpChat *
499 empathy_tp_chat_new (McAccount *account,
500                      TpChan    *tp_chan)
501 {
502         return g_object_new (EMPATHY_TYPE_TP_CHAT, 
503                              "account", account,
504                              "tp-chan", tp_chan,
505                              NULL);
506 }
507
508 EmpathyTpChat *
509 empathy_tp_chat_new_with_contact (GossipContact *contact)
510 {
511         EmpathyTpChat  *chat;
512         MissionControl *mc;
513         McAccount      *account;
514         TpConn         *tp_conn;
515         TpChan         *text_chan;
516         const gchar    *bus_name;
517         guint           handle;
518
519         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
520
521         mc = gossip_mission_control_new ();
522         account = gossip_contact_get_account (contact);
523
524         if (mission_control_get_connection_status (mc, account, NULL) != 0) {
525                 /* The account is not connected, nothing to do. */
526                 return NULL;
527         }
528
529         tp_conn = mission_control_get_connection (mc, account, NULL);
530         g_return_val_if_fail (tp_conn != NULL, NULL);
531         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
532         handle = gossip_contact_get_handle (contact);
533
534         text_chan = tp_conn_new_channel (tp_get_bus (),
535                                          tp_conn,
536                                          bus_name,
537                                          TP_IFACE_CHANNEL_TYPE_TEXT,
538                                          TP_HANDLE_TYPE_CONTACT,
539                                          handle,
540                                          TRUE);
541
542         chat = empathy_tp_chat_new (account, text_chan);
543
544         g_object_unref (tp_conn);
545         g_object_unref (text_chan);
546         g_object_unref (mc);
547
548         return chat;
549 }
550
551 void
552 empathy_tp_chat_request_pending (EmpathyTpChat *chat)
553 {
554         EmpathyTpChatPriv *priv;
555         GPtrArray         *messages_list;
556         guint              i;
557         GError            *error = NULL;
558
559         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
560
561         priv = GET_PRIV (chat);
562
563         /* If we do this call async, don't forget to ignore Received signal
564          * until we get the answer */
565         if (!tp_chan_type_text_list_pending_messages (priv->text_iface,
566                                                       TRUE,
567                                                       &messages_list,
568                                                       &error)) {
569                 gossip_debug (DEBUG_DOMAIN, 
570                               "Error retrieving pending messages: %s",
571                               error ? error->message : "No error given");
572                 g_clear_error (&error);
573                 return;
574         }
575
576         for (i = 0; i < messages_list->len; i++) {
577                 GValueArray *message_struct;
578                 const gchar *message_body;
579                 guint        message_id;
580                 guint        timestamp;
581                 guint        from_handle;
582                 guint        message_type;
583                 guint        message_flags;
584
585                 message_struct = g_ptr_array_index (messages_list, i);
586
587                 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
588                 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
589                 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
590                 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
591                 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
592                 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
593
594                 gossip_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
595
596                 tp_chat_emit_message (chat,
597                                       message_type,
598                                       timestamp,
599                                       from_handle,
600                                       message_body);
601
602                 g_value_array_free (message_struct);
603         }
604
605         g_ptr_array_free (messages_list, TRUE);
606 }
607
608 void
609 empathy_tp_chat_send (EmpathyTpChat *chat,
610                       GossipMessage *message)
611 {
612         EmpathyTpChatPriv *priv;
613         const gchar       *message_body;
614         GossipMessageType  message_type;
615         GError            *error = NULL;
616
617         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
618         g_return_if_fail (GOSSIP_IS_MESSAGE (message));
619
620         priv = GET_PRIV (chat);
621
622         message_body = gossip_message_get_body (message);
623         message_type = gossip_message_get_type (message);
624
625         gossip_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
626         if (!tp_chan_type_text_send (priv->text_iface,
627                                      message_type,
628                                      message_body,
629                                      &error)) {
630                 gossip_debug (DEBUG_DOMAIN, 
631                               "Send Error: %s", 
632                               error ? error->message : "No error given");
633                 g_clear_error (&error);
634         }
635 }
636
637 void
638 empathy_tp_chat_set_state (EmpathyTpChat             *chat,
639                            TelepathyChannelChatState  state)
640 {
641         EmpathyTpChatPriv *priv;
642         GError            *error = NULL;
643
644         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
645
646         priv = GET_PRIV (chat);
647
648         if (priv->chat_state_iface) {
649                 gossip_debug (DEBUG_DOMAIN, "Set state: %d", state);
650                 if (!tp_chan_iface_chat_state_set_chat_state (priv->chat_state_iface,
651                                                               state,
652                                                               &error)) {
653                         gossip_debug (DEBUG_DOMAIN,
654                                       "Set Chat State Error: %s",
655                                       error ? error->message : "No error given");
656                         g_clear_error (&error);
657                 }
658         }
659 }
660
661 const gchar *
662 empathy_tp_chat_get_id (EmpathyTpChat *chat)
663 {
664         EmpathyTpChatPriv *priv;
665
666         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
667
668         priv = GET_PRIV (chat);
669
670         if (priv->id) {
671                 return priv->id;
672         }
673
674         priv->id = gossip_get_channel_id (priv->account, priv->tp_chan);
675
676         return priv->id;
677 }
678
679 static void
680 tp_chat_destroy_cb (TpChan        *text_chan,
681                     EmpathyTpChat *chat)
682 {
683         EmpathyTpChatPriv *priv;
684
685         priv = GET_PRIV (chat);
686
687         gossip_debug (DEBUG_DOMAIN, "Channel Closed or CM crashed");
688
689         g_object_unref  (priv->tp_chan);
690         priv->tp_chan = NULL;
691         priv->text_iface = NULL;
692         priv->chat_state_iface = NULL;
693         priv->props_iface = NULL;
694
695         g_signal_emit (chat, signals[DESTROY], 0);
696 }
697
698 static void
699 tp_chat_closed_cb (TpChan        *text_chan,
700                    EmpathyTpChat *chat)
701 {
702         EmpathyTpChatPriv *priv;
703
704         priv = GET_PRIV (chat);
705
706         /* The channel is closed, do just like if the proxy was destroyed */
707         g_signal_handlers_disconnect_by_func (priv->tp_chan,
708                                               tp_chat_destroy_cb,
709                                               chat);
710         tp_chat_destroy_cb (text_chan, chat);
711
712 }
713
714 static void
715 tp_chat_received_cb (DBusGProxy    *text_iface,
716                      guint          message_id,
717                      guint          timestamp,
718                      guint          from_handle,
719                      guint          message_type,
720                      guint          message_flags,
721                      gchar         *message_body,
722                      EmpathyTpChat *chat)
723 {
724         EmpathyTpChatPriv *priv;
725         GArray            *message_ids;
726
727         priv = GET_PRIV (chat);
728
729         gossip_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
730
731         tp_chat_emit_message (chat,
732                               message_type,
733                               timestamp,
734                               from_handle,
735                               message_body);
736
737         message_ids = g_array_new (FALSE, FALSE, sizeof (guint));
738         g_array_append_val (message_ids, message_id);
739         tp_chan_type_text_acknowledge_pending_messages (priv->text_iface,
740                                                         message_ids, NULL);
741         g_array_free (message_ids, TRUE);
742 }
743
744 static void
745 tp_chat_sent_cb (DBusGProxy    *text_iface,
746                  guint          timestamp,
747                  guint          message_type,
748                  gchar         *message_body,
749                  EmpathyTpChat *chat)
750 {
751         gossip_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
752
753         tp_chat_emit_message (chat,
754                               message_type,
755                               timestamp,
756                               0,
757                               message_body);
758 }
759
760 static void
761 tp_chat_state_changed_cb (DBusGProxy                *chat_state_iface,
762                           guint                      handle,
763                           TelepathyChannelChatState  state,
764                           EmpathyTpChat             *chat)
765 {
766         EmpathyTpChatPriv *priv;
767         GossipContact     *contact;
768
769         priv = GET_PRIV (chat);
770
771         contact = empathy_tp_contact_list_get_from_handle (priv->list, handle);
772
773         gossip_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d",
774                       gossip_contact_get_name (contact),
775                       handle,
776                       state);
777
778         g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
779
780         g_object_unref (contact);
781 }
782
783 static void
784 tp_chat_emit_message (EmpathyTpChat *chat,
785                       guint          type,
786                       guint          timestamp,
787                       guint          from_handle,
788                       const gchar   *message_body)
789 {
790         EmpathyTpChatPriv *priv;
791         GossipMessage     *message;
792         GossipContact     *sender;
793
794         priv = GET_PRIV (chat);
795
796         if (from_handle == 0) {
797                 sender = empathy_tp_contact_list_get_user (priv->list);
798                 g_object_ref (sender);
799         } else {
800                 sender = empathy_tp_contact_list_get_from_handle (priv->list,
801                                                                   from_handle);
802         }
803
804         message = gossip_message_new (message_body);
805         gossip_message_set_type (message, type);
806         gossip_message_set_sender (message, sender);
807         gossip_message_set_timestamp (message, (GossipTime) timestamp);
808
809         g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
810
811         g_object_unref (message);
812         g_object_unref (sender);
813 }
814
815 static void
816 tp_chat_properties_ready_cb (TpPropsIface  *props_iface,
817                              EmpathyTpChat *chat)
818 {
819         g_object_notify (G_OBJECT (chat), "anonymous");
820         g_object_notify (G_OBJECT (chat), "invite-only");
821         g_object_notify (G_OBJECT (chat), "limit");
822         g_object_notify (G_OBJECT (chat), "limited");
823         g_object_notify (G_OBJECT (chat), "moderated");
824         g_object_notify (G_OBJECT (chat), "name");
825         g_object_notify (G_OBJECT (chat), "description");
826         g_object_notify (G_OBJECT (chat), "password");
827         g_object_notify (G_OBJECT (chat), "password-required");
828         g_object_notify (G_OBJECT (chat), "persistent");
829         g_object_notify (G_OBJECT (chat), "private");
830         g_object_notify (G_OBJECT (chat), "subject");
831         g_object_notify (G_OBJECT (chat), "subject-contact");
832         g_object_notify (G_OBJECT (chat), "subject-timestamp");
833 }
834
835 static void
836 tp_chat_properties_changed_cb (TpPropsIface   *props_iface,
837                                guint           prop_id,
838                                TpPropsChanged  flag,
839                                EmpathyTpChat  *chat)
840 {
841         switch (prop_id) {
842         case PROP_ANONYMOUS:
843                 g_object_notify (G_OBJECT (chat), "anonymous");
844                 break;
845         case PROP_INVITE_ONLY:
846                 g_object_notify (G_OBJECT (chat), "invite-only");
847                 break;
848         case PROP_LIMIT:
849                 g_object_notify (G_OBJECT (chat), "limit");
850                 break;
851         case PROP_LIMITED:
852                 g_object_notify (G_OBJECT (chat), "limited");
853                 break;
854         case PROP_MODERATED:
855                 g_object_notify (G_OBJECT (chat), "moderated");
856                 break;
857         case PROP_NAME:
858                 g_object_notify (G_OBJECT (chat), "name");
859                 break;
860         case PROP_DESCRIPTION:
861                 g_object_notify (G_OBJECT (chat), "description");
862                 break;
863         case PROP_PASSWORD:
864                 g_object_notify (G_OBJECT (chat), "password");
865                 break;
866         case PROP_PASSWORD_REQUIRED:
867                 g_object_notify (G_OBJECT (chat), "password-required");
868                 break;
869         case PROP_PERSISTENT:
870                 g_object_notify (G_OBJECT (chat), "persistent");
871                 break;
872         case PROP_PRIVATE:
873                 g_object_notify (G_OBJECT (chat), "private");
874                 break;
875         case PROP_SUBJECT:
876                 g_object_notify (G_OBJECT (chat), "subject");
877                 break;
878         case PROP_SUBJECT_CONTACT:
879                 g_object_notify (G_OBJECT (chat), "subject-contact");
880                 break;
881         case PROP_SUBJECT_TIMESTAMP:
882                 g_object_notify (G_OBJECT (chat), "subject-timestamp");
883                 break;
884         }
885 }
886