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