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