Conflicts:
[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 EmpathyTpChat *
565 empathy_tp_chat_new_with_contact (EmpathyContact *contact)
566 {
567         EmpathyTpChat  *chat;
568         MissionControl *mc;
569         McAccount      *account;
570         TpConn         *tp_conn;
571         TpChan         *text_chan;
572         const gchar    *bus_name;
573         guint           handle;
574
575         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
576
577         mc = empathy_mission_control_new ();
578         account = empathy_contact_get_account (contact);
579
580         if (mission_control_get_connection_status (mc, account, NULL) != 0) {
581                 /* The account is not connected, nothing to do. */
582                 return NULL;
583         }
584
585         tp_conn = mission_control_get_connection (mc, account, NULL);
586         g_return_val_if_fail (tp_conn != NULL, NULL);
587         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
588         handle = empathy_contact_get_handle (contact);
589
590         text_chan = tp_conn_new_channel (tp_get_bus (),
591                                          tp_conn,
592                                          bus_name,
593                                          TP_IFACE_CHANNEL_TYPE_TEXT,
594                                          TP_HANDLE_TYPE_CONTACT,
595                                          handle,
596                                          TRUE);
597
598         chat = empathy_tp_chat_new (account, text_chan);
599
600         g_object_unref (tp_conn);
601         g_object_unref (text_chan);
602         g_object_unref (mc);
603
604         return chat;
605 }
606
607 gboolean
608 empathy_tp_chat_get_acknowledge (EmpathyTpChat *chat)
609 {
610         EmpathyTpChatPriv *priv;
611
612         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), FALSE);
613
614         priv = GET_PRIV (chat);
615
616         return priv->acknowledge;
617 }
618
619 void
620 empathy_tp_chat_set_acknowledge (EmpathyTpChat *chat,
621                                  gboolean       acknowledge)
622 {
623         EmpathyTpChatPriv *priv;
624
625         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
626
627         priv = GET_PRIV (chat);
628
629         priv->acknowledge = acknowledge;
630         g_object_notify (G_OBJECT (chat), "acknowledge");
631 }
632
633 TpChan *
634 empathy_tp_chat_get_channel (EmpathyTpChat *chat)
635 {
636         EmpathyTpChatPriv *priv;
637
638         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
639
640         priv = GET_PRIV (chat);
641
642         return priv->tp_chan;
643 }
644
645 GList *
646 empathy_tp_chat_get_pendings (EmpathyTpChat *chat)
647 {
648         EmpathyTpChatPriv *priv;
649         GPtrArray         *messages_list;
650         guint              i;
651         GList             *messages = NULL;
652         GError            *error = NULL;
653
654         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
655
656         priv = GET_PRIV (chat);
657
658         /* If we do this call async, don't forget to ignore Received signal
659          * until we get the answer */
660         if (!tp_chan_type_text_list_pending_messages (priv->text_iface,
661                                                       priv->acknowledge,
662                                                       &messages_list,
663                                                       &error)) {
664                 empathy_debug (DEBUG_DOMAIN, 
665                               "Error retrieving pending messages: %s",
666                               error ? error->message : "No error given");
667                 g_clear_error (&error);
668                 return NULL;
669         }
670
671         for (i = 0; i < messages_list->len; i++) {
672                 EmpathyMessage *message;
673                 GValueArray    *message_struct;
674                 const gchar    *message_body;
675                 guint           message_id;
676                 guint           timestamp;
677                 guint           from_handle;
678                 guint           message_type;
679                 guint           message_flags;
680
681                 message_struct = g_ptr_array_index (messages_list, i);
682
683                 message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
684                 timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
685                 from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
686                 message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
687                 message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
688                 message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
689
690                 empathy_debug (DEBUG_DOMAIN, "Message pending: %s", message_body);
691
692                 message = tp_chat_build_message (chat,
693                                                  message_type,
694                                                  timestamp,
695                                                  from_handle,
696                                                  message_body);
697
698                 messages = g_list_prepend (messages, message);
699
700                 g_value_array_free (message_struct);
701         }
702         messages = g_list_reverse (messages);
703
704         g_ptr_array_free (messages_list, TRUE);
705
706         return messages;
707 }
708
709 void
710 empathy_tp_chat_send (EmpathyTpChat *chat,
711                       EmpathyMessage *message)
712 {
713         EmpathyTpChatPriv *priv;
714         const gchar       *message_body;
715         EmpathyMessageType  message_type;
716         GError            *error = NULL;
717
718         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
719         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
720
721         priv = GET_PRIV (chat);
722
723         message_body = empathy_message_get_body (message);
724         message_type = empathy_message_get_type (message);
725
726         empathy_debug (DEBUG_DOMAIN, "Sending message: %s", message_body);
727         if (!tp_chan_type_text_send (priv->text_iface,
728                                      message_type,
729                                      message_body,
730                                      &error)) {
731                 empathy_debug (DEBUG_DOMAIN, 
732                               "Send Error: %s", 
733                               error ? error->message : "No error given");
734                 g_clear_error (&error);
735         }
736 }
737
738 void
739 empathy_tp_chat_set_state (EmpathyTpChat      *chat,
740                            TpChannelChatState  state)
741 {
742         EmpathyTpChatPriv *priv;
743         GError            *error = NULL;
744
745         g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
746
747         priv = GET_PRIV (chat);
748
749         if (priv->chat_state_iface) {
750                 empathy_debug (DEBUG_DOMAIN, "Set state: %d", state);
751                 if (!tp_chan_iface_chat_state_set_chat_state (priv->chat_state_iface,
752                                                               state,
753                                                               &error)) {
754                         empathy_debug (DEBUG_DOMAIN,
755                                       "Set Chat State Error: %s",
756                                       error ? error->message : "No error given");
757                         g_clear_error (&error);
758                 }
759         }
760 }
761
762 const gchar *
763 empathy_tp_chat_get_id (EmpathyTpChat *chat)
764 {
765         EmpathyTpChatPriv *priv;
766
767         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
768
769         priv = GET_PRIV (chat);
770
771         if (!priv->id) {
772                 priv->id = empathy_inspect_channel (priv->account, priv->tp_chan);
773         }
774
775         return priv->id;
776 }
777
778 static void
779 tp_chat_destroy_cb (TpChan        *text_chan,
780                     EmpathyTpChat *chat)
781 {
782         EmpathyTpChatPriv *priv;
783
784         priv = GET_PRIV (chat);
785
786         empathy_debug (DEBUG_DOMAIN, "Channel Closed or CM crashed");
787
788         g_object_unref  (priv->tp_chan);
789         priv->tp_chan = NULL;
790         priv->text_iface = NULL;
791         priv->chat_state_iface = NULL;
792         priv->props_iface = NULL;
793
794         g_signal_emit (chat, signals[DESTROY], 0);
795 }
796
797 static void
798 tp_chat_closed_cb (TpChan        *text_chan,
799                    EmpathyTpChat *chat)
800 {
801         EmpathyTpChatPriv *priv;
802
803         priv = GET_PRIV (chat);
804
805         /* The channel is closed, do just like if the proxy was destroyed */
806         g_signal_handlers_disconnect_by_func (priv->tp_chan,
807                                               tp_chat_destroy_cb,
808                                               chat);
809         tp_chat_destroy_cb (text_chan, chat);
810 }
811
812 static void
813 tp_chat_received_cb (DBusGProxy    *text_iface,
814                      guint          message_id,
815                      guint          timestamp,
816                      guint          from_handle,
817                      guint          message_type,
818                      guint          message_flags,
819                      gchar         *message_body,
820                      EmpathyTpChat *chat)
821 {
822         EmpathyTpChatPriv *priv;
823         EmpathyMessage    *message;
824
825         priv = GET_PRIV (chat);
826
827         empathy_debug (DEBUG_DOMAIN, "Message received: %s", message_body);
828
829         message = tp_chat_build_message (chat,
830                                          message_type,
831                                          timestamp,
832                                          from_handle,
833                                          message_body);
834
835         g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
836         g_object_unref (message);
837
838         if (priv->acknowledge) {
839                 GArray *message_ids;
840
841                 message_ids = g_array_new (FALSE, FALSE, sizeof (guint));
842                 g_array_append_val (message_ids, message_id);
843                 tp_chan_type_text_acknowledge_pending_messages (priv->text_iface,
844                                                                 message_ids, NULL);
845                 g_array_free (message_ids, TRUE);
846         }
847 }
848
849 static void
850 tp_chat_sent_cb (DBusGProxy    *text_iface,
851                  guint          timestamp,
852                  guint          message_type,
853                  gchar         *message_body,
854                  EmpathyTpChat *chat)
855 {
856         EmpathyMessage *message;
857
858         empathy_debug (DEBUG_DOMAIN, "Message sent: %s", message_body);
859
860         message = tp_chat_build_message (chat,
861                                          message_type,
862                                          timestamp,
863                                          0,
864                                          message_body);
865
866         g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
867         g_object_unref (message);
868 }
869
870 static void
871 tp_chat_send_error_cb (DBusGProxy    *text_iface,
872                        guint          error_code,
873                        guint          timestamp,
874                        guint          message_type,
875                        gchar         *message_body,
876                        EmpathyTpChat *chat)
877 {
878         EmpathyMessage *message;
879
880         empathy_debug (DEBUG_DOMAIN, "Message sent error: %s (%d)",
881                        message_body, error_code);
882
883         message = tp_chat_build_message (chat,
884                                          message_type,
885                                          timestamp,
886                                          0,
887                                          message_body);
888
889         g_signal_emit (chat, signals[SEND_ERROR], 0, message, error_code);
890         g_object_unref (message);
891 }
892
893 static void
894 tp_chat_state_changed_cb (DBusGProxy         *chat_state_iface,
895                           guint               handle,
896                           TpChannelChatState  state,
897                           EmpathyTpChat      *chat)
898 {
899         EmpathyTpChatPriv *priv;
900         EmpathyContact     *contact;
901
902         priv = GET_PRIV (chat);
903
904         contact = empathy_contact_factory_get_from_handle (priv->factory,
905                                                            priv->account,
906                                                            handle);
907
908         empathy_debug (DEBUG_DOMAIN, "Chat state changed for %s (%d): %d",
909                       empathy_contact_get_name (contact),
910                       handle,
911                       state);
912
913         g_signal_emit (chat, signals[CHAT_STATE_CHANGED], 0, contact, state);
914         g_object_unref (contact);
915 }
916
917 static EmpathyMessage *
918 tp_chat_build_message (EmpathyTpChat *chat,
919                        guint          type,
920                        guint          timestamp,
921                        guint          from_handle,
922                        const gchar   *message_body)
923 {
924         EmpathyTpChatPriv *priv;
925         EmpathyMessage    *message;
926         EmpathyContact    *sender;
927
928         priv = GET_PRIV (chat);
929
930         if (from_handle == 0) {
931                 sender = g_object_ref (priv->user);
932         } else {
933                 sender = empathy_contact_factory_get_from_handle (priv->factory,
934                                                                   priv->account,
935                                                                   from_handle);
936         }
937
938         message = empathy_message_new (message_body);
939         empathy_message_set_type (message, type);
940         empathy_message_set_sender (message, sender);
941         empathy_message_set_receiver (message, priv->user);
942         empathy_message_set_timestamp (message, timestamp);
943
944         g_object_unref (sender);
945
946         return message;
947 }
948
949 static void
950 tp_chat_properties_ready_cb (TpPropsIface  *props_iface,
951                              EmpathyTpChat *chat)
952 {
953         g_object_notify (G_OBJECT (chat), "anonymous");
954         g_object_notify (G_OBJECT (chat), "invite-only");
955         g_object_notify (G_OBJECT (chat), "limit");
956         g_object_notify (G_OBJECT (chat), "limited");
957         g_object_notify (G_OBJECT (chat), "moderated");
958         g_object_notify (G_OBJECT (chat), "name");
959         g_object_notify (G_OBJECT (chat), "description");
960         g_object_notify (G_OBJECT (chat), "password");
961         g_object_notify (G_OBJECT (chat), "password-required");
962         g_object_notify (G_OBJECT (chat), "persistent");
963         g_object_notify (G_OBJECT (chat), "private");
964         g_object_notify (G_OBJECT (chat), "subject");
965         g_object_notify (G_OBJECT (chat), "subject-contact");
966         g_object_notify (G_OBJECT (chat), "subject-timestamp");
967 }
968
969 static void
970 tp_chat_properties_changed_cb (TpPropsIface   *props_iface,
971                                guint           prop_id,
972                                TpPropsChanged  flag,
973                                EmpathyTpChat  *chat)
974 {
975         switch (prop_id) {
976         case PROP_ANONYMOUS:
977                 g_object_notify (G_OBJECT (chat), "anonymous");
978                 break;
979         case PROP_INVITE_ONLY:
980                 g_object_notify (G_OBJECT (chat), "invite-only");
981                 break;
982         case PROP_LIMIT:
983                 g_object_notify (G_OBJECT (chat), "limit");
984                 break;
985         case PROP_LIMITED:
986                 g_object_notify (G_OBJECT (chat), "limited");
987                 break;
988         case PROP_MODERATED:
989                 g_object_notify (G_OBJECT (chat), "moderated");
990                 break;
991         case PROP_NAME:
992                 g_object_notify (G_OBJECT (chat), "name");
993                 break;
994         case PROP_DESCRIPTION:
995                 g_object_notify (G_OBJECT (chat), "description");
996                 break;
997         case PROP_PASSWORD:
998                 g_object_notify (G_OBJECT (chat), "password");
999                 break;
1000         case PROP_PASSWORD_REQUIRED:
1001                 g_object_notify (G_OBJECT (chat), "password-required");
1002                 break;
1003         case PROP_PERSISTENT:
1004                 g_object_notify (G_OBJECT (chat), "persistent");
1005                 break;
1006         case PROP_PRIVATE:
1007                 g_object_notify (G_OBJECT (chat), "private");
1008                 break;
1009         case PROP_SUBJECT:
1010                 g_object_notify (G_OBJECT (chat), "subject");
1011                 break;
1012         case PROP_SUBJECT_CONTACT:
1013                 g_object_notify (G_OBJECT (chat), "subject-contact");
1014                 break;
1015         case PROP_SUBJECT_TIMESTAMP:
1016                 g_object_notify (G_OBJECT (chat), "subject-timestamp");
1017                 break;
1018         }
1019 }
1020