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