]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-contact-factory.c
empathy-idle: remove trailing tabs
[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-2009 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/gtypes.h>
28 #include <telepathy-glib/dbus.h>
29 #if HAVE_GEOCLUE
30 #include <geoclue/geoclue-geocode.h>
31 #endif
32
33 #include <extensions/extensions.h>
34
35 #include "empathy-tp-contact-factory.h"
36 #include "empathy-utils.h"
37 #include "empathy-location.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CONTACT
40 #include "empathy-debug.h"
41
42 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpContactFactory)
43 typedef struct {
44         TpConnection   *connection;
45         GList          *contacts;
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         gboolean        can_request_ft;
54         gboolean        can_request_st;
55 } EmpathyTpContactFactoryPriv;
56
57 G_DEFINE_TYPE (EmpathyTpContactFactory, empathy_tp_contact_factory, G_TYPE_OBJECT);
58
59 enum {
60         PROP_0,
61         PROP_CONNECTION,
62
63         PROP_MIME_TYPES,
64         PROP_MIN_WIDTH,
65         PROP_MIN_HEIGHT,
66         PROP_MAX_WIDTH,
67         PROP_MAX_HEIGHT,
68         PROP_MAX_SIZE
69 };
70
71 static TpContactFeature contact_features[] = {
72         TP_CONTACT_FEATURE_ALIAS,
73         TP_CONTACT_FEATURE_PRESENCE,
74 };
75
76 static EmpathyContact *
77 tp_contact_factory_find_by_handle (EmpathyTpContactFactory *tp_factory,
78                                    guint                    handle)
79 {
80         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
81         GList                       *l;
82
83         for (l = priv->contacts; l; l = l->next) {
84                 if (empathy_contact_get_handle (l->data) == handle) {
85                         return l->data;
86                 }
87         }
88
89         return NULL;
90 }
91
92 static EmpathyContact *
93 tp_contact_factory_find_by_tp_contact (EmpathyTpContactFactory *tp_factory,
94                                        TpContact               *tp_contact)
95 {
96         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
97         GList                       *l;
98
99         for (l = priv->contacts; l; l = l->next) {
100                 if (empathy_contact_get_tp_contact (l->data) == tp_contact) {
101                         return l->data;
102                 }
103         }
104
105         return NULL;
106 }
107
108 static void
109 tp_contact_factory_weak_notify (gpointer data,
110                                 GObject *where_the_object_was)
111 {
112         EmpathyTpContactFactoryPriv *priv = GET_PRIV (data);
113
114         DEBUG ("Remove finalized contact %p", where_the_object_was);
115
116         priv->contacts = g_list_remove (priv->contacts, where_the_object_was);
117 }
118
119 static void
120 tp_contact_factory_set_aliases_cb (TpConnection *connection,
121                                    const GError *error,
122                                    gpointer      user_data,
123                                    GObject      *tp_factory)
124 {
125         if (error) {
126                 DEBUG ("Error: %s", error->message);
127         }
128 }
129
130 static void
131 tp_contact_factory_set_location_cb (TpProxy *proxy,
132                                     const GError *error,
133                                     gpointer user_data,
134                                     GObject *weak_object)
135 {
136         if (error != NULL) {
137                 DEBUG ("Error setting location: %s", error->message);
138         }
139 }
140
141 static void
142 tp_contact_factory_set_avatar_cb (TpConnection *connection,
143                                   const gchar  *token,
144                                   const GError *error,
145                                   gpointer      user_data,
146                                   GObject      *tp_factory)
147 {
148         if (error) {
149                 DEBUG ("Error: %s", error->message);
150         }
151 }
152
153 static void
154 tp_contact_factory_clear_avatar_cb (TpConnection *connection,
155                                     const GError *error,
156                                     gpointer      user_data,
157                                     GObject      *tp_factory)
158 {
159         if (error) {
160                 DEBUG ("Error: %s", error->message);
161         }
162 }
163
164 static void
165 tp_contact_factory_avatar_retrieved_cb (TpConnection *connection,
166                                         guint         handle,
167                                         const gchar  *token,
168                                         const GArray *avatar_data,
169                                         const gchar  *mime_type,
170                                         gpointer      user_data,
171                                         GObject      *tp_factory)
172 {
173         EmpathyContact *contact;
174
175         contact = tp_contact_factory_find_by_handle (EMPATHY_TP_CONTACT_FACTORY (tp_factory),
176                                                      handle);
177         if (!contact) {
178                 return;
179         }
180
181         DEBUG ("Avatar retrieved for contact %s (%d)",
182                 empathy_contact_get_id (contact),
183                 handle);
184
185         empathy_contact_load_avatar_data (contact,
186                                           avatar_data->data,
187                                           avatar_data->len,
188                                           mime_type,
189                                           token);
190 }
191
192 static void
193 tp_contact_factory_request_avatars_cb (TpConnection *connection,
194                                        const GError *error,
195                                        gpointer      user_data,
196                                        GObject      *tp_factory)
197 {
198         if (error) {
199                 DEBUG ("Error: %s", error->message);
200         }
201 }
202
203 static gboolean
204 tp_contact_factory_avatar_maybe_update (EmpathyTpContactFactory *tp_factory,
205                                         guint                    handle,
206                                         const gchar             *token)
207 {
208         EmpathyContact *contact;
209         EmpathyAvatar  *avatar;
210
211         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
212         if (!contact) {
213                 return TRUE;
214         }
215
216         /* Check if we have an avatar */
217         if (EMP_STR_EMPTY (token)) {
218                 empathy_contact_set_avatar (contact, NULL);
219                 return TRUE;
220         }
221
222         /* Check if the avatar changed */
223         avatar = empathy_contact_get_avatar (contact);
224         if (avatar && !tp_strdiff (avatar->token, token)) {
225                 return TRUE;
226         }
227
228         /* The avatar changed, search the new one in the cache */
229         if (empathy_contact_load_avatar_cache (contact, token)) {
230                 /* Got from cache, use it */
231                 return TRUE;
232         }
233
234         /* Avatar is not up-to-date, we have to request it. */
235         return FALSE;
236 }
237
238 typedef struct {
239         EmpathyTpContactFactory *tp_factory;
240         GArray                  *handles;
241 } TokensData;
242
243 static void
244 tp_contact_factory_avatar_tokens_foreach (gpointer key,
245                                           gpointer value,
246                                           gpointer user_data)
247 {
248         TokensData  *data = user_data;
249         const gchar *token = value;
250         guint        handle = GPOINTER_TO_UINT (key);
251
252         if (!tp_contact_factory_avatar_maybe_update (data->tp_factory,
253                                                      handle, token)) {
254                 g_array_append_val (data->handles, handle);
255         }
256 }
257
258 static void
259 tp_contact_factory_got_known_avatar_tokens (EmpathyTpContactFactory *tp_factory,
260                                             GHashTable              *tokens,
261                                             const GError            *error)
262 {
263         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
264         TokensData data;
265
266         if (error) {
267                 DEBUG ("Error: %s", error->message);
268                 return;
269         }
270
271         data.tp_factory = tp_factory;
272         data.handles = g_array_new (FALSE, FALSE, sizeof (guint));
273         g_hash_table_foreach (tokens,
274                               tp_contact_factory_avatar_tokens_foreach,
275                               &data);
276
277         DEBUG ("Got %d tokens, need to request %d avatars",
278                 g_hash_table_size (tokens), data.handles->len);
279
280         /* Request needed avatars */
281         if (data.handles->len > 0) {
282                 tp_cli_connection_interface_avatars_call_request_avatars (priv->connection,
283                                                                           -1,
284                                                                           data.handles,
285                                                                           tp_contact_factory_request_avatars_cb,
286                                                                           NULL, NULL,
287                                                                           G_OBJECT (tp_factory));
288         }
289
290         g_array_free (data.handles, TRUE);
291         g_hash_table_destroy (tokens);
292 }
293
294 static void
295 tp_contact_factory_avatar_updated_cb (TpConnection *connection,
296                                       guint         handle,
297                                       const gchar  *new_token,
298                                       gpointer      user_data,
299                                       GObject      *tp_factory)
300 {
301         GArray *handles;
302
303         if (tp_contact_factory_avatar_maybe_update (EMPATHY_TP_CONTACT_FACTORY (tp_factory),
304                                                     handle, new_token)) {
305                 /* Avatar was cached, nothing to do */
306                 return;
307         }
308
309         DEBUG ("Need to request avatar for token %s", new_token);
310
311         handles = g_array_new (FALSE, FALSE, sizeof (guint));
312         g_array_append_val (handles, handle);
313
314         tp_cli_connection_interface_avatars_call_request_avatars (connection,
315                                                                   -1,
316                                                                   handles,
317                                                                   tp_contact_factory_request_avatars_cb,
318                                                                   NULL, NULL,
319                                                                   tp_factory);
320         g_array_free (handles, TRUE);
321 }
322
323 static void
324 tp_contact_factory_update_capabilities (EmpathyTpContactFactory *tp_factory,
325                                         guint                    handle,
326                                         const gchar             *channel_type,
327                                         guint                    generic,
328                                         guint                    specific)
329 {
330         EmpathyContact      *contact;
331         EmpathyCapabilities  capabilities;
332
333         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
334         if (!contact) {
335                 return;
336         }
337
338         capabilities = empathy_contact_get_capabilities (contact);
339         capabilities &= ~EMPATHY_CAPABILITIES_UNKNOWN;
340
341         if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) == 0) {
342                 capabilities &= ~EMPATHY_CAPABILITIES_AUDIO;
343                 capabilities &= ~EMPATHY_CAPABILITIES_VIDEO;
344                 if (specific & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) {
345                         capabilities |= EMPATHY_CAPABILITIES_AUDIO;
346                 }
347                 if (specific & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) {
348                         capabilities |= EMPATHY_CAPABILITIES_VIDEO;
349                 }
350         }
351
352         DEBUG ("Changing capabilities for contact %s (%d) to %d",
353                 empathy_contact_get_id (contact),
354                 empathy_contact_get_handle (contact),
355                 capabilities);
356
357         empathy_contact_set_capabilities (contact, capabilities);
358 }
359
360 static void
361 tp_contact_factory_got_capabilities (EmpathyTpContactFactory *tp_factory,
362                                      GPtrArray *capabilities,
363                                      const GError    *error)
364 {
365         guint i;
366
367         if (error) {
368                 DEBUG ("Error: %s", error->message);
369                 /* FIXME Should set the capabilities of the contacts for which this request
370                  * originated to NONE */
371                 return;
372         }
373
374         for (i = 0; i < capabilities->len; i++) {
375                 GValueArray *values;
376                 guint        handle;
377                 const gchar *channel_type;
378                 guint        generic;
379                 guint        specific;
380
381                 values = g_ptr_array_index (capabilities, i);
382                 handle = g_value_get_uint (g_value_array_get_nth (values, 0));
383                 channel_type = g_value_get_string (g_value_array_get_nth (values, 1));
384                 generic = g_value_get_uint (g_value_array_get_nth (values, 2));
385                 specific = g_value_get_uint (g_value_array_get_nth (values, 3));
386
387                 tp_contact_factory_update_capabilities (tp_factory,
388                                                         handle,
389                                                         channel_type,
390                                                         generic,
391                                                         specific);
392
393                 g_value_array_free (values);
394         }
395
396         g_ptr_array_free (capabilities, TRUE);
397 }
398
399 #if HAVE_GEOCLUE
400 #define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo"
401 #define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
402
403 /* This callback is called by geoclue when it found a position
404  * for the given address.  A position is necessary for a contact
405  * to show up on the map
406  */
407 static void
408 geocode_cb (GeoclueGeocode *geocode,
409             GeocluePositionFields fields,
410             double latitude,
411             double longitude,
412             double altitude,
413             GeoclueAccuracy *accuracy,
414             GError *error,
415             gpointer contact)
416 {
417         GValue *new_value;
418         GHashTable *location;
419
420         location = empathy_contact_get_location (EMPATHY_CONTACT (contact));
421
422         if (error != NULL) {
423                 DEBUG ("Error geocoding location : %s", error->message);
424                 g_object_unref (geocode);
425                 g_object_unref (contact);
426                 return;
427         }
428
429         if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE) {
430                 new_value = tp_g_value_slice_new_double (latitude);
431                 g_hash_table_replace (location, g_strdup (EMPATHY_LOCATION_LAT),
432                         new_value);
433                 DEBUG ("\t - Latitude: %f", latitude);
434         }
435         if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
436                 new_value = tp_g_value_slice_new_double (longitude);
437                 g_hash_table_replace (location, g_strdup (EMPATHY_LOCATION_LON),
438                         new_value);
439                 DEBUG ("\t - Longitude: %f", longitude);
440         }
441         if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) {
442                 new_value = tp_g_value_slice_new_double (altitude);
443                 g_hash_table_replace (location, g_strdup (EMPATHY_LOCATION_ALT),
444                         new_value);
445                 DEBUG ("\t - Altitude: %f", altitude);
446         }
447
448         /* Don't change the accuracy as we used an address to get this position */
449         g_object_notify (contact, "location");
450         g_object_unref (geocode);
451         g_object_unref (contact);
452 }
453 #endif
454
455 #if HAVE_GEOCLUE
456 static gchar *
457 get_dup_string (GHashTable *location,
458     gchar *key)
459 {
460   GValue *value;
461
462   value = g_hash_table_lookup (location, key);
463   if (value != NULL)
464     return g_value_dup_string (value);
465
466   return NULL;
467 }
468 #endif
469
470 static void
471 tp_contact_factory_geocode (EmpathyContact *contact)
472 {
473 #if HAVE_GEOCLUE
474         static GeoclueGeocode *geocode;
475         gchar *str;
476         GHashTable *address;
477         GValue* value;
478         GHashTable *location;
479
480         location = empathy_contact_get_location (contact);
481         if (location == NULL)
482                 return;
483
484         value = g_hash_table_lookup (location, EMPATHY_LOCATION_LAT);
485         if (value != NULL)
486                 return;
487
488         if (geocode == NULL) {
489                 geocode = geoclue_geocode_new (GEOCODE_SERVICE, GEOCODE_PATH);
490                 g_object_add_weak_pointer (G_OBJECT (geocode), (gpointer *) &geocode);
491         }
492         else
493                 g_object_ref (geocode);
494
495         address = geoclue_address_details_new ();
496
497         str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY);
498         if (str != NULL)
499                 g_hash_table_insert (address, g_strdup ("country"), str);
500
501         str = get_dup_string (location, EMPATHY_LOCATION_POSTAL_CODE);
502         if (str != NULL)
503                 g_hash_table_insert (address, g_strdup ("postalcode"), str);
504
505         str = get_dup_string (location, EMPATHY_LOCATION_LOCALITY);
506         if (str != NULL)
507                 g_hash_table_insert (address, g_strdup ("locality"), str);
508
509         str = get_dup_string (location, EMPATHY_LOCATION_STREET);
510         if (str != NULL)
511                 g_hash_table_insert (address, g_strdup ("street"), str);
512
513         g_object_ref (contact);
514         geoclue_geocode_address_to_position_async (geocode, address,
515                 geocode_cb, contact);
516
517         g_hash_table_unref (address);
518 #endif
519 }
520
521 static void
522 tp_contact_factory_update_location (EmpathyTpContactFactory *tp_factory,
523                                     guint handle,
524                                     GHashTable *location)
525 {
526         EmpathyContact *contact;
527         GHashTable     *new_location;
528
529         contact = tp_contact_factory_find_by_handle (tp_factory, handle);
530
531         if (contact == NULL)
532                 return;
533
534         new_location = g_hash_table_new_full (g_str_hash, g_str_equal,
535                 (GDestroyNotify) g_free, (GDestroyNotify) tp_g_value_slice_free);
536         tp_g_hash_table_update (new_location, location, (GBoxedCopyFunc) g_strdup,
537                 (GBoxedCopyFunc) tp_g_value_slice_dup);
538         empathy_contact_set_location (contact, new_location);
539         g_hash_table_unref (new_location);
540
541         tp_contact_factory_geocode (contact);
542 }
543
544 static void
545 tp_contact_factory_got_locations (TpProxy                 *tp_proxy,
546                                   GHashTable              *locations,
547                                   const GError            *error,
548                                   gpointer                 user_data,
549                                   GObject                 *weak_object)
550 {
551         GHashTableIter iter;
552         gpointer key, value;
553         EmpathyTpContactFactory *tp_factory;
554
555         tp_factory = EMPATHY_TP_CONTACT_FACTORY (user_data);
556         if (error != NULL) {
557                 DEBUG ("Error: %s", error->message);
558                 return;
559         }
560
561         g_hash_table_iter_init (&iter, locations);
562         while (g_hash_table_iter_next (&iter, &key, &value)) {
563                 guint           handle = GPOINTER_TO_INT (key);
564                 GHashTable     *location = value;
565
566                 tp_contact_factory_update_location (tp_factory, handle, location);
567         }
568 }
569
570 static void
571 tp_contact_factory_capabilities_changed_cb (TpConnection    *connection,
572                                             const GPtrArray *capabilities,
573                                             gpointer         user_data,
574                                             GObject         *weak_object)
575 {
576         EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object);
577         guint                    i;
578
579         for (i = 0; i < capabilities->len; i++) {
580                 GValueArray *values;
581                 guint        handle;
582                 const gchar *channel_type;
583                 guint        generic;
584                 guint        specific;
585
586                 values = g_ptr_array_index (capabilities, i);
587                 handle = g_value_get_uint (g_value_array_get_nth (values, 0));
588                 channel_type = g_value_get_string (g_value_array_get_nth (values, 1));
589                 generic = g_value_get_uint (g_value_array_get_nth (values, 3));
590                 specific = g_value_get_uint (g_value_array_get_nth (values, 5));
591
592                 tp_contact_factory_update_capabilities (tp_factory,
593                                                         handle,
594                                                         channel_type,
595                                                         generic,
596                                                         specific);
597         }
598 }
599
600 static void
601 tp_contact_factory_location_updated_cb (TpProxy      *proxy,
602                                         guint         handle,
603                                         GHashTable   *location,
604                                         gpointer      user_data,
605                                         GObject      *weak_object)
606 {
607         EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object);
608         tp_contact_factory_update_location (tp_factory, handle, location);
609 }
610
611 static void
612 get_requestable_channel_classes_cb (TpProxy *connection,
613                                     const GValue *value,
614                                     const GError *error,
615                                     gpointer user_data,
616                                     GObject *weak_object)
617 {
618         EmpathyTpContactFactory     *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
619         EmpathyTpContactFactoryPriv *priv = GET_PRIV (self);
620         GPtrArray                   *classes;
621         guint                        i;
622         GList                       *l;
623
624         if (error != NULL) {
625                 DEBUG ("Error: %s", error->message);
626                 return;
627         }
628
629         classes = g_value_get_boxed (value);
630         for (i = 0; i < classes->len; i++) {
631                 GValueArray *class_struct;
632                 GHashTable *fixed_prop;
633                 GValue *chan_type, *handle_type;
634
635                 class_struct = g_ptr_array_index (classes, i);
636                 fixed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 0));
637
638                 handle_type = g_hash_table_lookup (fixed_prop,
639                         TP_IFACE_CHANNEL ".TargetHandleType");
640                 if (handle_type == NULL ||
641                     g_value_get_uint (handle_type) != TP_HANDLE_TYPE_CONTACT)
642                         continue;
643
644                 chan_type = g_hash_table_lookup (fixed_prop,
645                         TP_IFACE_CHANNEL ".ChannelType");
646                 if (chan_type == NULL)
647                         continue;
648
649                 if (!tp_strdiff (g_value_get_string (chan_type),
650                     TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
651                         priv->can_request_ft = TRUE;
652                 else if (!tp_strdiff (g_value_get_string (chan_type),
653                          TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
654                         priv->can_request_st = TRUE;
655         }
656
657         if (!priv->can_request_ft && !priv->can_request_st)
658                 return ;
659
660         /* Update the capabilities of all contacts */
661         for (l = priv->contacts; l != NULL; l = g_list_next (l)) {
662                 EmpathyContact *contact = l->data;
663                 EmpathyCapabilities caps;
664
665                 caps = empathy_contact_get_capabilities (contact);
666
667                 if (priv->can_request_ft)
668                         caps |= EMPATHY_CAPABILITIES_FT;
669
670                 if (priv->can_request_st)
671                         caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;
672
673                 empathy_contact_set_capabilities (contact, caps);
674         }
675 }
676
677 static void
678 tp_contact_factory_got_avatar_requirements_cb (TpConnection *proxy,
679                                                const gchar **mime_types,
680                                                guint         min_width,
681                                                guint         min_height,
682                                                guint         max_width,
683                                                guint         max_height,
684                                                guint         max_size,
685                                                const GError *error,
686                                                gpointer      user_data,
687                                                GObject      *tp_factory)
688 {
689         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
690
691         if (error) {
692                 DEBUG ("Failed to get avatar requirements: %s", error->message);
693                 /* We'll just leave avatar_mime_types as NULL; the
694                  * avatar-setting code can use this as a signal that you can't
695                  * set avatars.
696                  */
697         } else {
698                 priv->avatar_mime_types = g_strdupv ((gchar **) mime_types);
699                 priv->avatar_min_width = min_width;
700                 priv->avatar_min_height = min_height;
701                 priv->avatar_max_width = max_width;
702                 priv->avatar_max_height = max_height;
703                 priv->avatar_max_size = max_size;
704         }
705 }
706
707 static void
708 tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
709                                 EmpathyContact          *contact)
710 {
711         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
712         TpHandle self_handle;
713         TpHandle handle;
714         GArray handles = {(gchar *) &handle, 1};
715         GHashTable *tokens;
716         GPtrArray *capabilities;
717         GError *error = NULL;
718         EmpathyCapabilities caps;
719
720         /* Keep a weak ref to that contact */
721         g_object_weak_ref (G_OBJECT (contact),
722                            tp_contact_factory_weak_notify,
723                            tp_factory);
724         priv->contacts = g_list_prepend (priv->contacts, contact);
725
726         /* The contact keeps a ref to its factory */
727         g_object_set_data_full (G_OBJECT (contact), "empathy-factory",
728                                 g_object_ref (tp_factory),
729                                 g_object_unref);
730
731         caps = empathy_contact_get_capabilities (contact);
732
733         /* Set the FT capability */
734         if (priv->can_request_ft) {
735                 caps |= EMPATHY_CAPABILITIES_FT;
736         }
737
738         /* Set the Stream Tube capability */
739         if (priv->can_request_st) {
740                 caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;
741         }
742
743         empathy_contact_set_capabilities (contact, caps);
744
745         /* Set is-user property. Note that it could still be the handle is
746          * different from the connection's self handle, in the case the handle
747          * comes from a group interface. */
748         self_handle = tp_connection_get_self_handle (priv->connection);
749         handle = empathy_contact_get_handle (contact);
750         empathy_contact_set_is_user (contact, self_handle == handle);
751
752         /* FIXME: This should be done by TpContact */
753         tp_cli_connection_interface_avatars_run_get_known_avatar_tokens (priv->connection,
754                                                                          -1,
755                                                                          &handles,
756                                                                          &tokens,
757                                                                          &error,
758                                                                          NULL);
759         tp_contact_factory_got_known_avatar_tokens (tp_factory, tokens, error);
760         g_clear_error (&error);
761
762         tp_cli_connection_interface_capabilities_run_get_capabilities (priv->connection,
763                                                                         -1,
764                                                                         &handles,
765                                                                         &capabilities,
766                                                                         &error,
767                                                                         NULL);
768         tp_contact_factory_got_capabilities (tp_factory, capabilities, error);
769         g_clear_error (&error);
770
771         if (tp_proxy_has_interface_by_id (TP_PROXY (priv->connection),
772                 EMP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION)) {
773                 emp_cli_connection_interface_location_call_get_locations (TP_PROXY (priv->connection),
774                                                                          -1,
775                                                                          &handles,
776                                                                          tp_contact_factory_got_locations,
777                                                                          tp_factory,
778                                                                          NULL,
779                                                                          NULL);
780         }
781
782         DEBUG ("Contact added: %s (%d)",
783                 empathy_contact_get_id (contact),
784                 empathy_contact_get_handle (contact));
785 }
786
787 typedef union {
788         EmpathyTpContactFactoryContactsByIdCb ids_cb;
789         EmpathyTpContactFactoryContactsByHandleCb handles_cb;
790         EmpathyTpContactFactoryContactCb contact_cb;
791 } GetContactsCb;
792
793 typedef struct {
794         EmpathyTpContactFactory *tp_factory;
795         GetContactsCb callback;
796         gpointer user_data;
797         GDestroyNotify destroy;
798 } GetContactsData;
799
800 static void
801 get_contacts_data_free (gpointer user_data)
802 {
803         GetContactsData *data = user_data;
804
805         if (data->destroy) {
806                 data->destroy (data->user_data);
807         }
808         g_object_unref (data->tp_factory);
809
810         g_slice_free (GetContactsData, data);
811 }
812
813 static EmpathyContact *
814 dup_contact_for_tp_contact (EmpathyTpContactFactory *tp_factory,
815                             TpContact               *tp_contact)
816 {
817         EmpathyContact *contact;
818
819         contact = tp_contact_factory_find_by_tp_contact (tp_factory,
820                                                          tp_contact);
821
822         if (contact != NULL) {
823                 g_object_ref (contact);
824         } else {
825                 contact = empathy_contact_new (tp_contact);
826                 tp_contact_factory_add_contact (tp_factory, contact);
827         }
828
829         return contact;
830 }
831
832 static EmpathyContact **
833 contacts_array_new (EmpathyTpContactFactory *tp_factory,
834                     guint                    n_contacts,
835                     TpContact * const *      contacts)
836 {
837         EmpathyContact **ret;
838         guint            i;
839
840         ret = g_new0 (EmpathyContact *, n_contacts);
841         for (i = 0; i < n_contacts; i++) {
842                 ret[i] = dup_contact_for_tp_contact (tp_factory, contacts[i]);
843         }
844
845         return ret;
846 }
847
848 static void
849 contacts_array_free (guint            n_contacts,
850                      EmpathyContact **contacts)
851 {
852         guint i;
853
854         for (i = 0; i < n_contacts; i++) {
855                 g_object_unref (contacts[i]);
856         }
857         g_free (contacts);
858 }
859
860 static void
861 get_contacts_by_id_cb (TpConnection *connection,
862                        guint n_contacts,
863                        TpContact * const *contacts,
864                        const gchar * const *requested_ids,
865                        GHashTable *failed_id_errors,
866                        const GError *error,
867                        gpointer user_data,
868                        GObject *weak_object)
869 {
870         GetContactsData *data = user_data;
871         EmpathyContact **empathy_contacts;
872
873         empathy_contacts = contacts_array_new (data->tp_factory,
874                                                n_contacts, contacts);
875         if (data->callback.ids_cb) {
876                 data->callback.ids_cb (data->tp_factory,
877                                        n_contacts, empathy_contacts,
878                                        requested_ids,
879                                        failed_id_errors,
880                                        error,
881                                        data->user_data, weak_object);
882         }
883
884         contacts_array_free (n_contacts, empathy_contacts);
885 }
886
887 void
888 empathy_tp_contact_factory_get_from_ids (EmpathyTpContactFactory *tp_factory,
889                                          guint                    n_ids,
890                                          const gchar * const     *ids,
891                                          EmpathyTpContactFactoryContactsByIdCb callback,
892                                          gpointer                 user_data,
893                                          GDestroyNotify           destroy,
894                                          GObject                 *weak_object)
895 {
896         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
897         GetContactsData *data;
898
899         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
900         g_return_if_fail (ids != NULL);
901
902         data = g_slice_new (GetContactsData);
903         data->callback.ids_cb = callback;
904         data->user_data = user_data;
905         data->destroy = destroy;
906         data->tp_factory = g_object_ref (tp_factory);
907         tp_connection_get_contacts_by_id (priv->connection,
908                                           n_ids, ids,
909                                           G_N_ELEMENTS (contact_features),
910                                           contact_features,
911                                           get_contacts_by_id_cb,
912                                           data,
913                                           (GDestroyNotify) get_contacts_data_free,
914                                           weak_object);
915 }
916
917 static void
918 get_contact_by_id_cb (TpConnection *connection,
919                       guint n_contacts,
920                       TpContact * const *contacts,
921                       const gchar * const *requested_ids,
922                       GHashTable *failed_id_errors,
923                       const GError *error,
924                       gpointer user_data,
925                       GObject *weak_object)
926 {
927         GetContactsData *data = user_data;
928         EmpathyContact  *contact = NULL;
929
930         if (n_contacts == 1) {
931                 contact = dup_contact_for_tp_contact (data->tp_factory,
932                                                       contacts[0]);
933         }
934         else if (error == NULL) {
935                 GHashTableIter iter;
936                 gpointer       value;
937
938                 g_hash_table_iter_init (&iter, failed_id_errors);
939                 while (g_hash_table_iter_next (&iter, NULL, &value)) {
940                         if (value) {
941                                 error = value;
942                                 break;
943                         }
944                 }
945         }
946
947         if (data->callback.contact_cb) {
948                 data->callback.contact_cb (data->tp_factory,
949                                            contact,
950                                            error,
951                                            data->user_data, weak_object);
952         }
953 }
954
955 void
956 empathy_tp_contact_factory_get_from_id (EmpathyTpContactFactory *tp_factory,
957                                         const gchar             *id,
958                                         EmpathyTpContactFactoryContactCb callback,
959                                         gpointer                 user_data,
960                                         GDestroyNotify           destroy,
961                                         GObject                 *weak_object)
962 {
963         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
964         GetContactsData *data;
965
966         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
967         g_return_if_fail (id != NULL);
968
969         data = g_slice_new (GetContactsData);
970         data->callback.contact_cb = callback;
971         data->user_data = user_data;
972         data->destroy = destroy;
973         data->tp_factory = g_object_ref (tp_factory);
974         tp_connection_get_contacts_by_id (priv->connection,
975                                           1, &id,
976                                           G_N_ELEMENTS (contact_features),
977                                           contact_features,
978                                           get_contact_by_id_cb,
979                                           data,
980                                           (GDestroyNotify) get_contacts_data_free,
981                                           weak_object);
982 }
983
984 static void
985 get_contacts_by_handle_cb (TpConnection *connection,
986                            guint n_contacts,
987                            TpContact * const *contacts,
988                            guint n_failed,
989                            const TpHandle *failed,
990                            const GError *error,
991                            gpointer user_data,
992                            GObject *weak_object)
993 {
994         GetContactsData *data = user_data;
995         EmpathyContact **empathy_contacts;
996
997         empathy_contacts = contacts_array_new (data->tp_factory,
998                                                n_contacts, contacts);
999         if (data->callback.handles_cb) {
1000                 data->callback.handles_cb (data->tp_factory,
1001                                            n_contacts, empathy_contacts,
1002                                            n_failed, failed,
1003                                            error,
1004                                            data->user_data, weak_object);
1005         }
1006
1007         contacts_array_free (n_contacts, empathy_contacts);
1008 }
1009
1010 void
1011 empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory,
1012                                              guint n_handles,
1013                                              const TpHandle *handles,
1014                                              EmpathyTpContactFactoryContactsByHandleCb callback,
1015                                              gpointer                 user_data,
1016                                              GDestroyNotify           destroy,
1017                                              GObject                 *weak_object)
1018 {
1019         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1020         GetContactsData *data;
1021
1022         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1023         g_return_if_fail (handles != NULL);
1024
1025         data = g_slice_new (GetContactsData);
1026         data->callback.handles_cb = callback;
1027         data->user_data = user_data;
1028         data->destroy = destroy;
1029         data->tp_factory = g_object_ref (tp_factory);
1030         tp_connection_get_contacts_by_handle (priv->connection,
1031                                               n_handles, handles,
1032                                               G_N_ELEMENTS (contact_features),
1033                                               contact_features,
1034                                               get_contacts_by_handle_cb,
1035                                               data,
1036                                               (GDestroyNotify) get_contacts_data_free,
1037                                               weak_object);
1038 }
1039
1040 static void
1041 get_contact_by_handle_cb (TpConnection *connection,
1042                           guint n_contacts,
1043                           TpContact * const *contacts,
1044                           guint n_failed,
1045                           const TpHandle *failed,
1046                           const GError *error,
1047                           gpointer user_data,
1048                           GObject *weak_object)
1049 {
1050         GetContactsData *data = user_data;
1051         EmpathyContact  *contact = NULL;
1052
1053         if (n_contacts == 1) {
1054                 contact = dup_contact_for_tp_contact (data->tp_factory,
1055                                                       contacts[0]);
1056         }
1057
1058         if (data->callback.contact_cb) {
1059                 data->callback.contact_cb (data->tp_factory,
1060                                            contact,
1061                                            error,
1062                                            data->user_data, weak_object);
1063         }
1064 }
1065
1066 void
1067 empathy_tp_contact_factory_get_from_handle (EmpathyTpContactFactory *tp_factory,
1068                                             TpHandle                 handle,
1069                                             EmpathyTpContactFactoryContactCb callback,
1070                                             gpointer                 user_data,
1071                                             GDestroyNotify           destroy,
1072                                             GObject                 *weak_object)
1073 {
1074         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1075         GetContactsData *data;
1076
1077         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1078
1079         data = g_slice_new (GetContactsData);
1080         data->callback.contact_cb = callback;
1081         data->user_data = user_data;
1082         data->destroy = destroy;
1083         data->tp_factory = g_object_ref (tp_factory);
1084         tp_connection_get_contacts_by_handle (priv->connection,
1085                                               1, &handle,
1086                                               G_N_ELEMENTS (contact_features),
1087                                               contact_features,
1088                                               get_contact_by_handle_cb,
1089                                               data,
1090                                               (GDestroyNotify) get_contacts_data_free,
1091                                               weak_object);
1092 }
1093
1094 void
1095 empathy_tp_contact_factory_set_alias (EmpathyTpContactFactory *tp_factory,
1096                                       EmpathyContact          *contact,
1097                                       const gchar             *alias)
1098 {
1099         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1100         GHashTable                  *new_alias;
1101         guint                        handle;
1102
1103         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1104         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1105
1106         handle = empathy_contact_get_handle (contact);
1107
1108         DEBUG ("Setting alias for contact %s (%d) to %s",
1109                 empathy_contact_get_id (contact),
1110                 handle, alias);
1111
1112         new_alias = g_hash_table_new_full (g_direct_hash,
1113                                            g_direct_equal,
1114                                            NULL,
1115                                            g_free);
1116
1117         g_hash_table_insert (new_alias,
1118                              GUINT_TO_POINTER (handle),
1119                              g_strdup (alias));
1120
1121         tp_cli_connection_interface_aliasing_call_set_aliases (priv->connection,
1122                                                                -1,
1123                                                                new_alias,
1124                                                                tp_contact_factory_set_aliases_cb,
1125                                                                NULL, NULL,
1126                                                                G_OBJECT (tp_factory));
1127
1128         g_hash_table_destroy (new_alias);
1129 }
1130
1131 void
1132 empathy_tp_contact_factory_set_avatar (EmpathyTpContactFactory *tp_factory,
1133                                        const gchar             *data,
1134                                        gsize                    size,
1135                                        const gchar             *mime_type)
1136 {
1137         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1138
1139         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1140
1141         if (data && size > 0 && size < G_MAXUINT) {
1142                 GArray avatar;
1143
1144                 avatar.data = (gchar *) data;
1145                 avatar.len = size;
1146
1147                 DEBUG ("Setting avatar on connection %s",
1148                         tp_proxy_get_object_path (TP_PROXY (priv->connection)));
1149
1150                 tp_cli_connection_interface_avatars_call_set_avatar (priv->connection,
1151                                                                      -1,
1152                                                                      &avatar,
1153                                                                      mime_type,
1154                                                                      tp_contact_factory_set_avatar_cb,
1155                                                                      NULL, NULL,
1156                                                                      G_OBJECT (tp_factory));
1157         } else {
1158                 DEBUG ("Clearing avatar on connection %s",
1159                         tp_proxy_get_object_path (TP_PROXY (priv->connection)));
1160
1161                 tp_cli_connection_interface_avatars_call_clear_avatar (priv->connection,
1162                                                                        -1,
1163                                                                        tp_contact_factory_clear_avatar_cb,
1164                                                                        NULL, NULL,
1165                                                                        G_OBJECT (tp_factory));
1166         }
1167 }
1168
1169 void
1170 empathy_tp_contact_factory_set_location (EmpathyTpContactFactory *tp_factory,
1171                                          GHashTable              *location)
1172 {
1173         EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
1174
1175         g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
1176
1177         DEBUG ("Setting location");
1178
1179         emp_cli_connection_interface_location_call_set_location (TP_PROXY (priv->connection),
1180                                                                  -1,
1181                                                                  location,
1182                                                                  tp_contact_factory_set_location_cb,
1183                                                                  NULL, NULL,
1184                                                                  G_OBJECT (tp_factory));
1185 }
1186
1187 static void
1188 tp_contact_factory_get_property (GObject    *object,
1189                                  guint       param_id,
1190                                  GValue     *value,
1191                                  GParamSpec *pspec)
1192 {
1193         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1194
1195         switch (param_id) {
1196         case PROP_CONNECTION:
1197                 g_value_set_object (value, priv->connection);
1198                 break;
1199         case PROP_MIME_TYPES:
1200                 g_value_set_boxed (value, priv->avatar_mime_types);
1201                 break;
1202         case PROP_MIN_WIDTH:
1203                 g_value_set_uint (value, priv->avatar_min_width);
1204                 break;
1205         case PROP_MIN_HEIGHT:
1206                 g_value_set_uint (value, priv->avatar_min_height);
1207                 break;
1208         case PROP_MAX_WIDTH:
1209                 g_value_set_uint (value, priv->avatar_max_width);
1210                 break;
1211         case PROP_MAX_HEIGHT:
1212                 g_value_set_uint (value, priv->avatar_max_height);
1213                 break;
1214         case PROP_MAX_SIZE:
1215                 g_value_set_uint (value, priv->avatar_max_size);
1216                 break;
1217         default:
1218                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1219                 break;
1220         };
1221 }
1222
1223 static void
1224 tp_contact_factory_set_property (GObject      *object,
1225                                  guint         param_id,
1226                                  const GValue *value,
1227                                  GParamSpec   *pspec)
1228 {
1229         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1230
1231         switch (param_id) {
1232         case PROP_CONNECTION:
1233                 priv->connection = g_value_dup_object (value);
1234                 break;
1235         default:
1236                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1237                 break;
1238         };
1239 }
1240
1241 static void
1242 tp_contact_factory_finalize (GObject *object)
1243 {
1244         EmpathyTpContactFactoryPriv *priv = GET_PRIV (object);
1245         GList                       *l;
1246
1247         DEBUG ("Finalized: %p", object);
1248
1249         for (l = priv->contacts; l; l = l->next) {
1250                 g_object_weak_unref (G_OBJECT (l->data),
1251                                      tp_contact_factory_weak_notify,
1252                                      object);
1253         }
1254
1255         g_list_free (priv->contacts);
1256
1257         g_object_unref (priv->connection);
1258
1259         g_strfreev (priv->avatar_mime_types);
1260
1261         G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->finalize (object);
1262 }
1263
1264 static GObject *
1265 tp_contact_factory_constructor (GType                  type,
1266                                 guint                  n_props,
1267                                 GObjectConstructParam *props)
1268 {
1269         GObject *tp_factory;
1270         EmpathyTpContactFactoryPriv *priv;
1271
1272         tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
1273         priv = GET_PRIV (tp_factory);
1274
1275         /* FIXME: This should be moved to TpContact */
1276         tp_cli_connection_interface_avatars_connect_to_avatar_updated (priv->connection,
1277                                                                        tp_contact_factory_avatar_updated_cb,
1278                                                                        NULL, NULL,
1279                                                                        tp_factory,
1280                                                                        NULL);
1281         tp_cli_connection_interface_avatars_connect_to_avatar_retrieved (priv->connection,
1282                                                                          tp_contact_factory_avatar_retrieved_cb,
1283                                                                          NULL, NULL,
1284                                                                          tp_factory,
1285                                                                          NULL);
1286         tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection,
1287                                                                                   tp_contact_factory_capabilities_changed_cb,
1288                                                                                   NULL, NULL,
1289                                                                                   tp_factory,
1290                                                                                   NULL);
1291
1292
1293         emp_cli_connection_interface_location_connect_to_location_updated (TP_PROXY (priv->connection),
1294                                                                            tp_contact_factory_location_updated_cb,
1295                                                                            NULL, NULL,
1296                                                                            G_OBJECT (tp_factory),
1297                                                                            NULL);
1298
1299         /* FIXME: This should be moved to TpConnection */
1300         tp_cli_connection_interface_avatars_call_get_avatar_requirements (priv->connection,
1301                                                                           -1,
1302                                                                           tp_contact_factory_got_avatar_requirements_cb,
1303                                                                           NULL, NULL,
1304                                                                           tp_factory);
1305         tp_cli_dbus_properties_call_get (priv->connection, -1,
1306                 TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
1307                 "RequestableChannelClasses",
1308                 get_requestable_channel_classes_cb, NULL, NULL,
1309                 G_OBJECT (tp_factory));
1310
1311         return tp_factory;
1312 }
1313
1314 static void
1315 empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass)
1316 {
1317         GObjectClass *object_class = G_OBJECT_CLASS (klass);
1318
1319         object_class->finalize = tp_contact_factory_finalize;
1320         object_class->constructor = tp_contact_factory_constructor;
1321         object_class->get_property = tp_contact_factory_get_property;
1322         object_class->set_property = tp_contact_factory_set_property;
1323
1324         g_object_class_install_property (object_class,
1325                                          PROP_CONNECTION,
1326                                          g_param_spec_object ("connection",
1327                                                               "Factory's Connection",
1328                                                               "The connection associated with the factory",
1329                                                               TP_TYPE_CONNECTION,
1330                                                               G_PARAM_READWRITE |
1331                                                               G_PARAM_CONSTRUCT_ONLY |
1332                                                               G_PARAM_STATIC_STRINGS));
1333         g_object_class_install_property (object_class,
1334                                          PROP_MIME_TYPES,
1335                                          g_param_spec_boxed ("avatar-mime-types",
1336                                                              "Supported MIME types for avatars",
1337                                                              "Types of images that may be set as "
1338                                                              "avatars on this connection.",
1339                                                              G_TYPE_STRV,
1340                                                              G_PARAM_READABLE |
1341                                                              G_PARAM_STATIC_STRINGS));
1342         g_object_class_install_property (object_class,
1343                                          PROP_MIN_WIDTH,
1344                                          g_param_spec_uint ("avatar-min-width",
1345                                                             "Minimum width for avatars",
1346                                                             "Minimum width of avatar that may be set.",
1347                                                             0,
1348                                                             G_MAXUINT,
1349                                                             0,
1350                                                             G_PARAM_READABLE |
1351                                                             G_PARAM_STATIC_STRINGS));
1352         g_object_class_install_property (object_class,
1353                                          PROP_MIN_HEIGHT,
1354                                          g_param_spec_uint ("avatar-min-height",
1355                                                             "Minimum height for avatars",
1356                                                             "Minimum height of avatar that may be set.",
1357                                                             0,
1358                                                             G_MAXUINT,
1359                                                             0,
1360                                                             G_PARAM_READABLE |
1361                                                             G_PARAM_STATIC_STRINGS));
1362         g_object_class_install_property (object_class,
1363                                          PROP_MAX_WIDTH,
1364                                          g_param_spec_uint ("avatar-max-width",
1365                                                             "Maximum width for avatars",
1366                                                             "Maximum width of avatar that may be set "
1367                                                             "or 0 if there is no maximum.",
1368                                                             0,
1369                                                             G_MAXUINT,
1370                                                             0,
1371                                                             G_PARAM_READABLE |
1372                                                             G_PARAM_STATIC_STRINGS));
1373         g_object_class_install_property (object_class,
1374                                          PROP_MAX_HEIGHT,
1375                                          g_param_spec_uint ("avatar-max-height",
1376                                                             "Maximum height for avatars",
1377                                                             "Maximum height of avatar that may be set "
1378                                                             "or 0 if there is no maximum.",
1379                                                             0,
1380                                                             G_MAXUINT,
1381                                                             0,
1382                                                             G_PARAM_READABLE |
1383                                                             G_PARAM_STATIC_STRINGS));
1384         g_object_class_install_property (object_class,
1385                                          PROP_MAX_SIZE,
1386                                          g_param_spec_uint ("avatar-max-size",
1387                                                             "Maximum size for avatars in bytes",
1388                                                             "Maximum file size of avatar that may be "
1389                                                             "set or 0 if there is no maximum.",
1390                                                             0,
1391                                                             G_MAXUINT,
1392                                                             0,
1393                                                             G_PARAM_READABLE |
1394                                                             G_PARAM_STATIC_STRINGS));
1395
1396
1397         g_type_class_add_private (object_class, sizeof (EmpathyTpContactFactoryPriv));
1398 }
1399
1400 static void
1401 empathy_tp_contact_factory_init (EmpathyTpContactFactory *tp_factory)
1402 {
1403         EmpathyTpContactFactoryPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (tp_factory,
1404                 EMPATHY_TYPE_TP_CONTACT_FACTORY, EmpathyTpContactFactoryPriv);
1405
1406         tp_factory->priv = priv;
1407         priv->can_request_ft = FALSE;
1408         priv->can_request_st = FALSE;
1409 }
1410
1411 static GHashTable *factories = NULL;
1412
1413 static void
1414 tp_contact_factory_connection_invalidated_cb (TpProxy *connection,
1415                                               guint    domain,
1416                                               gint     code,
1417                                               gchar   *message,
1418                                               gpointer user_data)
1419 {
1420         DEBUG ("Message: %s", message);
1421         g_hash_table_remove (factories, connection);
1422 }
1423
1424 static void
1425 tp_contact_factory_connection_weak_notify_cb (gpointer connection,
1426                                               GObject *where_the_object_was)
1427 {
1428         g_hash_table_remove (factories, connection);
1429 }
1430
1431 static void
1432 tp_contact_factory_remove_connection (gpointer connection)
1433 {
1434         g_signal_handlers_disconnect_by_func (connection,
1435                 tp_contact_factory_connection_invalidated_cb, NULL);
1436         g_object_unref (connection);
1437 }
1438
1439 EmpathyTpContactFactory *
1440 empathy_tp_contact_factory_dup_singleton (TpConnection *connection)
1441 {
1442         EmpathyTpContactFactory *tp_factory;
1443
1444         g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
1445
1446         if (factories == NULL) {
1447                 factories = g_hash_table_new_full (empathy_proxy_hash,
1448                                                    empathy_proxy_equal,
1449                                                    tp_contact_factory_remove_connection,
1450                                                    NULL);
1451         }
1452
1453         tp_factory = g_hash_table_lookup (factories, connection);
1454         if (tp_factory == NULL) {
1455                 tp_factory = g_object_new (EMPATHY_TYPE_TP_CONTACT_FACTORY,
1456                                            "connection", connection,
1457                                            NULL);
1458                 g_hash_table_insert (factories, g_object_ref (connection),
1459                                      tp_factory);
1460                 g_object_weak_ref (G_OBJECT (tp_factory),
1461                                    tp_contact_factory_connection_weak_notify_cb,
1462                                    connection);
1463                 g_signal_connect (connection, "invalidated",
1464                                   G_CALLBACK (tp_contact_factory_connection_invalidated_cb),
1465                                   NULL);
1466         } else {
1467                 g_object_ref (tp_factory);
1468         }
1469
1470         return tp_factory;
1471 }
1472