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