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