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