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