]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-contact-factory.c
Queue received messages until the sender got his alias.
[empathy.git] / libempathy / empathy-tp-contact-factory.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 <telepathy-glib/util.h>
27 #include <libtelepathy/tp-conn.h>
28 #include <libtelepathy/tp-conn-iface-aliasing-gen.h>
29 #include <libtelepathy/tp-conn-iface-presence-gen.h>
30 #include <libtelepathy/tp-conn-iface-avatars-gen.h>
31 #include <libtelepathy/tp-conn-iface-capabilities-gen.h>
32 #include <libmissioncontrol/mission-control.h>
33
34 #include "empathy-tp-contact-factory.h"
35 #include "empathy-utils.h"
36 #include "empathy-debug.h"
37
38 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
39                        EMPATHY_TYPE_TP_CONTACT_FACTORY, EmpathyTpContactFactoryPriv))
40
41 #define DEBUG_DOMAIN "TpContactFactory"
42
43 struct _EmpathyTpContactFactoryPriv {
44         MissionControl *mc;
45         McAccount      *account;
46
47         TpConn         *tp_conn;
48         DBusGProxy     *aliasing_iface;
49         DBusGProxy     *avatars_iface;
50         DBusGProxy     *presence_iface;
51         DBusGProxy     *capabilities_iface;
52
53         GList          *contacts;
54         guint           self_handle;
55 };
56
57 static void empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass);
58 static void empathy_tp_contact_factory_init       (EmpathyTpContactFactory      *factory);
59
60 G_DEFINE_TYPE (EmpathyTpContactFactory, empathy_tp_contact_factory, G_TYPE_OBJECT);
61
62 enum {
63         PROP_0,
64         PROP_ACCOUNT,
65 };
66
67 static EmpathyContact *
68 tp_contact_factory_find_by_handle (EmpathyTpContactFactory *tp_factory,
69                                    guint                    handle)
70 {
71         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
72         GList                       *l;
73
74         for (l = priv->contacts; l; l = l->next) {
75                 if (empathy_contact_get_handle (l->data) == handle) {
76                         return l->data;
77                 }
78         }
79
80         return NULL;
81 }
82
83 static EmpathyContact *
84 tp_contact_factory_find_by_id (EmpathyTpContactFactory *tp_factory,
85                                const gchar             *id)
86 {
87         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
88         GList                       *l;
89
90         for (l = priv->contacts; l; l = l->next) {
91                 if (!tp_strdiff (empathy_contact_get_id (l->data), id)) {
92                         return l->data;
93                 }
94         }
95
96         return NULL;
97 }
98
99 static void
100 tp_contact_factory_weak_notify (gpointer data,
101                                 GObject *where_the_object_was)
102 {
103         EmpathyTpContactFactoryPriv *priv = GET_PRIV (data);
104
105         empathy_debug (DEBUG_DOMAIN, "Remove finalized contact %p",
106                        where_the_object_was);
107
108         priv->contacts = g_list_remove (priv->contacts, where_the_object_was);
109 }
110
111 static void
112 tp_contact_factory_presences_table_foreach (const gchar    *state_str,
113                                             GHashTable     *presences_table,
114                                             EmpathyContact *contact)
115 {
116         const GValue *message;
117
118         empathy_contact_set_presence (contact,
119                                       empathy_presence_from_str (state_str));
120         
121         message = g_hash_table_lookup (presences_table, "message");
122         if (message != NULL) {
123                 empathy_contact_set_presence_message (contact,
124                                                       g_value_get_string (message));
125         } else {
126                 empathy_contact_set_presence_message (contact, NULL);
127         }
128 }
129
130 static void
131 tp_contact_factory_parse_presence_foreach (guint                    handle,
132                                            GValueArray             *presence_struct,
133                                            EmpathyTpContactFactory *tp_factory)
134 {
135         GHashTable      *presences_table;
136         EmpathyContact  *contact;
137
138         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
139         if (!contact) {
140                 return;
141         }
142
143         presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1));
144
145         g_hash_table_foreach (presences_table,
146                               (GHFunc) tp_contact_factory_presences_table_foreach,
147                               contact);
148
149         empathy_debug (DEBUG_DOMAIN, "Changing presence for contact %s (%d) to %s (%d)",
150                       empathy_contact_get_id (contact),
151                       handle,
152                       empathy_contact_get_presence_message (contact),
153                       empathy_contact_get_presence (contact));
154 }
155
156 static void
157 tp_contact_factory_get_presence_cb (DBusGProxy *proxy,
158                                     GHashTable *handle_table,
159                                     GError     *error,
160                                     gpointer    user_data)
161 {
162         EmpathyTpContactFactory *tp_factory = user_data;
163
164         if (error) {
165                 empathy_debug (DEBUG_DOMAIN, "Error getting presence: %s",
166                               error->message);
167                 goto OUT;
168         }
169
170         g_hash_table_foreach (handle_table,
171                               (GHFunc) tp_contact_factory_parse_presence_foreach,
172                               tp_factory);
173
174         g_hash_table_destroy (handle_table);
175 OUT:
176         g_object_unref (tp_factory);
177 }
178
179 static void
180 tp_contact_factory_presence_update_cb (DBusGProxy              *proxy,
181                                        GHashTable              *handle_table,
182                                        EmpathyTpContactFactory *tp_factory)
183 {
184         g_hash_table_foreach (handle_table,
185                               (GHFunc) tp_contact_factory_parse_presence_foreach,
186                               tp_factory);
187 }
188
189 static void
190 tp_contact_factory_set_aliases_cb (DBusGProxy *proxy,
191                                    GError     *error,
192                                    gpointer    user_data)
193 {
194         EmpathyTpContactFactory *tp_factory = user_data;
195
196         if (error) {
197                 empathy_debug (DEBUG_DOMAIN, "Error setting alias: %s",
198                                error->message);
199         }
200
201         g_object_unref (tp_factory);
202 }
203
204 typedef struct {
205         EmpathyTpContactFactory *tp_factory;
206         guint                   *handles;
207 } RequestAliasesData;
208
209 static void
210 tp_contact_factory_request_aliases_cb (DBusGProxy  *proxy,
211                                        gchar      **contact_names,
212                                        GError      *error,
213                                        gpointer     user_data)
214 {
215         RequestAliasesData  *data = user_data;
216         guint                i = 0;
217         gchar              **name;
218
219         if (error) {
220                 empathy_debug (DEBUG_DOMAIN, "Error requesting aliases: %s",
221                               error->message);
222                 goto OUT;
223         }
224
225         for (name = contact_names; *name; name++) {
226                 EmpathyContact *contact;
227
228                 contact = tp_contact_factory_find_by_handle (data->tp_factory,
229                                                              data->handles[i]);
230                 if (!contact) {
231                         continue;
232                 }
233
234                 empathy_debug (DEBUG_DOMAIN, "Renaming contact %s (%d) to %s (request cb)",
235                                empathy_contact_get_id (contact),
236                                data->handles[i], *name);
237
238                 empathy_contact_set_name (contact, *name);
239
240                 i++;
241         }
242
243         g_strfreev (contact_names);
244 OUT:
245         g_object_unref (data->tp_factory);
246         g_free (data->handles);
247         g_slice_free (RequestAliasesData, data);
248 }
249
250 static void
251 tp_contact_factory_aliases_changed_cb (DBusGProxy *proxy,
252                                        GPtrArray  *renamed_handlers,
253                                        gpointer    user_data)
254 {
255         EmpathyTpContactFactory *tp_factory = user_data;
256         guint                    i;
257
258         for (i = 0; renamed_handlers->len > i; i++) {
259                 guint           handle;
260                 const gchar    *alias;
261                 GValueArray    *renamed_struct;
262                 EmpathyContact *contact;
263
264                 renamed_struct = g_ptr_array_index (renamed_handlers, i);
265                 handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0));
266                 alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1));
267                 contact = tp_contact_factory_find_by_handle (tp_factory, handle);
268
269                 if (!contact) {
270                         /* We don't know this contact, skip */
271                         continue;
272                 }
273
274                 empathy_debug (DEBUG_DOMAIN, "Renaming contact %s (%d) to %s (changed cb)",
275                                empathy_contact_get_id (contact),
276                                handle, alias);
277
278                 empathy_contact_set_name (contact, alias);
279         }
280 }
281
282 static void
283 tp_contact_factory_set_avatar_cb (DBusGProxy *proxy,
284                                   gchar      *token,
285                                   GError     *error,
286                                   gpointer    user_data)
287 {
288         EmpathyTpContactFactory *tp_factory = user_data;
289
290         if (error) {
291                 empathy_debug (DEBUG_DOMAIN, "Error setting avatar: %s",
292                                error->message);
293         }
294
295         g_object_unref (tp_factory);
296         g_free (token);
297 }
298
299 static void
300 tp_contact_factory_clear_avatar_cb (DBusGProxy *proxy,
301                                     GError     *error,
302                                     gpointer    user_data)
303 {
304         EmpathyTpContactFactory *tp_factory = user_data;
305
306         if (error) {
307                 empathy_debug (DEBUG_DOMAIN, "Error clearing avatar: %s",
308                                error->message);
309         }
310
311         g_object_unref (tp_factory);
312 }
313
314 static void
315 tp_contact_factory_avatar_retrieved_cb (DBusGProxy *proxy,
316                                         guint       handle,
317                                         gchar      *token,
318                                         GArray     *avatar_data,
319                                         gchar      *mime_type,
320                                         gpointer    user_data)
321 {
322         EmpathyTpContactFactory *tp_factory = user_data;
323         EmpathyContact          *contact;
324         EmpathyAvatar           *avatar;
325
326         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
327         if (!contact) {
328                 return;
329         }
330
331         empathy_debug (DEBUG_DOMAIN, "Avatar retrieved for contact %s (%d)",
332                        empathy_contact_get_id (contact),
333                        handle);
334
335         avatar = empathy_avatar_new (avatar_data->data,
336                                      avatar_data->len,
337                                      mime_type,
338                                      token);
339
340         empathy_contact_set_avatar (contact, avatar);
341         empathy_avatar_unref (avatar);
342 }
343
344 static void
345 tp_contact_factory_request_avatars_cb (DBusGProxy *proxy,
346                                        GError     *error,
347                                        gpointer    user_data)
348 {
349         EmpathyTpContactFactory *tp_factory = user_data;
350
351         if (error) {
352                 empathy_debug (DEBUG_DOMAIN, "Error requesting avatars: %s",
353                                error->message);
354         }
355
356         g_object_unref (tp_factory);
357 }
358
359 static gboolean
360 tp_contact_factory_avatar_maybe_update (EmpathyTpContactFactory *tp_factory,
361                                         guint                    handle,
362                                         const gchar             *token)
363 {
364         EmpathyContact *contact;
365         EmpathyAvatar  *avatar;
366
367         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
368         if (!contact) {
369                 return TRUE;
370         }
371
372         /* Check if we have an avatar */
373         if (G_STR_EMPTY (token)) {
374                 empathy_contact_set_avatar (contact, NULL);
375                 return TRUE;
376         }
377
378         /* Check if the avatar changed */
379         avatar = empathy_contact_get_avatar (contact);
380         if (avatar && !tp_strdiff (avatar->token, token)) {
381                 return TRUE;
382         }
383
384         /* The avatar changed, search the new one in the cache */
385         avatar = empathy_avatar_new_from_cache (token);
386         if (avatar) {
387                 /* Got from cache, use it */
388                 empathy_contact_set_avatar (contact, avatar);
389                 empathy_avatar_unref (avatar);
390                 return TRUE;
391         }
392
393         /* Avatar is not up-to-date, we have to request it. */
394         return FALSE;
395 }
396
397 typedef struct {
398         EmpathyTpContactFactory *tp_factory;
399         GArray                  *handles;
400 } TokensData;
401
402 static void
403 tp_contact_factory_avatar_tokens_foreach (gpointer key,
404                                           gpointer value,
405                                           gpointer user_data)
406 {
407         TokensData  *data = user_data;
408         const gchar *token = value;
409         guint        handle = GPOINTER_TO_UINT (key);
410
411         if (!tp_contact_factory_avatar_maybe_update (data->tp_factory,
412                                                      handle, token)) {
413                 g_array_append_val (data->handles, handle);
414         }
415 }
416
417 static void
418 tp_contact_factory_get_known_avatar_tokens_cb (DBusGProxy *proxy,
419                                                GHashTable *tokens,
420                                                GError     *error,
421                                                gpointer    user_data)
422 {
423         EmpathyTpContactFactory     *tp_factory = user_data;
424         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
425         TokensData                   data;
426
427         if (error) {
428                 empathy_debug (DEBUG_DOMAIN,
429                                "Error getting known avatars tokens: %s",
430                                error->message);
431                 goto OUT;
432         }
433
434         data.tp_factory = tp_factory;
435         data.handles = g_array_new (FALSE, FALSE, sizeof (guint));
436         g_hash_table_foreach (tokens,
437                               tp_contact_factory_avatar_tokens_foreach,
438                               &data);
439
440         empathy_debug (DEBUG_DOMAIN, "Got %d tokens, need to request %d avatars",
441                        g_hash_table_size (tokens),
442                        data.handles->len);
443
444         /* Request needed avatars */
445         if (data.handles->len > 0) {
446                 tp_conn_iface_avatars_request_avatars_async (priv->avatars_iface,
447                                                              data.handles,
448                                                              tp_contact_factory_request_avatars_cb,
449                                                              g_object_ref (tp_factory));
450         }
451
452         g_hash_table_destroy (tokens);
453         g_array_free (data.handles, TRUE);
454 OUT:
455         g_object_unref (tp_factory);
456 }
457
458 static void
459 tp_contact_factory_avatar_updated_cb (DBusGProxy *proxy,
460                                       guint       handle,
461                                       gchar      *new_token,
462                                       gpointer    user_data)
463 {
464         EmpathyTpContactFactory     *tp_factory = user_data;
465         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
466         GArray                      *handles;
467
468         if (tp_contact_factory_avatar_maybe_update (tp_factory, handle, new_token)) {
469                 /* Avatar was cached, nothing to do */
470                 return;
471         }
472
473         empathy_debug (DEBUG_DOMAIN, "Need to request avatar for token %s",
474                        new_token);
475
476         handles = g_array_new (FALSE, FALSE, sizeof (guint));
477         g_array_append_val (handles, handle);
478
479         tp_conn_iface_avatars_request_avatars_async (priv->avatars_iface,
480                                                      handles,
481                                                      tp_contact_factory_request_avatars_cb,
482                                                      g_object_ref (tp_factory));
483         g_array_free (handles, TRUE);
484 }
485
486 static void
487 tp_contact_factory_update_capabilities (EmpathyTpContactFactory *tp_factory,
488                                         guint                    handle,
489                                         const gchar             *channel_type,
490                                         guint                    generic,
491                                         guint                    specific)
492 {
493         EmpathyContact      *contact;
494         EmpathyCapabilities  capabilities;
495
496         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
497         if (!contact) {
498                 return;
499         }
500
501         capabilities = empathy_contact_get_capabilities (contact);
502         capabilities &= ~EMPATHY_CAPABILITIES_UNKNOWN;
503
504         if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) == 0) {
505                 capabilities &= ~EMPATHY_CAPABILITIES_AUDIO;
506                 capabilities &= ~EMPATHY_CAPABILITIES_VIDEO;
507                 if (specific & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) {
508                         capabilities |= EMPATHY_CAPABILITIES_AUDIO;
509                 }
510                 if (specific & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) {
511                         capabilities |= EMPATHY_CAPABILITIES_VIDEO;
512                 }
513         }
514
515         empathy_debug (DEBUG_DOMAIN, "Changing capabilities for contact %s (%d) to %d",
516                        empathy_contact_get_id (contact),
517                        empathy_contact_get_handle (contact),
518                        capabilities);
519
520         empathy_contact_set_capabilities (contact, capabilities);
521 }
522
523 static void
524 tp_contact_factory_get_capabilities_cb (DBusGProxy *proxy,
525                                         GPtrArray  *capabilities,
526                                         GError     *error,
527                                         gpointer    user_data)
528 {
529         EmpathyTpContactFactory *tp_factory = user_data;
530         guint                    i;
531
532         if (error) {
533                 empathy_debug (DEBUG_DOMAIN, "Error getting capabilities: %s",
534                                error->message);
535                 /* FIXME Should set the capabilities of the contacts for which this request
536                  * originated to NONE */
537                 goto OUT;
538         }
539
540         for (i = 0; i < capabilities->len; i++) {
541                 GValueArray *values;
542                 guint        handle;
543                 const gchar *channel_type;
544                 guint        generic;
545                 guint        specific;
546
547                 values = g_ptr_array_index (capabilities, i);
548                 handle = g_value_get_uint (g_value_array_get_nth (values, 0));
549                 channel_type = g_value_get_string (g_value_array_get_nth (values, 1));
550                 generic = g_value_get_uint (g_value_array_get_nth (values, 2));
551                 specific = g_value_get_uint (g_value_array_get_nth (values, 3));
552
553                 tp_contact_factory_update_capabilities (tp_factory,
554                                                         handle,
555                                                         channel_type,
556                                                         generic,
557                                                         specific);
558
559                 g_value_array_free (values);
560         }
561
562         g_ptr_array_free (capabilities, TRUE);
563 OUT:
564         g_object_unref (tp_factory);
565 }
566
567 static void
568 tp_contact_factory_capabilities_changed_cb (DBusGProxy *proxy,
569                                             GPtrArray  *capabilities,
570                                             gpointer    user_data)
571 {
572         EmpathyTpContactFactory *tp_factory = user_data;
573         guint                    i;
574
575         for (i = 0; i < capabilities->len; i++) {
576                 GValueArray *values;
577                 guint        handle;
578                 const gchar *channel_type;
579                 guint        generic;
580                 guint        specific;
581
582                 values = g_ptr_array_index (capabilities, i);
583                 handle = g_value_get_uint (g_value_array_get_nth (values, 0));
584                 channel_type = g_value_get_string (g_value_array_get_nth (values, 1));
585                 generic = g_value_get_uint (g_value_array_get_nth (values, 3));
586                 specific = g_value_get_uint (g_value_array_get_nth (values, 5));
587
588                 tp_contact_factory_update_capabilities (tp_factory,
589                                                         handle,
590                                                         channel_type,
591                                                         generic,
592                                                         specific);
593         }
594 }
595
596 static void
597 tp_contact_factory_request_everything (EmpathyTpContactFactory *tp_factory,
598                                        GArray                  *handles)
599 {
600         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
601
602         if (priv->presence_iface) {
603                 tp_conn_iface_presence_get_presence_async (priv->presence_iface,
604                                                            handles,
605                                                            tp_contact_factory_get_presence_cb,
606                                                            g_object_ref (tp_factory));
607         }
608
609         if (priv->aliasing_iface) {
610                 RequestAliasesData *data;
611
612                 data = g_slice_new (RequestAliasesData);
613                 data->tp_factory = g_object_ref (tp_factory);
614                 data->handles = g_memdup (handles->data, handles->len * sizeof (guint));
615
616                 tp_conn_iface_aliasing_request_aliases_async (priv->aliasing_iface,
617                                                               handles,
618                                                               tp_contact_factory_request_aliases_cb,
619                                                               data);
620         }
621
622         if (priv->avatars_iface) {
623                 tp_conn_iface_avatars_get_known_avatar_tokens_async (priv->avatars_iface,
624                                                                      handles,
625                                                                      tp_contact_factory_get_known_avatar_tokens_cb,
626                                                                      g_object_ref (tp_factory));
627         }
628
629         if (priv->capabilities_iface) {
630                 tp_conn_iface_capabilities_get_capabilities_async (priv->capabilities_iface,
631                                                                    handles,
632                                                                    tp_contact_factory_get_capabilities_cb,
633                                                                    g_object_ref (tp_factory));
634         }
635 }
636
637 typedef struct {
638         EmpathyTpContactFactory *tp_factory;
639         GList                   *contacts;
640 } RequestHandlesData;
641
642 static void
643 tp_contact_factory_request_handles_cb (DBusGProxy *proxy,
644                                        GArray     *handles,
645                                        GError     *error,
646                                        gpointer    user_data)
647 {
648         RequestHandlesData          *data = user_data;
649         EmpathyTpContactFactory     *tp_factory = data->tp_factory;
650         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
651         GList                       *l;
652         guint                        i = 0;
653
654         if (error) {
655                 empathy_debug (DEBUG_DOMAIN, "Failed to request handles: %s",
656                                error->message);
657                 goto OUT;
658         }
659
660         for (l = data->contacts; l; l = l->next) {
661                 guint handle;
662
663                 handle = g_array_index (handles, guint, i);
664                 empathy_contact_set_handle (l->data, handle);
665                 if (handle == priv->self_handle) {
666                         empathy_contact_set_is_user (l->data, TRUE);
667                 }
668
669                 i++;
670         }
671
672         tp_contact_factory_request_everything (tp_factory, handles);
673         g_array_free (handles, TRUE);
674
675 OUT:
676         g_list_foreach (data->contacts, (GFunc) g_object_unref, NULL);
677         g_list_free (data->contacts);
678         g_object_unref (tp_factory);
679         g_slice_free (RequestHandlesData, data);
680 }
681
682 static void
683 tp_contact_factory_disconnect_contact_foreach (gpointer data,
684                                                gpointer user_data)
685 {
686         EmpathyContact *contact = data;
687         
688         empathy_contact_set_presence (contact, MC_PRESENCE_UNSET);
689         empathy_contact_set_handle (contact, 0);
690 }
691
692 static void
693 tp_contact_factory_destroy_cb (TpConn                  *tp_conn,
694                                EmpathyTpContactFactory *tp_factory)
695 {
696         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
697
698         empathy_debug (DEBUG_DOMAIN, "Account disconnected or CM crashed");
699
700         g_object_unref (priv->tp_conn);
701         priv->tp_conn = NULL;
702         priv->aliasing_iface = NULL;
703         priv->avatars_iface = NULL;
704         priv->presence_iface = NULL;
705         priv->capabilities_iface = NULL;
706
707         g_list_foreach (priv->contacts,
708                         tp_contact_factory_disconnect_contact_foreach,
709                         tp_factory);
710 }
711
712 static void
713 tp_contact_factory_disconnect (EmpathyTpContactFactory *tp_factory)
714 {
715         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
716
717         if (priv->aliasing_iface) {
718                 dbus_g_proxy_disconnect_signal (priv->aliasing_iface,
719                                                 "AliasesChanged",
720                                                 G_CALLBACK (tp_contact_factory_aliases_changed_cb),
721                                                 tp_factory);
722         }
723         if (priv->avatars_iface) {
724                 dbus_g_proxy_disconnect_signal (priv->avatars_iface,
725                                                 "AvatarUpdated",
726                                                 G_CALLBACK (tp_contact_factory_avatar_updated_cb),
727                                                 tp_factory);
728                 dbus_g_proxy_disconnect_signal (priv->avatars_iface,
729                                                 "AvatarRetrieved",
730                                                 G_CALLBACK (tp_contact_factory_avatar_retrieved_cb),
731                                                 tp_factory);
732         }
733         if (priv->presence_iface) {
734                 dbus_g_proxy_disconnect_signal (priv->presence_iface,
735                                                 "PresenceUpdate",
736                                                 G_CALLBACK (tp_contact_factory_presence_update_cb),
737                                                 tp_factory);
738         }
739         if (priv->capabilities_iface) {
740                 dbus_g_proxy_disconnect_signal (priv->capabilities_iface,
741                                                 "CapabilitiesChanged",
742                                                 G_CALLBACK (tp_contact_factory_capabilities_changed_cb),
743                                                 tp_factory);
744         }
745         if (priv->tp_conn) {
746                 g_signal_handlers_disconnect_by_func (priv->tp_conn,
747                                                       tp_contact_factory_destroy_cb,
748                                                       tp_factory);
749         }
750 }
751
752 static void
753 tp_contact_factory_update (EmpathyTpContactFactory *tp_factory)
754 {
755         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
756         TpConn                      *tp_conn = NULL;
757         RequestHandlesData          *data;
758         const gchar                **contact_ids;
759         guint                        i;
760         GList                       *l;
761         GError                      *error = NULL;
762
763         if (priv->account) {
764                 guint status;
765
766                 /* status == 0 means the status is CONNECTED */
767                 status = mission_control_get_connection_status (priv->mc,
768                                                                 priv->account,
769                                                                 NULL);
770                 if (status == 0) {
771                         tp_conn = mission_control_get_connection (priv->mc,
772                                                                   priv->account,
773                                                                   NULL);
774                 }
775         }
776
777         if (!tp_conn) {
778                 /* We are not connected anymore, remove the old connection */
779                 tp_contact_factory_disconnect (tp_factory);
780                 if (priv->tp_conn) {
781                         tp_contact_factory_destroy_cb (priv->tp_conn, tp_factory);
782                 }
783                 return;
784         }
785         else if (priv->tp_conn) {
786                 /* We were connected and we still are connected, nothing
787                  * changed so nothing to do. */
788                 g_object_unref (tp_conn);
789                 return;
790         }
791
792         /* We got a new connection */
793         priv->tp_conn = tp_conn;
794         priv->aliasing_iface = tp_conn_get_interface (priv->tp_conn,
795                                                       TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING);
796         priv->avatars_iface = tp_conn_get_interface (priv->tp_conn,
797                                                      TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS);
798         priv->presence_iface = tp_conn_get_interface (priv->tp_conn,
799                                                       TP_IFACE_QUARK_CONNECTION_INTERFACE_PRESENCE);
800         priv->capabilities_iface = tp_conn_get_interface (priv->tp_conn,
801                                                           TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES);
802
803         /* Connect signals */
804         if (priv->aliasing_iface) {
805                 dbus_g_proxy_connect_signal (priv->aliasing_iface,
806                                              "AliasesChanged",
807                                              G_CALLBACK (tp_contact_factory_aliases_changed_cb),
808                                              tp_factory, NULL);
809         }
810         if (priv->avatars_iface) {
811                 dbus_g_proxy_connect_signal (priv->avatars_iface,
812                                              "AvatarUpdated",
813                                              G_CALLBACK (tp_contact_factory_avatar_updated_cb),
814                                              tp_factory, NULL);
815                 dbus_g_proxy_connect_signal (priv->avatars_iface,
816                                              "AvatarRetrieved",
817                                              G_CALLBACK (tp_contact_factory_avatar_retrieved_cb),
818                                              tp_factory, NULL);
819         }
820         if (priv->presence_iface) {
821                 dbus_g_proxy_connect_signal (priv->presence_iface,
822                                              "PresenceUpdate",
823                                              G_CALLBACK (tp_contact_factory_presence_update_cb),
824                                              tp_factory, NULL);
825         }
826         if (priv->capabilities_iface) {
827                 dbus_g_proxy_connect_signal (priv->capabilities_iface,
828                                              "CapabilitiesChanged",
829                                              G_CALLBACK (tp_contact_factory_capabilities_changed_cb),
830                                              tp_factory, NULL);
831         }
832         g_signal_connect (priv->tp_conn, "destroy",
833                           G_CALLBACK (tp_contact_factory_destroy_cb),
834                           tp_factory);
835
836         /* Get our own handle */
837         if (!tp_conn_get_self_handle (DBUS_G_PROXY (priv->tp_conn),
838                                       &priv->self_handle,
839                                       &error)) {
840                 empathy_debug (DEBUG_DOMAIN, "GetSelfHandle Error: %s",
841                               error ? error->message : "No error given");
842                 g_clear_error (&error);
843         }
844
845         /* Request new handles for all contacts */
846         if (priv->contacts) {
847                 data = g_slice_new (RequestHandlesData);
848                 data->tp_factory = g_object_ref (tp_factory);
849                 data->contacts = g_list_copy (priv->contacts);
850                 g_list_foreach (data->contacts, (GFunc) g_object_ref, NULL);
851
852                 i = g_list_length (data->contacts);
853                 contact_ids = g_new0 (const gchar*, i + 1);
854                 i = 0;
855                 for (l = data->contacts; l; l = l->next) {
856                         contact_ids[i] = empathy_contact_get_id (l->data);
857                         i++;
858                 }
859
860                 tp_conn_request_handles_async (DBUS_G_PROXY (priv->tp_conn),
861                                                TP_HANDLE_TYPE_CONTACT,
862                                                contact_ids,
863                                                tp_contact_factory_request_handles_cb,
864                                                data);
865                 g_free (contact_ids);
866         }
867 }
868
869 static void
870 tp_contact_factory_status_changed_cb (MissionControl           *mc,
871                                       TpConnectionStatus        status,
872                                       McPresence                presence,
873                                       TpConnectionStatusReason  reason,
874                                       const gchar              *unique_name,
875                                       EmpathyTpContactFactory  *tp_factory)
876 {
877         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
878         McAccount                   *account;
879
880         account = mc_account_lookup (unique_name);
881         if (account && empathy_account_equal (account, priv->account)) {
882                 tp_contact_factory_update (tp_factory);
883         }
884         g_object_unref (account);
885 }
886
887 static void
888 tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
889                                 EmpathyContact          *contact)
890 {
891         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
892
893         g_object_weak_ref (G_OBJECT (contact),
894                            tp_contact_factory_weak_notify,
895                            tp_factory);
896         priv->contacts = g_list_prepend (priv->contacts, contact);
897
898         if (!priv->presence_iface) {
899                 /* We have no presence iface, set default presence
900                  * to available */
901                 empathy_contact_set_presence (contact, MC_PRESENCE_AVAILABLE);
902         }
903
904         empathy_debug (DEBUG_DOMAIN, "Contact added: %s (%d)",
905                        empathy_contact_get_id (contact),
906                        empathy_contact_get_handle (contact));
907 }
908
909 static void
910 tp_contact_factory_hold_handles_cb (DBusGProxy *proxy,
911                                     GError     *error,
912                                     gpointer    userdata)
913 {
914         if (error) {
915                 empathy_debug (DEBUG_DOMAIN, "Failed to hold handles: %s",
916                                error->message);
917         }
918 }
919
920 EmpathyContact *
921 empathy_tp_contact_factory_get_user (EmpathyTpContactFactory *tp_factory)
922 {
923         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
924
925         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
926
927         return empathy_tp_contact_factory_get_from_handle (tp_factory,
928                                                            priv->self_handle);
929 }
930
931 EmpathyContact *
932 empathy_tp_contact_factory_get_from_id (EmpathyTpContactFactory *tp_factory,
933                                         const gchar             *id)
934 {
935         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
936         EmpathyContact              *contact;
937
938         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
939         g_return_val_if_fail (id != NULL, NULL);
940
941         /* Check if the contact already exists */
942         contact = tp_contact_factory_find_by_id (tp_factory, id);
943         if (contact) {
944                 return g_object_ref (contact);
945         }
946
947         /* Create new contact */
948         contact = g_object_new (EMPATHY_TYPE_CONTACT,
949                                 "account", priv->account,
950                                 "id", id,
951                                 NULL);
952         tp_contact_factory_add_contact (tp_factory, contact);
953
954         /* If the account is connected, request contact's handle */
955         if (priv->tp_conn) {
956                 RequestHandlesData *data;
957                 const gchar        *contact_ids[] = {id, NULL};
958                 
959                 data = g_slice_new (RequestHandlesData);
960                 data->tp_factory = g_object_ref (tp_factory);
961                 data->contacts = g_list_prepend (NULL, g_object_ref (contact));
962                 tp_conn_request_handles_async (DBUS_G_PROXY (priv->tp_conn),
963                                                TP_HANDLE_TYPE_CONTACT,
964                                                contact_ids,
965                                                tp_contact_factory_request_handles_cb,
966                                                data);
967         }
968
969         return contact;
970 }
971
972 EmpathyContact *
973 empathy_tp_contact_factory_get_from_handle (EmpathyTpContactFactory *tp_factory,
974                                             guint                    handle)
975 {
976         EmpathyContact *contact;
977         GArray         *handles;
978         GList          *contacts;
979
980         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
981
982         handles = g_array_new (FALSE, FALSE, sizeof (guint));
983         g_array_append_val (handles, handle);
984
985         contacts = empathy_tp_contact_factory_get_from_handles (tp_factory, handles);
986         g_array_free (handles, TRUE);
987
988         contact = contacts ? contacts->data : NULL;
989         g_list_free (contacts);
990
991         return contact;
992 }
993
994 GList *
995 empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory,
996                                              GArray                  *handles)
997 {
998         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
999         GList                       *contacts = NULL;
1000         GArray                      *new_handles;
1001         gchar                      **handles_names;
1002         guint                        i;
1003         GError                      *error = NULL;
1004
1005         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
1006         g_return_val_if_fail (handles != NULL, NULL);
1007
1008         /* Search all contacts we already have */
1009         new_handles = g_array_new (FALSE, FALSE, sizeof (guint));
1010         for (i = 0; i < handles->len; i++) {
1011                 EmpathyContact *contact;
1012                 guint           handle;
1013
1014                 handle = g_array_index (handles, guint, i);
1015                 if (handle == 0) {
1016                         continue;
1017                 }
1018
1019                 contact = tp_contact_factory_find_by_handle (tp_factory, handle);
1020                 if (contact) {
1021                         contacts = g_list_prepend (contacts, g_object_ref (contact));
1022                 } else {
1023                         g_array_append_val (new_handles, handle);
1024                 }
1025         }
1026
1027         if (new_handles->len == 0) {
1028                 g_array_free (new_handles, TRUE);
1029                 return contacts;
1030         }
1031
1032         /* Get the IDs of all new handles */
1033         if (!tp_conn_inspect_handles (DBUS_G_PROXY (priv->tp_conn),
1034                                       TP_HANDLE_TYPE_CONTACT,
1035                                       new_handles,
1036                                       &handles_names,
1037                                       &error)) {
1038                 empathy_debug (DEBUG_DOMAIN, 
1039                               "Couldn't inspect contact: %s",
1040                               error ? error->message : "No error given");
1041                 g_clear_error (&error);
1042                 g_array_free (new_handles, TRUE);
1043                 return contacts;
1044         }
1045
1046         /* Create new contacts */
1047         for (i = 0; i < new_handles->len; i++) {
1048                 EmpathyContact *contact;
1049                 gchar          *id;
1050                 guint           handle;
1051                 gboolean        is_user;
1052
1053                 id = handles_names[i];
1054                 handle = g_array_index (new_handles, guint, i);
1055
1056                 is_user = (handle == priv->self_handle);
1057                 contact = g_object_new (EMPATHY_TYPE_CONTACT,
1058                                         "account", priv->account,
1059                                         "handle", handle,
1060                                         "id", id,
1061                                         "is-user", is_user,
1062                                         NULL);
1063                 tp_contact_factory_add_contact (tp_factory, contact);
1064                 contacts = g_list_prepend (contacts, contact);
1065                 g_free (id);
1066         }
1067         g_free (handles_names);
1068
1069         /* Hold all new handles. */
1070         tp_conn_hold_handles_async (DBUS_G_PROXY (priv->tp_conn),
1071                                     TP_HANDLE_TYPE_CONTACT,
1072                                     new_handles,
1073                                     tp_contact_factory_hold_handles_cb,
1074                                     NULL);
1075
1076         tp_contact_factory_request_everything (tp_factory, new_handles);
1077
1078         g_array_free (new_handles, TRUE);
1079
1080         return contacts;
1081 }
1082
1083 void
1084 empathy_tp_contact_factory_set_alias (EmpathyTpContactFactory *tp_factory,
1085                                       EmpathyContact          *contact,
1086                                       const gchar             *alias)
1087 {
1088         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1089         GHashTable                  *new_alias;
1090         guint                        handle;
1091
1092         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1093         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1094         g_return_if_fail (empathy_account_equal (empathy_contact_get_account (contact),
1095                                                  priv->account));
1096
1097         if (!priv->aliasing_iface) {
1098                 return;
1099         }
1100
1101         handle = empathy_contact_get_handle (contact);
1102
1103         empathy_debug (DEBUG_DOMAIN, "Setting alias for contact %s (%d) to %s",
1104                        empathy_contact_get_id (contact),
1105                        handle, alias);
1106
1107         new_alias = g_hash_table_new_full (g_direct_hash,
1108                                            g_direct_equal,
1109                                            NULL,
1110                                            g_free);
1111
1112         g_hash_table_insert (new_alias,
1113                              GUINT_TO_POINTER (handle),
1114                              g_strdup (alias));
1115
1116         tp_conn_iface_aliasing_set_aliases_async (priv->aliasing_iface,
1117                                                   new_alias,
1118                                                   tp_contact_factory_set_aliases_cb,
1119                                                   g_object_ref (tp_factory));
1120
1121         g_hash_table_destroy (new_alias);
1122 }
1123
1124 void
1125 empathy_tp_contact_factory_set_avatar (EmpathyTpContactFactory *tp_factory,
1126                                        const gchar             *data,
1127                                        gsize                    size,
1128                                        const gchar             *mime_type)
1129 {
1130         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1131
1132         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1133
1134         if (!priv->avatars_iface) {
1135                 return;
1136         }
1137
1138         if (data && size > 0 && size < G_MAXUINT) {
1139                 GArray avatar;
1140
1141                 avatar.data = (gchar*) data;
1142                 avatar.len = size;
1143
1144                 empathy_debug (DEBUG_DOMAIN, "Setting avatar on account %s",
1145                                mc_account_get_unique_name (priv->account));
1146
1147                 tp_conn_iface_avatars_set_avatar_async (priv->avatars_iface,
1148                                                         &avatar,
1149                                                         mime_type,
1150                                                         tp_contact_factory_set_avatar_cb,
1151                                                         g_object_ref (tp_factory));
1152         } else {
1153                 empathy_debug (DEBUG_DOMAIN, "Clearing avatar on account %s",
1154                                mc_account_get_unique_name (priv->account));
1155                 tp_conn_iface_avatars_clear_avatar_async (priv->avatars_iface,
1156                                                           tp_contact_factory_clear_avatar_cb,
1157                                                           g_object_ref (tp_factory));
1158         }
1159 }
1160
1161 static void
1162 tp_contact_factory_get_property (GObject    *object,
1163                                  guint       param_id,
1164                                  GValue     *value,
1165                                  GParamSpec *pspec)
1166 {
1167         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1168
1169         switch (param_id) {
1170         case PROP_ACCOUNT:
1171                 g_value_set_object (value, priv->account);
1172                 break;
1173         default:
1174                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1175                 break;
1176         };
1177 }
1178
1179 static void
1180 tp_contact_factory_set_property (GObject      *object,
1181                                  guint         param_id,
1182                                  const GValue *value,
1183                                  GParamSpec   *pspec)
1184 {
1185         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1186
1187         switch (param_id) {
1188         case PROP_ACCOUNT:
1189                 priv->account = g_object_ref (g_value_get_object (value));
1190                 break;
1191         default:
1192                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1193                 break;
1194         };
1195 }
1196
1197 static void
1198 tp_contact_factory_finalize (GObject *object)
1199 {
1200         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1201         GList                       *l;
1202
1203         empathy_debug (DEBUG_DOMAIN, "Finalized: %p (%s)",
1204                        object,
1205                        mc_account_get_normalized_name (priv->account));
1206
1207         tp_contact_factory_disconnect (EMPATHY_TP_CONTACT_FACTORY (object));
1208         dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
1209                                         "AccountStatusChanged",
1210                                         G_CALLBACK (tp_contact_factory_status_changed_cb),
1211                                         object);
1212
1213         for (l = priv->contacts; l; l = l->next) {
1214                 g_object_weak_unref (G_OBJECT (l->data),
1215                                      tp_contact_factory_weak_notify,
1216                                      object);
1217         }
1218
1219         g_list_free (priv->contacts);
1220         g_object_unref (priv->mc);
1221         g_object_unref (priv->account);
1222
1223         if (priv->tp_conn) {
1224                 g_object_unref (priv->tp_conn);
1225         }
1226
1227         G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->finalize (object);
1228 }
1229
1230 static GObject *
1231 tp_contact_factory_constructor (GType                  type,
1232                                 guint                  n_props,
1233                                 GObjectConstructParam *props)
1234 {
1235         GObject *tp_factory;
1236
1237         tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
1238
1239         tp_contact_factory_update (EMPATHY_TP_CONTACT_FACTORY (tp_factory));
1240
1241         return tp_factory;
1242 }
1243
1244
1245 static void
1246 empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass)
1247 {
1248         GObjectClass *object_class = G_OBJECT_CLASS (klass);
1249
1250         object_class->finalize = tp_contact_factory_finalize;
1251         object_class->constructor = tp_contact_factory_constructor;
1252         object_class->get_property = tp_contact_factory_get_property;
1253         object_class->set_property = tp_contact_factory_set_property;
1254
1255         /* Construct-only properties */
1256         g_object_class_install_property (object_class,
1257                                          PROP_ACCOUNT,
1258                                          g_param_spec_object ("account",
1259                                                               "Factory's Account",
1260                                                               "The account associated with the factory",
1261                                                               MC_TYPE_ACCOUNT,
1262                                                               G_PARAM_READWRITE |
1263                                                               G_PARAM_CONSTRUCT_ONLY));
1264
1265         g_type_class_add_private (object_class, sizeof (EmpathyTpContactFactoryPriv));
1266 }
1267
1268 static void
1269 empathy_tp_contact_factory_init (EmpathyTpContactFactory *tp_factory)
1270 {
1271         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1272
1273         priv->mc = empathy_mission_control_new ();
1274         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
1275                                      "AccountStatusChanged",
1276                                      G_CALLBACK (tp_contact_factory_status_changed_cb),
1277                                      tp_factory, NULL);
1278 }
1279
1280 EmpathyTpContactFactory *
1281 empathy_tp_contact_factory_new (McAccount *account)
1282 {
1283         return g_object_new (EMPATHY_TYPE_TP_CONTACT_FACTORY,
1284                              "account", account,
1285                              NULL);
1286 }
1287