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