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