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