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