]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-contact-factory.c
Remove previous hack, it was stupid, we have a param to increase dbus timeout now...
[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         /* FIXME: Sometimes the dbus call timesout because CM takes
603          * too much time to request all aliases from the server,
604          * that's why we increase the timeout here. See fd.o bug #14795 */
605         dup_handles = g_memdup (handles->data, handles->len * sizeof (guint));
606         tp_cli_connection_interface_aliasing_call_request_aliases (priv->connection,
607                                                                    5*60*1000,
608                                                                    handles,
609                                                                    tp_contact_factory_request_aliases_cb,
610                                                                    dup_handles, g_free,
611                                                                    G_OBJECT (tp_factory));
612
613         tp_cli_connection_interface_avatars_call_get_known_avatar_tokens (priv->connection,
614                                                                           -1,
615                                                                           handles,
616                                                                           tp_contact_factory_get_known_avatar_tokens_cb,
617                                                                           NULL, NULL,
618                                                                           G_OBJECT (tp_factory));
619
620         tp_cli_connection_interface_capabilities_call_get_capabilities (priv->connection,
621                                                                         -1,
622                                                                         handles,
623                                                                         tp_contact_factory_get_capabilities_cb,
624                                                                         NULL, NULL,
625                                                                         G_OBJECT (tp_factory));
626 }
627
628 static void
629 tp_contact_factory_list_free (gpointer data)
630 {
631         GList *l = data;
632
633         g_list_foreach (l, (GFunc) g_object_unref, NULL);
634         g_list_free (l);
635 }
636
637 static void
638 tp_contact_factory_request_handles_cb (TpConnection *connection,
639                                        const GArray *handles,
640                                        const GError *error,
641                                        gpointer      user_data,
642                                        GObject      *tp_factory)
643 {
644         GList *contacts = user_data;
645         GList *l;
646         guint  i = 0;
647
648         if (error) {
649                 empathy_debug (DEBUG_DOMAIN, "Failed to request handles: %s",
650                                error->message);
651                 return;
652         }
653
654         for (l = contacts; l; l = l->next) {
655                 guint handle;
656
657                 handle = g_array_index (handles, guint, i);
658                 empathy_contact_set_handle (l->data, handle);
659
660                 i++;
661         }
662
663         tp_contact_factory_request_everything (EMPATHY_TP_CONTACT_FACTORY (tp_factory),
664                                                handles);
665 }
666
667 static void
668 tp_contact_factory_inspect_handles_cb (TpConnection  *connection,
669                                        const gchar  **ids,
670                                        const GError  *error,
671                                        gpointer       user_data,
672                                        GObject       *tp_factory)
673 {
674         const gchar **id;
675         GList        *contacts = user_data;
676         GList        *l;
677
678         if (error) {
679                 empathy_debug (DEBUG_DOMAIN, "Failed to inspect handles: %s",
680                                error->message);
681                 return;
682         }
683
684         id = ids;
685         for (l = contacts; l; l = l->next) {
686                 empathy_contact_set_id (l->data, *id);
687                 id++;
688         }
689 }
690
691 static void
692 tp_contact_factory_disconnect_contact_foreach (gpointer data,
693                                                gpointer user_data)
694 {
695         EmpathyContact *contact = data;
696         
697         empathy_contact_set_presence (contact, MC_PRESENCE_UNSET);
698         empathy_contact_set_handle (contact, 0);
699 }
700
701 static void
702 tp_contact_factory_connection_invalidated_cb (EmpathyTpContactFactory *tp_factory)
703 {
704         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
705
706         empathy_debug (DEBUG_DOMAIN, "Connection invalidated");
707
708         g_object_unref (priv->connection);
709         priv->connection = NULL;
710         priv->ready = FALSE;
711         g_object_notify (G_OBJECT (tp_factory), "ready");
712
713
714         g_list_foreach (priv->contacts,
715                         tp_contact_factory_disconnect_contact_foreach,
716                         tp_factory);
717 }
718
719
720 static void
721 tp_contact_factory_got_self_handle_cb (TpConnection *proxy,
722                                        guint         handle,
723                                        const GError *error,
724                                        gpointer      user_data,
725                                        GObject      *tp_factory)
726 {
727         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
728         GList                       *l;
729         GArray                      *handle_needed;
730         GArray                      *id_needed;
731         GList                       *handle_needed_contacts = NULL;
732         GList                       *id_needed_contacts = NULL;
733
734         if (error) {
735                 empathy_debug (DEBUG_DOMAIN, "Failed to get self handles: %s",
736                                error->message);
737                 return;
738         }
739
740         empathy_debug (DEBUG_DOMAIN, "Connection ready");
741
742         empathy_contact_set_handle (priv->user, handle);
743         priv->ready = TRUE;
744         g_object_notify (tp_factory, "ready");
745
746         /* Connect signals */
747         tp_cli_connection_interface_aliasing_connect_to_aliases_changed (priv->connection,
748                                                                          tp_contact_factory_aliases_changed_cb,
749                                                                          NULL, NULL,
750                                                                          G_OBJECT (tp_factory),
751                                                                          NULL);
752         tp_cli_connection_interface_avatars_connect_to_avatar_updated (priv->connection,
753                                                                        tp_contact_factory_avatar_updated_cb,
754                                                                        NULL, NULL,
755                                                                        G_OBJECT (tp_factory),
756                                                                        NULL);
757         tp_cli_connection_interface_avatars_connect_to_avatar_retrieved (priv->connection,
758                                                                          tp_contact_factory_avatar_retrieved_cb,
759                                                                          NULL, NULL,
760                                                                          G_OBJECT (tp_factory),
761                                                                          NULL);
762         tp_cli_connection_interface_presence_connect_to_presence_update (priv->connection,
763                                                                          tp_contact_factory_presence_update_cb,
764                                                                          NULL, NULL,
765                                                                          G_OBJECT (tp_factory),
766                                                                          NULL);
767         tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection,
768                                                                                   tp_contact_factory_capabilities_changed_cb,
769                                                                                   NULL, NULL,
770                                                                                   G_OBJECT (tp_factory),
771                                                                                   NULL);
772
773         /* Request needed info for all existing contacts */
774         handle_needed = g_array_new (TRUE, FALSE, sizeof (gchar*));
775         id_needed = g_array_new (FALSE, FALSE, sizeof (guint));
776         for (l = priv->contacts; l; l = l->next) {
777                 EmpathyContact *contact;
778                 guint           handle;
779                 const gchar    *id;
780
781                 contact = l->data;
782                 handle = empathy_contact_get_handle (contact);
783                 id = empathy_contact_get_id (contact);
784                 if (handle == 0) {
785                         g_assert (!G_STR_EMPTY (id));
786                         g_array_append_val (handle_needed, id);
787                         handle_needed_contacts = g_list_prepend (handle_needed_contacts,
788                                                                  g_object_ref (contact));
789                 }
790                 if (G_STR_EMPTY (id)) {
791                         g_array_append_val (id_needed, handle);
792                         id_needed_contacts = g_list_prepend (id_needed_contacts,
793                                                              g_object_ref (contact));
794                 }
795         }
796         handle_needed_contacts = g_list_reverse (handle_needed_contacts);
797         id_needed_contacts = g_list_reverse (id_needed_contacts);
798
799         tp_cli_connection_call_request_handles (priv->connection,
800                                                 -1,
801                                                 TP_HANDLE_TYPE_CONTACT,
802                                                 (const gchar**) handle_needed->data,
803                                                 tp_contact_factory_request_handles_cb,
804                                                 handle_needed_contacts, tp_contact_factory_list_free,
805                                                 G_OBJECT (tp_factory));
806
807         tp_contact_factory_request_everything ((EmpathyTpContactFactory*) tp_factory,
808                                                id_needed);
809         tp_cli_connection_call_inspect_handles (priv->connection,
810                                                 -1,
811                                                 TP_HANDLE_TYPE_CONTACT,
812                                                 id_needed,
813                                                 tp_contact_factory_inspect_handles_cb,
814                                                 id_needed_contacts, tp_contact_factory_list_free,
815                                                 G_OBJECT (tp_factory));
816
817         g_array_free (handle_needed, TRUE);
818         g_array_free (id_needed, TRUE);
819 }
820
821 static void
822 tp_contact_factory_connection_ready_cb (EmpathyTpContactFactory *tp_factory)
823 {
824         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
825
826         /* Get our own handle */
827         tp_cli_connection_call_get_self_handle (priv->connection,
828                                                 -1,
829                                                 tp_contact_factory_got_self_handle_cb,
830                                                 NULL, NULL,
831                                                 G_OBJECT (tp_factory));
832 }
833
834 static void
835 tp_contact_factory_status_updated (EmpathyTpContactFactory *tp_factory)
836 {
837         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
838         TpConn                      *tp_conn;
839         gboolean                     connection_ready;
840
841         if (priv->connection) {
842                 /* We already have our connection object */
843                 return;
844         }
845
846         tp_conn = mission_control_get_connection (priv->mc, priv->account, NULL);
847         if (!tp_conn) {
848                 return;
849         }
850
851         /* We got a new connection, wait for it to be ready */
852         priv->connection = tp_conn_dup_connection (tp_conn);
853         g_object_unref (tp_conn);
854
855         g_signal_connect_swapped (priv->connection, "invalidated",
856                                   G_CALLBACK (tp_contact_factory_connection_invalidated_cb),
857                                   tp_factory);
858
859         g_object_get (priv->connection, "connection-ready", &connection_ready, NULL);
860         if (connection_ready) {
861                 tp_contact_factory_connection_ready_cb (tp_factory);
862         } else {
863                 g_signal_connect_swapped (priv->connection, "notify::connection-ready",
864                                           G_CALLBACK (tp_contact_factory_connection_ready_cb),
865                                           tp_factory);
866         }
867 }
868
869 static void
870 tp_contact_factory_status_changed_cb (MissionControl           *mc,
871                                       TpConnectionStatus        status,
872                                       McPresence                presence,
873                                       TpConnectionStatusReason  reason,
874                                       const gchar              *unique_name,
875                                       EmpathyTpContactFactory  *tp_factory)
876 {
877         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
878         McAccount                   *account;
879
880         account = mc_account_lookup (unique_name);
881         if (account && empathy_account_equal (account, priv->account)) {
882                 tp_contact_factory_status_updated (tp_factory);
883         }
884         g_object_unref (account);
885 }
886
887 static void
888 tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
889                                 EmpathyContact          *contact)
890 {
891         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
892
893         g_object_weak_ref (G_OBJECT (contact),
894                            tp_contact_factory_weak_notify,
895                            tp_factory);
896         priv->contacts = g_list_prepend (priv->contacts, contact);
897
898         empathy_debug (DEBUG_DOMAIN, "Contact added: %s (%d)",
899                        empathy_contact_get_id (contact),
900                        empathy_contact_get_handle (contact));
901 }
902
903 static void
904 tp_contact_factory_hold_handles_cb (TpConnection *connection,
905                                     const GError *error,
906                                     gpointer      userdata,
907                                     GObject      *tp_factory)
908 {
909         if (error) {
910                 empathy_debug (DEBUG_DOMAIN, "Failed to hold handles: %s",
911                                error->message);
912         }
913 }
914
915 EmpathyContact *
916 empathy_tp_contact_factory_get_user (EmpathyTpContactFactory *tp_factory)
917 {
918         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
919
920         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
921
922         return g_object_ref (priv->user);
923 }
924
925 EmpathyContact *
926 empathy_tp_contact_factory_get_from_id (EmpathyTpContactFactory *tp_factory,
927                                         const gchar             *id)
928 {
929         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
930         EmpathyContact              *contact;
931
932         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
933         g_return_val_if_fail (id != NULL, NULL);
934
935         /* Check if the contact already exists */
936         contact = tp_contact_factory_find_by_id (tp_factory, id);
937         if (contact) {
938                 return g_object_ref (contact);
939         }
940
941         /* Create new contact */
942         contact = g_object_new (EMPATHY_TYPE_CONTACT,
943                                 "account", priv->account,
944                                 "id", id,
945                                 NULL);
946         tp_contact_factory_add_contact (tp_factory, contact);
947
948         if (priv->ready) {
949                 const gchar *contact_ids[] = {id, NULL};
950                 GList       *contacts;
951                 
952                 contacts = g_list_prepend (NULL, g_object_ref (contact));
953                 tp_cli_connection_call_request_handles (priv->connection,
954                                                         -1,
955                                                         TP_HANDLE_TYPE_CONTACT,
956                                                         contact_ids,
957                                                         tp_contact_factory_request_handles_cb,
958                                                         contacts, tp_contact_factory_list_free,
959                                                         G_OBJECT (tp_factory));
960         }
961
962         return contact;
963 }
964
965 EmpathyContact *
966 empathy_tp_contact_factory_get_from_handle (EmpathyTpContactFactory *tp_factory,
967                                             guint                    handle)
968 {
969         EmpathyContact *contact;
970         GArray         *handles;
971         GList          *contacts;
972
973         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
974
975         handles = g_array_new (FALSE, FALSE, sizeof (guint));
976         g_array_append_val (handles, handle);
977
978         contacts = empathy_tp_contact_factory_get_from_handles (tp_factory, handles);
979         g_array_free (handles, TRUE);
980
981         contact = contacts ? contacts->data : NULL;
982         g_list_free (contacts);
983
984         return contact;
985 }
986
987 GList *
988 empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory,
989                                              const GArray            *handles)
990 {
991         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
992         GList                       *contacts = NULL;
993         GArray                      *new_handles;
994         GList                       *new_contacts = NULL;
995         guint                        i;
996
997         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL);
998         g_return_val_if_fail (handles != NULL, NULL);
999
1000         /* Search all contacts we already have */
1001         new_handles = g_array_new (FALSE, FALSE, sizeof (guint));
1002         for (i = 0; i < handles->len; i++) {
1003                 EmpathyContact *contact;
1004                 guint           handle;
1005
1006                 handle = g_array_index (handles, guint, i);
1007                 if (handle == 0) {
1008                         continue;
1009                 }
1010
1011                 contact = tp_contact_factory_find_by_handle (tp_factory, handle);
1012                 if (contact) {
1013                         contacts = g_list_prepend (contacts, g_object_ref (contact));
1014                 } else {
1015                         g_array_append_val (new_handles, handle);
1016                 }
1017         }
1018
1019         if (new_handles->len == 0) {
1020                 g_array_free (new_handles, TRUE);
1021                 return contacts;
1022         }
1023
1024         /* Create new contacts */
1025         for (i = 0; i < new_handles->len; i++) {
1026                 EmpathyContact *contact;
1027                 guint           handle;
1028
1029                 handle = g_array_index (new_handles, guint, i);
1030
1031                 contact = g_object_new (EMPATHY_TYPE_CONTACT,
1032                                         "account", priv->account,
1033                                         "handle", handle,
1034                                         NULL);
1035                 tp_contact_factory_add_contact (tp_factory, contact);
1036                 contacts = g_list_prepend (contacts, contact);
1037                 new_contacts = g_list_prepend (new_contacts, g_object_ref (contact));
1038         }
1039         new_contacts = g_list_reverse (new_contacts);
1040
1041         if (priv->ready) {
1042                 /* Get the IDs of all new handles */
1043                 tp_cli_connection_call_inspect_handles (priv->connection,
1044                                                         -1,
1045                                                         TP_HANDLE_TYPE_CONTACT,
1046                                                         new_handles,
1047                                                         tp_contact_factory_inspect_handles_cb,
1048                                                         new_contacts, tp_contact_factory_list_free,
1049                                                         G_OBJECT (tp_factory));
1050
1051                 /* Hold all new handles. */
1052                 /* FIXME: Should be unholded when removed from the factory */
1053                 tp_cli_connection_call_hold_handles (priv->connection,
1054                                                      -1,
1055                                                      TP_HANDLE_TYPE_CONTACT,
1056                                                      new_handles,
1057                                                      tp_contact_factory_hold_handles_cb,
1058                                                      NULL, NULL,
1059                                                      G_OBJECT (tp_factory));
1060
1061                 tp_contact_factory_request_everything (tp_factory, new_handles);
1062         }
1063
1064         return contacts;
1065 }
1066
1067 void
1068 empathy_tp_contact_factory_set_alias (EmpathyTpContactFactory *tp_factory,
1069                                       EmpathyContact          *contact,
1070                                       const gchar             *alias)
1071 {
1072         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1073         GHashTable                  *new_alias;
1074         guint                        handle;
1075
1076         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1077         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1078         g_return_if_fail (empathy_account_equal (empathy_contact_get_account (contact),
1079                                                  priv->account));
1080
1081         if (!priv->ready) {
1082                 return;
1083         }
1084
1085         handle = empathy_contact_get_handle (contact);
1086
1087         empathy_debug (DEBUG_DOMAIN, "Setting alias for contact %s (%d) to %s",
1088                        empathy_contact_get_id (contact),
1089                        handle, alias);
1090
1091         new_alias = g_hash_table_new_full (g_direct_hash,
1092                                            g_direct_equal,
1093                                            NULL,
1094                                            g_free);
1095
1096         g_hash_table_insert (new_alias,
1097                              GUINT_TO_POINTER (handle),
1098                              g_strdup (alias));
1099
1100         tp_cli_connection_interface_aliasing_call_set_aliases (priv->connection,
1101                                                                -1,
1102                                                                new_alias,
1103                                                                tp_contact_factory_set_aliases_cb,
1104                                                                NULL, NULL,
1105                                                                G_OBJECT (tp_factory));
1106
1107         g_hash_table_destroy (new_alias);
1108 }
1109
1110 void
1111 empathy_tp_contact_factory_set_avatar (EmpathyTpContactFactory *tp_factory,
1112                                        const gchar             *data,
1113                                        gsize                    size,
1114                                        const gchar             *mime_type)
1115 {
1116         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1117
1118         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1119
1120         if (!priv->ready) {
1121                 return;
1122         }
1123
1124         if (data && size > 0 && size < G_MAXUINT) {
1125                 GArray avatar;
1126
1127                 avatar.data = (gchar*) data;
1128                 avatar.len = size;
1129
1130                 empathy_debug (DEBUG_DOMAIN, "Setting avatar on account %s",
1131                                mc_account_get_unique_name (priv->account));
1132
1133                 tp_cli_connection_interface_avatars_call_set_avatar (priv->connection,
1134                                                                      -1,
1135                                                                      &avatar,
1136                                                                      mime_type,
1137                                                                      tp_contact_factory_set_avatar_cb,
1138                                                                      NULL, NULL,
1139                                                                      G_OBJECT (tp_factory));
1140         } else {
1141                 empathy_debug (DEBUG_DOMAIN, "Clearing avatar on account %s",
1142                                mc_account_get_unique_name (priv->account));
1143
1144                 tp_cli_connection_interface_avatars_call_clear_avatar (priv->connection,
1145                                                                        -1,
1146                                                                        tp_contact_factory_clear_avatar_cb,
1147                                                                        NULL, NULL,
1148                                                                        G_OBJECT (tp_factory));
1149         }
1150 }
1151
1152 gboolean
1153 empathy_tp_contact_factory_is_ready (EmpathyTpContactFactory *tp_factory)
1154 {
1155         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1156
1157         g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), FALSE);
1158
1159         return priv->ready;
1160 }
1161
1162 static void
1163 tp_contact_factory_get_property (GObject    *object,
1164                                  guint       param_id,
1165                                  GValue     *value,
1166                                  GParamSpec *pspec)
1167 {
1168         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1169
1170         switch (param_id) {
1171         case PROP_ACCOUNT:
1172                 g_value_set_object (value, priv->account);
1173                 break;
1174         case PROP_READY:
1175                 g_value_set_boolean (value, priv->ready);
1176                 break;
1177         default:
1178                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1179                 break;
1180         };
1181 }
1182
1183 static void
1184 tp_contact_factory_set_property (GObject      *object,
1185                                  guint         param_id,
1186                                  const GValue *value,
1187                                  GParamSpec   *pspec)
1188 {
1189         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1190
1191         switch (param_id) {
1192         case PROP_ACCOUNT:
1193                 priv->account = g_object_ref (g_value_get_object (value));
1194                 break;
1195         default:
1196                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1197                 break;
1198         };
1199 }
1200
1201 static void
1202 tp_contact_factory_finalize (GObject *object)
1203 {
1204         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1205         GList                       *l;
1206
1207         empathy_debug (DEBUG_DOMAIN, "Finalized: %p (%s)",
1208                        object,
1209                        mc_account_get_normalized_name (priv->account));
1210
1211         dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
1212                                         "AccountStatusChanged",
1213                                         G_CALLBACK (tp_contact_factory_status_changed_cb),
1214                                         object);
1215
1216         for (l = priv->contacts; l; l = l->next) {
1217                 g_object_weak_unref (G_OBJECT (l->data),
1218                                      tp_contact_factory_weak_notify,
1219                                      object);
1220         }
1221
1222         g_list_free (priv->contacts);
1223         g_object_unref (priv->mc);
1224         g_object_unref (priv->account);
1225         g_object_unref (priv->user);
1226
1227         if (priv->connection) {
1228                 g_signal_handlers_disconnect_by_func (priv->connection,
1229                                                       tp_contact_factory_connection_invalidated_cb,
1230                                                       object);
1231                 g_object_unref (priv->connection);
1232         }
1233
1234         G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->finalize (object);
1235 }
1236
1237 static GObject *
1238 tp_contact_factory_constructor (GType                  type,
1239                                 guint                  n_props,
1240                                 GObjectConstructParam *props)
1241 {
1242         GObject *tp_factory;
1243         EmpathyTpContactFactoryPriv *priv;
1244
1245         tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
1246         priv = GET_PRIV (tp_factory);
1247
1248         priv->ready = FALSE;
1249         priv->user = empathy_contact_new (priv->account);
1250         empathy_contact_set_is_user (priv->user, TRUE);
1251         tp_contact_factory_add_contact ((EmpathyTpContactFactory*) tp_factory, priv->user);
1252         tp_contact_factory_status_updated (EMPATHY_TP_CONTACT_FACTORY (tp_factory));
1253
1254         return tp_factory;
1255 }
1256
1257 static void
1258 empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass)
1259 {
1260         GObjectClass *object_class = G_OBJECT_CLASS (klass);
1261
1262         object_class->finalize = tp_contact_factory_finalize;
1263         object_class->constructor = tp_contact_factory_constructor;
1264         object_class->get_property = tp_contact_factory_get_property;
1265         object_class->set_property = tp_contact_factory_set_property;
1266
1267         g_object_class_install_property (object_class,
1268                                          PROP_ACCOUNT,
1269                                          g_param_spec_object ("account",
1270                                                               "Factory's Account",
1271                                                               "The account associated with the factory",
1272                                                               MC_TYPE_ACCOUNT,
1273                                                               G_PARAM_READWRITE |
1274                                                               G_PARAM_CONSTRUCT_ONLY));
1275         g_object_class_install_property (object_class,
1276                                          PROP_READY,
1277                                          g_param_spec_boolean ("ready",
1278                                                                "Wheter the factor is ready",
1279                                                                "Is the factory ready",
1280                                                                FALSE,
1281                                                                G_PARAM_READABLE));
1282
1283         g_type_class_add_private (object_class, sizeof (EmpathyTpContactFactoryPriv));
1284 }
1285
1286 static void
1287 empathy_tp_contact_factory_init (EmpathyTpContactFactory *tp_factory)
1288 {
1289         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1290
1291         priv->mc = empathy_mission_control_new ();
1292         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
1293                                      "AccountStatusChanged",
1294                                      G_CALLBACK (tp_contact_factory_status_changed_cb),
1295                                      tp_factory, NULL);
1296 }
1297
1298 EmpathyTpContactFactory *
1299 empathy_tp_contact_factory_new (McAccount *account)
1300 {
1301         return g_object_new (EMPATHY_TYPE_TP_CONTACT_FACTORY,
1302                              "account", account,
1303                              NULL);
1304 }
1305