]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-chat.c
Completely reworked ContactList API. Fixes bug #471611, bug #467280, bug #459540...
[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->tp_chan) {
350                 g_signal_handlers_disconnect_by_func (priv->tp_chan,
351                                                       tp_chat_destroy_cb,
352                                                       object);
353                 if (priv->acknowledge) {
354                         empathy_debug (DEBUG_DOMAIN, "Closing channel...");
355                         if (!tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error)) {
356                                 empathy_debug (DEBUG_DOMAIN, 
357                                               "Error closing text channel: %s",
358                                               error ? error->message : "No error given");
359                                 g_clear_error (&error);
360                         }
361                 }
362                 g_object_unref (priv->tp_chan);
363         }
364
365         if (priv->factory) {
366                 g_object_unref (priv->factory);
367         }
368         if (priv->user) {
369                 g_object_unref (priv->user);
370         }
371         if (priv->account) {
372                 g_object_unref (priv->account);
373         }
374         if (priv->mc) {
375                 g_object_unref (priv->mc);
376         }
377         g_free (priv->id);
378
379         G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
380 }
381
382 static GObject *
383 tp_chat_constructor (GType                  type,
384                      guint                  n_props,
385                      GObjectConstructParam *props)
386 {
387         GObject           *chat;
388         EmpathyTpChatPriv *priv;
389
390         chat = G_OBJECT_CLASS (empathy_tp_chat_parent_class)->constructor (type, n_props, props);
391
392         priv = GET_PRIV (chat);
393
394         priv->factory = empathy_contact_factory_new ();
395         priv->user = empathy_contact_factory_get_user (priv->factory, priv->account);
396         priv->mc = empathy_mission_control_new ();
397
398         priv->text_iface = tp_chan_get_interface (priv->tp_chan,
399                                                   TELEPATHY_CHAN_IFACE_TEXT_QUARK);
400         priv->chat_state_iface = tp_chan_get_interface (priv->tp_chan,
401                                                         TELEPATHY_CHAN_IFACE_CHAT_STATE_QUARK);
402         priv->props_iface = tp_chan_get_interface (priv->tp_chan,
403                                                    TELEPATHY_PROPS_IFACE_QUARK);
404
405         g_signal_connect (priv->tp_chan, "destroy",
406                           G_CALLBACK (tp_chat_destroy_cb),
407                           chat);
408         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed",
409                                      G_CALLBACK (tp_chat_closed_cb),
410                                      chat, NULL);
411         dbus_g_proxy_connect_signal (priv->text_iface, "Received",
412                                      G_CALLBACK (tp_chat_received_cb),
413                                      chat, NULL);
414         dbus_g_proxy_connect_signal (priv->text_iface, "Sent",
415                                      G_CALLBACK (tp_chat_sent_cb),
416                                      chat, NULL);
417         dbus_g_proxy_connect_signal (priv->text_iface, "SendError",
418                                      G_CALLBACK (tp_chat_send_error_cb),
419                                      chat, NULL);
420
421         if (priv->chat_state_iface != NULL) {
422                 dbus_g_proxy_connect_signal (priv->chat_state_iface,
423                                              "ChatStateChanged",
424                                              G_CALLBACK (tp_chat_state_changed_cb),
425                                              chat, NULL);
426         }
427         if (priv->props_iface != NULL) {
428                 tp_props_iface_set_mapping (TELEPATHY_PROPS_IFACE (priv->props_iface),
429                                             "anonymous", PROP_ANONYMOUS,
430                                             "invite-only", PROP_INVITE_ONLY,
431                                             "limit", PROP_LIMIT,
432                                             "limited", PROP_LIMITED,
433                                             "moderated", PROP_MODERATED,
434                                             "name", PROP_NAME,
435                                             "description", PROP_DESCRIPTION,
436                                             "password", PROP_PASSWORD,
437                                             "password-required", PROP_PASSWORD_REQUIRED,
438                                             "persistent", PROP_PERSISTENT,
439                                             "private", PROP_PRIVATE,
440                                             "subject", PROP_SUBJECT,
441                                             "subject-contact", PROP_SUBJECT_CONTACT,
442                                             "subject-timestamp", PROP_SUBJECT_TIMESTAMP,
443                                             NULL);
444                 g_signal_connect (priv->props_iface, "properties-ready",
445                                   G_CALLBACK (tp_chat_properties_ready_cb),
446                                   chat);
447                 g_signal_connect (priv->props_iface, "properties-changed",
448                                   G_CALLBACK (tp_chat_properties_changed_cb),
449                                   chat);
450         }
451
452         return chat;
453 }
454
455 static void
456 tp_chat_get_property (GObject    *object,
457                       guint       param_id,
458                       GValue     *value,
459                       GParamSpec *pspec)
460 {
461         EmpathyTpChatPriv *priv;
462         EmpathyTpChat     *chat;
463
464         priv = GET_PRIV (object);
465         chat = EMPATHY_TP_CHAT (object);
466
467         if (param_id >= PROP_ANONYMOUS &&
468             param_id <= PROP_SUBJECT_TIMESTAMP) {
469                 if (priv->props_iface) {
470                         tp_props_iface_get_value (TELEPATHY_PROPS_IFACE (priv->props_iface),
471                                                   param_id,
472                                                   value);
473                 }
474
475                 return;
476         }
477
478         switch (param_id) {
479         case PROP_ACCOUNT:
480                 g_value_set_object (value, priv->account);
481                 break;
482         case PROP_TP_CHAN:
483                 g_value_set_object (value, priv->tp_chan);
484                 break;
485         case PROP_ACKNOWLEDGE:
486                 g_value_set_boolean (value, priv->acknowledge);
487                 break;
488         default:
489                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
490                 break;
491         };
492 }
493
494 static void
495 tp_chat_set_property (GObject      *object,
496                       guint         param_id,
497                       const GValue *value,
498                       GParamSpec   *pspec)
499 {
500         EmpathyTpChatPriv *priv;
501         EmpathyTpChat     *chat;
502
503         priv = GET_PRIV (object);
504         chat = EMPATHY_TP_CHAT (object);
505
506         if (param_id >= PROP_ANONYMOUS &&
507             param_id <= PROP_SUBJECT_TIMESTAMP) {
508                 if (priv->props_iface) {
509                         tp_props_iface_set_value (TELEPATHY_PROPS_IFACE (priv->props_iface),
510                                                   param_id,
511                                                   value);
512                 }
513
514                 return;
515         }
516
517         switch (param_id) {
518         case PROP_ACCOUNT:
519                 priv->account = g_object_ref (g_value_get_object (value));
520                 break;
521         case PROP_TP_CHAN:
522                 priv->tp_chan = g_object_ref (g_value_get_object (value));
523                 break;
524         case PROP_ACKNOWLEDGE:
525                 empathy_tp_chat_set_acknowledge (EMPATHY_TP_CHAT (object),
526                                                  g_value_get_boolean (value));
527                 break;
528         default:
529                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
530                 break;
531         };
532 }
533
534 EmpathyTpChat *
535 empathy_tp_chat_new (McAccount *account,
536                      TpChan    *tp_chan)
537 {
538         return g_object_new (EMPATHY_TYPE_TP_CHAT, 
539                              "account", account,
540                              "tp-chan", tp_chan,
541                              NULL);
542 }
543
544 EmpathyTpChat *
545 empathy_tp_chat_new_with_contact (EmpathyContact *contact)
546 {
547         EmpathyTpChat  *chat;
548         MissionControl *mc;
549         McAccount      *account;
550         TpConn         *tp_conn;
551         TpChan         *text_chan;
552         const gchar    *bus_name;
553         guint           handle;
554
555         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
556
557         mc = empathy_mission_control_new ();
558         account = empathy_contact_get_account (contact);
559
560         if (mission_control_get_connection_status (mc, account, NULL) != 0) {
561                 /* The account is not connected, nothing to do. */
562                 return NULL;
563         }
564
565         tp_conn = mission_control_get_connection (mc, account, NULL);
566         g_return_val_if_fail (tp_conn != NULL, NULL);
567         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
568         handle = empathy_contact_get_handle (contact);
569
570         text_chan = tp_conn_new_channel (tp_get_bus (),
571                                          tp_conn,
572                                          bus_name,
573                                          TP_IFACE_CHANNEL_TYPE_TEXT,
574                                          TP_HANDLE_TYPE_CONTACT,
575                                          handle,
576                                          TRUE);
577
578         chat = empathy_tp_chat_new (account, text_chan);
579
580         g_object_unref (tp_conn);
581         g_object_unref (text_chan);
582         g_object_unref (mc);
583
584         return chat;
585 }
586
587 gboolean
588 empathy_tp_chat_get_acknowledge (EmpathyTpChat *chat)
589 {
590         EmpathyTpChatPriv *priv;
591
592         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE);
593
594         priv = GET_PRIV (chat);
595
596         return priv->acknowledge;
597 }
598
599 void
600 empathy_tp_chat_set_acknowledge (EmpathyTpChat *chat,
601                                  gboolean       acknowledge)
602 {
603         EmpathyTpChatPriv *priv;
604
605         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
606
607         priv = GET_PRIV (chat);
608
609         priv->acknowledge = acknowledge;
610         g_object_notify (G_OBJECT (chat), "acknowledge");
611 }
612
613 TpChan *
614 empathy_tp_chat_get_channel (EmpathyTpChat *chat)
615 {
616         EmpathyTpChatPriv *priv;
617
618         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
619
620         priv = GET_PRIV (chat);
621
622         return priv->tp_chan;
623 }
624
625 GList *
626 empathy_tp_chat_get_pendings (EmpathyTpChat *chat)
627 {
628         EmpathyTpChatPriv *priv;
629         GPtrArray         *messages_list;
630         guint              i;
631         GList             *messages = NULL;
632         GError            *error = NULL;
633
634         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
635
636         priv = GET_PRIV (chat);
637
638         /* If we do this call async, don't forget to ignore Received signal
639          * until we get the answer */
640         if (!tp_chan_type_text_list_pending_messages (priv->text_iface,
641                                                       priv->acknowledge,
642                                                       &messages_list,
643                                                       &error)) {
644                 empathy_debug (DEBUG_DOMAIN, 
645                               "Error retrieving pending messages: %s",
646                               error ? error->message : "No error given");
647                 g_clear_error (&error);
648                 return NULL;
649         }
650
651         for (i = 0; i < messages_list->len; i++) {
652                 EmpathyMessage *message;
653                 GValueArray    *message_struct;
654                 const gchar    *message_body;
655                 guint           message_id;
656                 guint           timestamp;
657                 guint           from_handle;
658                 guint           message_type;
659                 guint           message_flags;
660
661                 message_struct = g_ptr_array_index (messages_list, i);
662
663                 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
664                 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
665                 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
666                 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
667                 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
668                 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
669
670                 empathy_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
671
672                 message = tp_chat_build_message (chat,
673                                                  message_type,
674                                                  timestamp,
675                                                  from_handle,
676                                                  message_body);
677
678                 messages = g_list_prepend (messages, message);
679
680                 g_value_array_free (message_struct);
681         }
682         messages = g_list_reverse (messages);
683
684         g_ptr_array_free (messages_list, TRUE);
685
686         return messages;
687 }
688
689 void
690 empathy_tp_chat_send (EmpathyTpChat *chat,
691                       EmpathyMessage *message)
692 {
693         EmpathyTpChatPriv *priv;
694         const gchar       *message_body;
695         EmpathyMessageType  message_type;
696         GError            *error = NULL;
697
698         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
699         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
700
701         priv = GET_PRIV (chat);
702
703         message_body = empathy_message_get_body (message);
704         message_type = empathy_message_get_type (message);
705
706         empathy_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
707         if (!tp_chan_type_text_send (priv->text_iface,
708                                      message_type,
709                                      message_body,
710                                      &error)) {
711                 empathy_debug (DEBUG_DOMAIN, 
712                               "Send Error: %s", 
713                               error ? error->message : "No error given");
714                 g_clear_error (&error);
715         }
716 }
717
718 void
719 empathy_tp_chat_set_state (EmpathyTpChat             *chat,
720                            TelepathyChannelChatState  state)
721 {
722         EmpathyTpChatPriv *priv;
723         GError            *error = NULL;
724
725         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
726
727         priv = GET_PRIV (chat);
728
729         if (priv->chat_state_iface) {
730                 empathy_debug (DEBUG_DOMAIN, "Set state: %d", state);
731                 if (!tp_chan_iface_chat_state_set_chat_state (priv->chat_state_iface,
732                                                               state,
733                                                               &error)) {
734                         empathy_debug (DEBUG_DOMAIN,
735                                       "Set Chat State Error: %s",
736                                       error ? error->message : "No error given");
737                         g_clear_error (&error);
738                 }
739         }
740 }
741
742 const gchar *
743 empathy_tp_chat_get_id (EmpathyTpChat *chat)
744 {
745         EmpathyTpChatPriv *priv;
746
747         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
748
749         priv = GET_PRIV (chat);
750
751         if (!priv->id) {
752                 priv->id = empathy_inspect_channel (priv->account, priv->tp_chan);
753         }
754
755         return priv->id;
756 }
757
758 static void
759 tp_chat_destroy_cb (TpChan        *text_chan,
760                     EmpathyTpChat *chat)
761 {
762         EmpathyTpChatPriv *priv;
763
764         priv = GET_PRIV (chat);
765
766         empathy_debug (DEBUG_DOMAIN, "Channel Closed or CM crashed");
767
768         g_object_unref  (priv->tp_chan);
769         priv->tp_chan = NULL;
770         priv->text_iface = NULL;
771         priv->chat_state_iface = NULL;
772         priv->props_iface = NULL;
773
774         g_signal_emit (chat, signals[DESTROY], 0);
775 }
776
777 static void
778 tp_chat_closed_cb (TpChan        *text_chan,
779                    EmpathyTpChat *chat)
780 {
781         EmpathyTpChatPriv *priv;
782
783         priv = GET_PRIV (chat);
784
785         /* The channel is closed, do just like if the proxy was destroyed */
786         g_signal_handlers_disconnect_by_func (priv->tp_chan,
787                                               tp_chat_destroy_cb,
788                                               chat);
789         tp_chat_destroy_cb (text_chan, chat);
790 }
791
792 static void
793 tp_chat_received_cb (DBusGProxy    *text_iface,
794                      guint          message_id,
795                      guint          timestamp,
796                      guint          from_handle,
797                      guint          message_type,
798                      guint          message_flags,
799                      gchar         *message_body,
800                      EmpathyTpChat *chat)
801 {
802         EmpathyTpChatPriv *priv;
803         EmpathyMessage    *message;
804
805         priv = GET_PRIV (chat);
806
807         empathy_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
808
809         message = tp_chat_build_message (chat,
810                                          message_type,
811                                          timestamp,
812                                          from_handle,
813                                          message_body);
814
815         g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
816         g_object_unref (message);
817
818         if (priv->acknowledge) {
819                 GArray *message_ids;
820
821                 message_ids = g_array_new (FALSE, FALSE, sizeof (guint));
822                 g_array_append_val (message_ids, message_id);
823                 tp_chan_type_text_acknowledge_pending_messages (priv->text_iface,
824                                                                 message_ids, NULL);
825                 g_array_free (message_ids, TRUE);
826         }
827 }
828
829 static void
830 tp_chat_sent_cb (DBusGProxy    *text_iface,
831                  guint          timestamp,
832                  guint          message_type,
833                  gchar         *message_body,
834                  EmpathyTpChat *chat)
835 {
836         EmpathyMessage *message;
837
838         empathy_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
839
840         message = tp_chat_build_message (chat,
841                                          message_type,
842                                          timestamp,
843                                          0,
844                                          message_body);
845
846         g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
847         g_object_unref (message);
848 }
849
850 static void
851 tp_chat_send_error_cb (DBusGProxy    *text_iface,
852                        guint          error_code,
853                        guint          timestamp,
854                        guint          message_type,
855                        gchar         *message_body,
856                        EmpathyTpChat *chat)
857 {
858         EmpathyMessage *message;
859
860         empathy_debug (DEBUG_DOMAIN, "Message sent error: %s (%d)",
861                        message_body, error_code);
862
863         message = tp_chat_build_message (chat,
864                                          message_type,
865                                          timestamp,
866                                          0,
867                                          message_body);
868
869         g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code);
870         g_object_unref (message);
871 }
872
873 static void
874 tp_chat_state_changed_cb (DBusGProxy                *chat_state_iface,
875                           guint                      handle,
876                           TelepathyChannelChatState  state,
877                           EmpathyTpChat             *chat)
878 {
879         EmpathyTpChatPriv *priv;
880         EmpathyContact     *contact;
881
882         priv = GET_PRIV (chat);
883
884         contact = empathy_contact_factory_get_from_handle (priv->factory,
885                                                            priv->account,
886                                                            handle);
887
888         empathy_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d",
889                       empathy_contact_get_name (contact),
890                       handle,
891                       state);
892
893         g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
894         g_object_unref (contact);
895 }
896
897 static EmpathyMessage *
898 tp_chat_build_message (EmpathyTpChat *chat,
899                        guint          type,
900                        guint          timestamp,
901                        guint          from_handle,
902                        const gchar   *message_body)
903 {
904         EmpathyTpChatPriv *priv;
905         EmpathyMessage    *message;
906         EmpathyContact    *sender;
907
908         priv = GET_PRIV (chat);
909
910         if (from_handle == 0) {
911                 sender = g_object_ref (priv->user);
912         } else {
913                 sender = empathy_contact_factory_get_from_handle (priv->factory,
914                                                                   priv->account,
915                                                                   from_handle);
916         }
917
918         message = empathy_message_new (message_body);
919         empathy_message_set_type (message, type);
920         empathy_message_set_sender (message, sender);
921         empathy_message_set_receiver (message, priv->user);
922         empathy_message_set_timestamp (message, (EmpathyTime) timestamp);
923
924         g_object_unref (sender);
925
926         return message;
927 }
928
929 static void
930 tp_chat_properties_ready_cb (TpPropsIface  *props_iface,
931                              EmpathyTpChat *chat)
932 {
933         g_object_notify (G_OBJECT (chat), "anonymous");
934         g_object_notify (G_OBJECT (chat), "invite-only");
935         g_object_notify (G_OBJECT (chat), "limit");
936         g_object_notify (G_OBJECT (chat), "limited");
937         g_object_notify (G_OBJECT (chat), "moderated");
938         g_object_notify (G_OBJECT (chat), "name");
939         g_object_notify (G_OBJECT (chat), "description");
940         g_object_notify (G_OBJECT (chat), "password");
941         g_object_notify (G_OBJECT (chat), "password-required");
942         g_object_notify (G_OBJECT (chat), "persistent");
943         g_object_notify (G_OBJECT (chat), "private");
944         g_object_notify (G_OBJECT (chat), "subject");
945         g_object_notify (G_OBJECT (chat), "subject-contact");
946         g_object_notify (G_OBJECT (chat), "subject-timestamp");
947 }
948
949 static void
950 tp_chat_properties_changed_cb (TpPropsIface   *props_iface,
951                                guint           prop_id,
952                                TpPropsChanged  flag,
953                                EmpathyTpChat  *chat)
954 {
955         switch (prop_id) {
956         case PROP_ANONYMOUS:
957                 g_object_notify (G_OBJECT (chat), "anonymous");
958                 break;
959         case PROP_INVITE_ONLY:
960                 g_object_notify (G_OBJECT (chat), "invite-only");
961                 break;
962         case PROP_LIMIT:
963                 g_object_notify (G_OBJECT (chat), "limit");
964                 break;
965         case PROP_LIMITED:
966                 g_object_notify (G_OBJECT (chat), "limited");
967                 break;
968         case PROP_MODERATED:
969                 g_object_notify (G_OBJECT (chat), "moderated");
970                 break;
971         case PROP_NAME:
972                 g_object_notify (G_OBJECT (chat), "name");
973                 break;
974         case PROP_DESCRIPTION:
975                 g_object_notify (G_OBJECT (chat), "description");
976                 break;
977         case PROP_PASSWORD:
978                 g_object_notify (G_OBJECT (chat), "password");
979                 break;
980         case PROP_PASSWORD_REQUIRED:
981                 g_object_notify (G_OBJECT (chat), "password-required");
982                 break;
983         case PROP_PERSISTENT:
984                 g_object_notify (G_OBJECT (chat), "persistent");
985                 break;
986         case PROP_PRIVATE:
987                 g_object_notify (G_OBJECT (chat), "private");
988                 break;
989         case PROP_SUBJECT:
990                 g_object_notify (G_OBJECT (chat), "subject");
991                 break;
992         case PROP_SUBJECT_CONTACT:
993                 g_object_notify (G_OBJECT (chat), "subject-contact");
994                 break;
995         case PROP_SUBJECT_TIMESTAMP:
996                 g_object_notify (G_OBJECT (chat), "subject-timestamp");
997                 break;
998         }
999 }
1000