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