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