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