Some unit test cleanup and fixes
[qtcontacts-tracker:sirajs-contactsd-merge.git] / tests / lib / glib / contacts-conn.c
1 /*
2  * contacts-conn.c - connection with contact info
3  *
4  * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/>
5  * Copyright (C) 2007-2008 Nokia Corporation
6  *
7  * Copying and distribution of this file, with or without modification,
8  * are permitted in any medium without royalty provided the copyright
9  * notice and this notice are preserved.
10  */
11 #include "contacts-conn.h"
12 #include "contact-list.h"
13 #include "contact-list-manager.h"
14 #include "debug.h"
15
16 #include <dbus/dbus-glib.h>
17
18 #include <telepathy-glib/interfaces.h>
19 #include <telepathy-glib/dbus.h>
20 #include <telepathy-glib/dbus-properties-mixin.h>
21 #include <telepathy-glib/errors.h>
22 #include <telepathy-glib/gtypes.h>
23 #include <telepathy-glib/handle-repo-dynamic.h>
24 #include <telepathy-glib/handle-repo-static.h>
25 #include <telepathy-glib/util.h>
26
27 #include "debug.h"
28
29 static void init_aliasing (gpointer, gpointer);
30 static void init_avatars (gpointer, gpointer);
31 static void init_location (gpointer, gpointer);
32 static void init_contact_caps (gpointer, gpointer);
33 static void init_contact_info (gpointer, gpointer);
34 static void conn_avatars_properties_getter (GObject *object, GQuark interface,
35     GQuark name, GValue *value, gpointer getter_data);
36
37 G_DEFINE_TYPE_WITH_CODE (TpTestsContactsConnection,
38     tp_tests_contacts_connection,
39     TP_TESTS_TYPE_SIMPLE_CONNECTION,
40     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING,
41       init_aliasing);
42     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS,
43       init_avatars);
44     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
45       tp_contacts_mixin_iface_init);
46     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
47       tp_presence_mixin_iface_init);
48     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
49       tp_presence_mixin_simple_presence_iface_init)
50     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION,
51       init_location)
52     G_IMPLEMENT_INTERFACE (
53       TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
54       init_contact_caps)
55     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO,
56       init_contact_info)
57     );
58
59 /* type definition stuff */
60
61 static const char *mime_types[] = { "image/png", NULL };
62 static TpDBusPropertiesMixinPropImpl conn_avatars_properties[] = {
63       { "MinimumAvatarWidth", GUINT_TO_POINTER (1), NULL },
64       { "MinimumAvatarHeight", GUINT_TO_POINTER (2), NULL },
65       { "RecommendedAvatarWidth", GUINT_TO_POINTER (3), NULL },
66       { "RecommendedAvatarHeight", GUINT_TO_POINTER (4), NULL },
67       { "MaximumAvatarWidth", GUINT_TO_POINTER (5), NULL },
68       { "MaximumAvatarHeight", GUINT_TO_POINTER (6), NULL },
69       { "MaximumAvatarBytes", GUINT_TO_POINTER (7), NULL },
70       /* special-cased - it's the only one with a non-guint value */
71       { "SupportedAvatarMIMETypes", NULL, NULL },
72       { NULL }
73 };
74
75 enum
76 {
77   PROP_0,
78   PROP_SIMULATION_DELAY,
79   N_PROPS
80 };
81
82 enum
83 {
84   N_SIGNALS
85 };
86
87 struct _TpTestsContactsConnectionPrivate
88 {
89   /* TpHandle => gchar * */
90   GHashTable *aliases;
91   /* TpHandle => AvatarData */
92   GHashTable *avatars;
93   /* TpHandle => ContactsConnectionPresenceStatusIndex */
94   GHashTable *presence_statuses;
95   /* TpHandle => gchar * */
96   GHashTable *presence_messages;
97   /* TpHandle => GHashTable * */
98   GHashTable *locations;
99   /* TpHandle => GPtrArray * */
100   GHashTable *capabilities;
101   /* TpHandle => GPtrArray * */
102   GHashTable *contact_info;
103   GPtrArray *default_contact_info;
104
105   TestContactListManager *list_manager;
106   guint simulation_delay;
107 };
108
109 typedef struct
110 {
111   GArray *data;
112   gchar *mime_type;
113   gchar *token;
114 } AvatarData;
115
116 static AvatarData *
117 avatar_data_new (GArray *data,
118     const gchar *mime_type,
119     const gchar *token)
120 {
121   AvatarData *a;
122
123   a = g_slice_new (AvatarData);
124   a->data = data ? g_array_ref (data) : NULL;
125   a->mime_type = g_strdup (mime_type);
126   a->token = g_strdup (token);
127
128   return a;
129 }
130
131 static void
132 avatar_data_free (gpointer data)
133 {
134   AvatarData *a = data;
135
136   if (a != NULL)
137     {
138       if (a->data != NULL)
139         g_array_unref (a->data);
140       g_free (a->mime_type);
141       g_free (a->token);
142       g_slice_free (AvatarData, a);
143     }
144 }
145
146 static void
147 free_rcc_list (GPtrArray *rccs)
148 {
149   g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs);
150 }
151
152 static void
153 tp_tests_contacts_connection_init (TpTestsContactsConnection *self)
154 {
155   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_CONTACTS_CONNECTION,
156       TpTestsContactsConnectionPrivate);
157   self->priv->aliases = g_hash_table_new_full (g_direct_hash, g_direct_equal,
158       NULL, g_free);
159   self->priv->avatars = g_hash_table_new_full (g_direct_hash,
160       g_direct_equal, NULL, avatar_data_free);
161   self->priv->presence_statuses = g_hash_table_new_full (g_direct_hash,
162       g_direct_equal, NULL, NULL);
163   self->priv->presence_messages = g_hash_table_new_full (g_direct_hash,
164       g_direct_equal, NULL, g_free);
165   self->priv->locations = g_hash_table_new_full (g_direct_hash, g_direct_equal,
166       NULL, (GDestroyNotify) g_hash_table_unref);
167   self->priv->capabilities = g_hash_table_new_full (g_direct_hash,
168       g_direct_equal, NULL, (GDestroyNotify) free_rcc_list);
169   self->priv->contact_info = g_hash_table_new_full (g_direct_hash,
170       g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref);
171 }
172
173 static void
174 finalize (GObject *object)
175 {
176   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
177
178   tp_contacts_mixin_finalize (object);
179   g_hash_table_destroy (self->priv->aliases);
180   g_hash_table_destroy (self->priv->avatars);
181   g_hash_table_destroy (self->priv->presence_statuses);
182   g_hash_table_destroy (self->priv->presence_messages);
183   g_hash_table_destroy (self->priv->locations);
184   g_hash_table_destroy (self->priv->capabilities);
185   g_hash_table_destroy (self->priv->contact_info);
186
187   if (self->priv->default_contact_info != NULL)
188     g_ptr_array_unref (self->priv->default_contact_info);
189
190   G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->finalize (object);
191 }
192
193 static void
194 aliasing_fill_contact_attributes (GObject *object,
195                                   const GArray *contacts,
196                                   GHashTable *attributes)
197 {
198   guint i;
199   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
200   TpBaseConnection *base = TP_BASE_CONNECTION (object);
201   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
202       TP_HANDLE_TYPE_CONTACT);
203
204   for (i = 0; i < contacts->len; i++)
205     {
206       TpHandle handle = g_array_index (contacts, guint, i);
207       const gchar *alias = g_hash_table_lookup (self->priv->aliases,
208           GUINT_TO_POINTER (handle));
209
210       if (alias == NULL)
211         {
212           alias = tp_handle_inspect (contact_repo, handle);
213         }
214
215       tp_contacts_mixin_set_contact_attribute (attributes, handle,
216           TP_IFACE_CONNECTION_INTERFACE_ALIASING "/alias",
217           tp_g_value_slice_new_string (alias));
218     }
219 }
220
221 static void
222 avatars_fill_contact_attributes (GObject *object,
223                                  const GArray *contacts,
224                                  GHashTable *attributes)
225 {
226   guint i;
227   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
228
229   for (i = 0; i < contacts->len; i++)
230     {
231       TpHandle handle = g_array_index (contacts, guint, i);
232       AvatarData *a = g_hash_table_lookup (self->priv->avatars,
233           GUINT_TO_POINTER (handle));
234
235       if (a != NULL && a->token != NULL)
236         {
237           tp_contacts_mixin_set_contact_attribute (attributes, handle,
238               TP_IFACE_CONNECTION_INTERFACE_AVATARS "/token",
239               tp_g_value_slice_new_string (a->token));
240         }
241     }
242 }
243
244 static void
245 location_fill_contact_attributes (GObject *object,
246     const GArray *contacts,
247     GHashTable *attributes)
248 {
249   guint i;
250   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
251
252   for (i = 0; i < contacts->len; i++)
253     {
254       TpHandle handle = g_array_index (contacts, guint, i);
255       GHashTable *location = g_hash_table_lookup (self->priv->locations,
256           GUINT_TO_POINTER (handle));
257
258       if (location != NULL)
259         {
260           tp_contacts_mixin_set_contact_attribute (attributes, handle,
261               TP_IFACE_CONNECTION_INTERFACE_LOCATION "/location",
262               tp_g_value_slice_new_boxed (TP_HASH_TYPE_LOCATION, location));
263         }
264     }
265 }
266
267 static void
268 contact_caps_fill_contact_attributes (GObject *object,
269     const GArray *contacts,
270     GHashTable *attributes)
271 {
272   guint i;
273   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
274
275   for (i = 0; i < contacts->len; i++)
276     {
277       TpHandle handle = g_array_index (contacts, guint, i);
278       GPtrArray *caps = g_hash_table_lookup (self->priv->capabilities,
279           GUINT_TO_POINTER (handle));
280
281       if (caps != NULL)
282         {
283           tp_contacts_mixin_set_contact_attribute (attributes, handle,
284               TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES "/capabilities",
285               tp_g_value_slice_new_boxed (
286                 TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, caps));
287         }
288     }
289 }
290
291 static void
292 contact_info_fill_contact_attributes (GObject *object,
293     const GArray *contacts,
294     GHashTable *attributes)
295 {
296   guint i;
297   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
298
299   for (i = 0; i < contacts->len; i++)
300     {
301       TpHandle handle = g_array_index (contacts, guint, i);
302       GPtrArray *info = g_hash_table_lookup (self->priv->contact_info,
303           GUINT_TO_POINTER (handle));
304
305       if (info != NULL)
306         {
307           tp_contacts_mixin_set_contact_attribute (attributes, handle,
308               TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO "/info",
309               tp_g_value_slice_new_boxed (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST,
310                   info));
311         }
312     }
313 }
314
315 static TpDBusPropertiesMixinPropImpl conn_contact_info_properties[] = {
316       { "ContactInfoFlags", GUINT_TO_POINTER (TP_CONTACT_INFO_FLAG_PUSH |
317           TP_CONTACT_INFO_FLAG_CAN_SET), NULL },
318       { "SupportedFields", NULL, NULL },
319       { NULL }
320 };
321
322 static void
323 conn_contact_info_properties_getter (GObject *object,
324                                      GQuark interface,
325                                      GQuark name,
326                                      GValue *value,
327                                      gpointer getter_data)
328 {
329   GQuark q_supported_fields = g_quark_from_static_string ("SupportedFields");
330   static GPtrArray *supported_fields = NULL;
331
332   if (name == q_supported_fields)
333     {
334       if (supported_fields == NULL)
335         {
336           supported_fields = g_ptr_array_new ();
337           g_ptr_array_add (supported_fields, tp_value_array_build (4,
338               G_TYPE_STRING, "n",
339               G_TYPE_STRV, NULL,
340               G_TYPE_UINT, 0,
341               G_TYPE_UINT, 0,
342               G_TYPE_INVALID));
343         }
344       g_value_set_boxed (value, supported_fields);
345     }
346   else
347     {
348       g_value_set_uint (value, GPOINTER_TO_UINT (getter_data));
349     }
350 }
351
352 static void
353 get_property (GObject *object,
354               guint property_id,
355               GValue *value,
356               GParamSpec *spec)
357 {
358   TpTestsContactsConnection *self =
359       TP_TESTS_CONTACTS_CONNECTION (object);
360
361   switch (property_id)
362     {
363     case PROP_SIMULATION_DELAY:
364       g_value_set_uint (value, self->priv->simulation_delay);
365       break;
366
367     default:
368       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
369     }
370 }
371
372 static void
373 set_property (GObject *object,
374               guint property_id,
375               const GValue *value,
376               GParamSpec *spec)
377 {
378   TpTestsContactsConnection *self =
379       TP_TESTS_CONTACTS_CONNECTION (object);
380
381   switch (property_id)
382     {
383     case PROP_SIMULATION_DELAY:
384       self->priv->simulation_delay = g_value_get_uint (value);
385       break;
386
387     default:
388       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
389     }
390 }
391
392 static void
393 constructed (GObject *object)
394 {
395   TpBaseConnection *base = TP_BASE_CONNECTION (object);
396   void (*parent_impl) (GObject *) =
397     G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->constructed;
398
399   if (parent_impl != NULL)
400     parent_impl (object);
401
402   tp_contacts_mixin_init (object,
403       G_STRUCT_OFFSET (TpTestsContactsConnection, contacts_mixin));
404   tp_base_connection_register_with_contacts_mixin (base);
405   tp_contacts_mixin_add_contact_attributes_iface (object,
406       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
407       aliasing_fill_contact_attributes);
408   tp_contacts_mixin_add_contact_attributes_iface (object,
409       TP_IFACE_CONNECTION_INTERFACE_AVATARS,
410       avatars_fill_contact_attributes);
411   tp_contacts_mixin_add_contact_attributes_iface (object,
412       TP_IFACE_CONNECTION_INTERFACE_LOCATION,
413       location_fill_contact_attributes);
414   tp_contacts_mixin_add_contact_attributes_iface (object,
415       TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
416       contact_caps_fill_contact_attributes);
417   tp_contacts_mixin_add_contact_attributes_iface (object,
418       TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
419       contact_info_fill_contact_attributes);
420
421   tp_presence_mixin_init (object,
422       G_STRUCT_OFFSET (TpTestsContactsConnection, presence_mixin));
423   tp_presence_mixin_simple_presence_register_with_contacts_mixin (object);
424 }
425
426 static const TpPresenceStatusOptionalArgumentSpec can_have_message[] = {
427       { "message", "s", NULL, NULL },
428       { NULL }
429 };
430
431 /* Must match TpTestsContactsConnectionPresenceStatusIndex in the .h */
432 static const TpPresenceStatusSpec my_statuses[] = {
433       { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE,
434         can_have_message },
435       { "busy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE, can_have_message },
436       { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, can_have_message },
437       { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, NULL },
438       { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, NULL },
439       { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, NULL },
440       { NULL }
441 };
442
443 static gboolean
444 my_status_available (GObject *object,
445                      guint index)
446 {
447   TpBaseConnection *base = TP_BASE_CONNECTION (object);
448
449   if (base->status != TP_CONNECTION_STATUS_CONNECTED)
450     return FALSE;
451
452   return TRUE;
453 }
454
455 static GHashTable *
456 my_get_contact_statuses (GObject *object,
457                          const GArray *contacts,
458                          GError **error)
459 {
460   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
461   GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal,
462       NULL, (GDestroyNotify) tp_presence_status_free);
463   guint i;
464
465   for (i = 0; i < contacts->len; i++)
466     {
467       TpHandle handle = g_array_index (contacts, TpHandle, i);
468       gpointer key = GUINT_TO_POINTER (handle);
469       TpTestsContactsConnectionPresenceStatusIndex index;
470       const gchar *presence_message;
471       GHashTable *parameters;
472
473       if (!g_hash_table_lookup_extended (self->priv->presence_statuses,
474           key, NULL, &index))
475         {
476           index = TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN;
477         }
478
479       presence_message = g_hash_table_lookup (
480           self->priv->presence_messages, key);
481
482       parameters = g_hash_table_new_full (g_str_hash,
483           g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free);
484
485       if (presence_message != NULL)
486         g_hash_table_insert (parameters, "message",
487             tp_g_value_slice_new_string (presence_message));
488
489       g_hash_table_insert (result, key,
490           tp_presence_status_new (index, parameters));
491       g_hash_table_destroy (parameters);
492     }
493
494   return result;
495 }
496
497 static gboolean
498 my_set_own_status (GObject *object,
499                    const TpPresenceStatus *status,
500                    GError **error)
501 {
502   TpBaseConnection *base_conn = TP_BASE_CONNECTION (object);
503   TpTestsContactsConnectionPresenceStatusIndex index = status->index;
504   const gchar *message = "";
505
506   if (status->optional_arguments != NULL)
507     {
508       message = g_hash_table_lookup (status->optional_arguments, "message");
509
510       if (message == NULL)
511         message = "";
512     }
513
514   tp_tests_contacts_connection_change_presences (TP_TESTS_CONTACTS_CONNECTION (object),
515       1, &(base_conn->self_handle), &index, &message);
516
517   return TRUE;
518 }
519
520 static gchar *
521 normalize_contact (TpHandleRepoIface *repo,
522                    const gchar *id,
523                    gpointer context,
524                    GError **error)
525 {
526   if (id[0] == '\0')
527     {
528       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE,
529           "Contact ID must not be empty");
530       return NULL;
531     }
532
533   return g_utf8_normalize (id, -1, G_NORMALIZE_ALL_COMPOSE);
534 }
535
536 static gchar *
537 normalize_group (TpHandleRepoIface *repo,
538                  const gchar *id,
539                  gpointer context,
540                  GError **error)
541 {
542   if (id[0] == '\0')
543     {
544       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE,
545           "Contact group name cannot be empty");
546       return NULL;
547     }
548
549   return g_utf8_normalize (id, -1, G_NORMALIZE_ALL_COMPOSE);
550 }
551
552 static void
553 create_handle_repos (TpBaseConnection *conn,
554                      TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
555 {
556   repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new
557       (TP_HANDLE_TYPE_CONTACT, normalize_contact, NULL);
558
559   repos[TP_HANDLE_TYPE_LIST] = tp_static_handle_repo_new
560       (TP_HANDLE_TYPE_LIST, test_contact_lists ());
561
562   repos[TP_HANDLE_TYPE_GROUP] = tp_dynamic_handle_repo_new
563       (TP_HANDLE_TYPE_GROUP, normalize_group, NULL);
564 }
565
566 static void
567 alias_updated_cb (TestContactListManager *manager,
568                   TpHandle contact,
569                   TpTestsContactsConnection *self)
570 {
571   const gchar *alias;
572
573   alias = test_contact_list_manager_get_alias (manager, contact);
574   tp_tests_contacts_connection_change_aliases (self, 1, &contact, &alias);
575 }
576
577 static void
578 avatar_updated_cb (TestContactListManager *manager,
579                    TpHandle contact,
580                    TpTestsContactsConnection *self)
581 {
582   const TestContactListAvatarData *data;
583
584   data = test_contact_list_manager_get_avatar (manager, contact);
585   tp_tests_contacts_connection_change_avatar_data (self, contact, data->data,
586       data->mime_type, data->token);
587 }
588
589 static void
590 presence_updated_cb (TestContactListManager *manager,
591                      TpHandle contact,
592                      TpTestsContactsConnection *self)
593 {
594   TpBaseConnection *base = (TpBaseConnection *) self;
595   TpTestsContactsConnectionPresenceStatusIndex status;
596   const gchar *message = NULL;
597
598   /* we ignore the presence indicated by the contact list for our own handle */
599   if (contact == base->self_handle)
600     return;
601
602   switch(test_contact_list_manager_get_presence (manager, contact))
603     {
604     case TEST_CONTACT_LIST_PRESENCE_OFFLINE:
605       status = TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE;
606       break;
607     case TEST_CONTACT_LIST_PRESENCE_UNKNOWN:
608       status = TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN;
609       break;
610     case TEST_CONTACT_LIST_PRESENCE_ERROR:
611       status = TP_TESTS_CONTACTS_CONNECTION_STATUS_ERROR;
612       break;
613     case TEST_CONTACT_LIST_PRESENCE_AWAY:
614       status = TP_TESTS_CONTACTS_CONNECTION_STATUS_AWAY;
615       break;
616     case TEST_CONTACT_LIST_PRESENCE_AVAILABLE:
617       status = TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE;
618       break;
619     }
620
621   tp_tests_contacts_connection_change_presences (self, 1, &contact, &status, &message);
622 }
623
624 static GPtrArray *
625 create_channel_managers (TpBaseConnection *conn)
626 {
627   TpTestsContactsConnection *self =
628     TP_TESTS_CONTACTS_CONNECTION (conn);
629   GPtrArray *ret = g_ptr_array_sized_new (1);
630
631   self->priv->list_manager =
632     TEST_CONTACT_LIST_MANAGER (g_object_new (
633           TEST_TYPE_CONTACT_LIST_MANAGER,
634           "connection", conn,
635           "simulation-delay", self->priv->simulation_delay,
636           NULL));
637
638   g_signal_connect (self->priv->list_manager, "alias-updated",
639       G_CALLBACK (alias_updated_cb), self);
640   g_signal_connect (self->priv->list_manager, "avatar-updated",
641       G_CALLBACK (avatar_updated_cb), self);
642   g_signal_connect (self->priv->list_manager, "presence-updated",
643       G_CALLBACK (presence_updated_cb), self);
644
645   g_ptr_array_add (ret, self->priv->list_manager);
646
647   return ret;
648 }
649
650 static void
651 tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
652 {
653   TpBaseConnectionClass *base_class =
654       (TpBaseConnectionClass *) klass;
655   GObjectClass *object_class = (GObjectClass *) klass;
656   GParamSpec *param_spec;
657   static const gchar *interfaces_always_present[] = {
658       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
659       TP_IFACE_CONNECTION_INTERFACE_AVATARS,
660       TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
661       TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
662       TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
663       TP_IFACE_CONNECTION_INTERFACE_LOCATION,
664       TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
665       TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
666       TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
667       NULL };
668   static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
669         { TP_IFACE_CONNECTION_INTERFACE_AVATARS,
670           conn_avatars_properties_getter,
671           NULL,
672           conn_avatars_properties,
673         },
674         { TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
675           conn_contact_info_properties_getter,
676           NULL,
677           conn_contact_info_properties,
678         },
679         { NULL }
680   };
681
682   object_class->get_property = get_property;
683   object_class->set_property = set_property;
684   object_class->constructed = constructed;
685   object_class->finalize = finalize;
686   g_type_class_add_private (klass, sizeof (TpTestsContactsConnectionPrivate));
687
688   base_class->interfaces_always_present = interfaces_always_present;
689   base_class->create_handle_repos = create_handle_repos;
690   base_class->create_channel_managers = create_channel_managers;
691
692   param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay",
693       "Delay between simulated network events",
694       0, G_MAXUINT32, 1000,
695       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
696   g_object_class_install_property (object_class, PROP_SIMULATION_DELAY,
697       param_spec);
698
699   tp_contacts_mixin_class_init (object_class,
700       G_STRUCT_OFFSET (TpTestsContactsConnectionClass, contacts_mixin));
701
702   tp_presence_mixin_class_init (object_class,
703       G_STRUCT_OFFSET (TpTestsContactsConnectionClass, presence_mixin),
704       my_status_available, my_get_contact_statuses,
705       my_set_own_status, my_statuses);
706
707   tp_presence_mixin_simple_presence_init_dbus_properties (object_class);
708
709   klass->properties_class.interfaces = prop_interfaces;
710   tp_dbus_properties_mixin_class_init (object_class,
711       G_STRUCT_OFFSET (TpTestsContactsConnectionClass, properties_class));
712 }
713
714 TestContactListManager *
715 tp_tests_contacts_connection_get_contact_list_manager (
716     TpTestsContactsConnection *self)
717 {
718   return self->priv->list_manager;
719 }
720
721 void
722 tp_tests_contacts_connection_change_aliases (TpTestsContactsConnection *self,
723                                     guint n,
724                                     const TpHandle *handles,
725                                     const gchar * const *aliases)
726 {
727   GPtrArray *structs = g_ptr_array_sized_new (n);
728   guint i;
729
730   for (i = 0; i < n; i++)
731     {
732       GValueArray *pair = g_value_array_new (2);
733
734       DEBUG ("contact#%u -> %s", handles[i], aliases[i]);
735
736       g_hash_table_insert (self->priv->aliases,
737           GUINT_TO_POINTER (handles[i]), g_strdup (aliases[i]));
738
739       g_value_array_append (pair, NULL);
740       g_value_init (pair->values + 0, G_TYPE_UINT);
741       g_value_set_uint (pair->values + 0, handles[i]);
742
743       g_value_array_append (pair, NULL);
744       g_value_init (pair->values + 1, G_TYPE_STRING);
745       g_value_set_string (pair->values + 1, aliases[i]);
746
747       g_ptr_array_add (structs, pair);
748     }
749
750   tp_svc_connection_interface_aliasing_emit_aliases_changed (self,
751       structs);
752
753   g_ptr_array_foreach (structs, (GFunc) g_value_array_free, NULL);
754   g_ptr_array_free (structs, TRUE);
755 }
756
757 void
758 tp_tests_contacts_connection_change_presences (
759     TpTestsContactsConnection *self,
760     guint n,
761     const TpHandle *handles,
762     const TpTestsContactsConnectionPresenceStatusIndex *indexes,
763     const gchar * const *messages)
764 {
765   GHashTable *presences = g_hash_table_new_full (g_direct_hash, g_direct_equal,
766       NULL, (GDestroyNotify) tp_presence_status_free);
767   guint i;
768
769   for (i = 0; i < n; i++)
770     {
771       GHashTable *parameters;
772       gpointer key = GUINT_TO_POINTER (handles[i]);
773
774       DEBUG ("contact#%u -> %s \"%s\"", handles[i],
775           my_statuses[indexes[i]].name, messages[i]);
776
777       g_hash_table_insert (self->priv->presence_statuses, key,
778           GUINT_TO_POINTER (indexes[i]));
779       g_hash_table_insert (self->priv->presence_messages, key,
780           g_strdup (messages[i]));
781
782       parameters = g_hash_table_new_full (g_str_hash,
783           g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free);
784
785       if (messages[i] != NULL && messages[i][0] != '\0')
786         g_hash_table_insert (parameters, "message",
787             tp_g_value_slice_new_string (messages[i]));
788
789       g_hash_table_insert (presences, key, tp_presence_status_new (indexes[i],
790             parameters));
791       g_hash_table_destroy (parameters);
792     }
793
794   tp_presence_mixin_emit_presence_update ((GObject *) self,
795       presences);
796   g_hash_table_destroy (presences);
797 }
798
799 void
800 tp_tests_contacts_connection_change_avatar_tokens (TpTestsContactsConnection *self,
801                                           guint n,
802                                           const TpHandle *handles,
803                                           const gchar * const *tokens)
804 {
805   guint i;
806
807   for (i = 0; i < n; i++)
808     {
809       DEBUG ("contact#%u -> %s", handles[i], tokens[i]);
810       g_hash_table_insert (self->priv->avatars,
811           GUINT_TO_POINTER (handles[i]), avatar_data_new (NULL, NULL, tokens[i]));
812       tp_svc_connection_interface_avatars_emit_avatar_updated (self,
813           handles[i], tokens[i]);
814     }
815 }
816
817 void
818 tp_tests_contacts_connection_change_avatar_data (
819     TpTestsContactsConnection *self,
820     TpHandle handle,
821     GArray *data,
822     const gchar *mime_type,
823     const gchar *token)
824 {
825   g_hash_table_insert (self->priv->avatars,
826       GUINT_TO_POINTER (handle), avatar_data_new (data, mime_type, token));
827
828   tp_svc_connection_interface_avatars_emit_avatar_updated (self,
829       handle, token);
830 }
831
832 void
833 tp_tests_contacts_connection_change_locations (TpTestsContactsConnection *self,
834     guint n,
835     const TpHandle *handles,
836     GHashTable **locations)
837 {
838   guint i;
839
840   for (i = 0; i < n; i++)
841     {
842       DEBUG ("contact#%u ->", handles[i]);
843       tp_asv_dump (locations[i]);
844       g_hash_table_insert (self->priv->locations,
845           GUINT_TO_POINTER (handles[i]), g_hash_table_ref (locations[i]));
846
847       tp_svc_connection_interface_location_emit_location_updated (self,
848           handles[i], locations[i]);
849     }
850 }
851
852 void
853 tp_tests_contacts_connection_change_capabilities (
854     TpTestsContactsConnection *self,
855     GHashTable *capabilities)
856 {
857   GHashTableIter iter;
858   gpointer handle, caps;
859
860   g_hash_table_iter_init (&iter, capabilities);
861   while (g_hash_table_iter_next (&iter, &handle, &caps))
862     {
863       g_hash_table_insert (self->priv->capabilities,
864           handle,
865           g_boxed_copy (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST,
866             caps));
867     }
868
869   tp_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed (
870       self, capabilities);
871 }
872
873 void
874 tp_tests_contacts_connection_change_contact_info (
875     TpTestsContactsConnection *self,
876     TpHandle handle,
877     GPtrArray *info)
878 {
879   g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle),
880       g_ptr_array_ref (info));
881
882   tp_svc_connection_interface_contact_info_emit_contact_info_changed (self,
883       handle, info);
884 }
885
886 void
887 tp_tests_contacts_connection_set_default_contact_info (
888     TpTestsContactsConnection *self,
889     GPtrArray *info)
890 {
891   if (self->priv->default_contact_info != NULL)
892     g_ptr_array_unref (self->priv->default_contact_info);
893   self->priv->default_contact_info = g_ptr_array_ref (info);
894 }
895
896 static void
897 my_get_alias_flags (TpSvcConnectionInterfaceAliasing *aliasing,
898                     DBusGMethodInvocation *context)
899 {
900   TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
901
902   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
903   tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context,
904       0);
905 }
906
907 static void
908 my_get_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
909                 const GArray *contacts,
910                 DBusGMethodInvocation *context)
911 {
912   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing);
913   TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
914   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
915       TP_HANDLE_TYPE_CONTACT);
916   GHashTable *result;
917   GError *error = NULL;
918   guint i;
919
920   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
921
922   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
923     {
924       dbus_g_method_return_error (context, error);
925       g_error_free (error);
926       return;
927     }
928
929   result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
930
931   for (i = 0; i < contacts->len; i++)
932     {
933       TpHandle handle = g_array_index (contacts, TpHandle, i);
934       const gchar *alias = g_hash_table_lookup (self->priv->aliases,
935           GUINT_TO_POINTER (handle));
936
937       if (alias == NULL)
938         g_hash_table_insert (result, GUINT_TO_POINTER (handle),
939             (gchar *) tp_handle_inspect (contact_repo, handle));
940       else
941         g_hash_table_insert (result, GUINT_TO_POINTER (handle),
942             (gchar *) alias);
943     }
944
945   tp_svc_connection_interface_aliasing_return_from_get_aliases (context,
946       result);
947   g_hash_table_destroy (result);
948 }
949
950 static void
951 my_request_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
952                     const GArray *contacts,
953                     DBusGMethodInvocation *context)
954 {
955   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing);
956   TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
957   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
958       TP_HANDLE_TYPE_CONTACT);
959   GPtrArray *result;
960   gchar **strings;
961   GError *error = NULL;
962   guint i;
963
964   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
965
966   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
967     {
968       dbus_g_method_return_error (context, error);
969       g_error_free (error);
970       return;
971     }
972
973   result = g_ptr_array_sized_new (contacts->len + 1);
974
975   for (i = 0; i < contacts->len; i++)
976     {
977       TpHandle handle = g_array_index (contacts, TpHandle, i);
978       const gchar *alias = g_hash_table_lookup (self->priv->aliases,
979           GUINT_TO_POINTER (handle));
980
981       if (alias == NULL)
982         g_ptr_array_add (result,
983             (gchar *) tp_handle_inspect (contact_repo, handle));
984       else
985         g_ptr_array_add (result, (gchar *) alias);
986     }
987
988   g_ptr_array_add (result, NULL);
989   strings = (gchar **) g_ptr_array_free (result, FALSE);
990   tp_svc_connection_interface_aliasing_return_from_request_aliases (context,
991       (const gchar **) strings);
992   g_free (strings);
993 }
994
995 static void
996 init_aliasing (gpointer g_iface,
997                gpointer iface_data)
998 {
999   TpSvcConnectionInterfaceAliasingClass *klass = g_iface;
1000
1001 #define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\
1002     klass, my_##x)
1003   IMPLEMENT(get_alias_flags);
1004   IMPLEMENT(request_aliases);
1005   IMPLEMENT(get_aliases);
1006   /* IMPLEMENT(set_aliases); */
1007 #undef IMPLEMENT
1008 }
1009
1010 static void
1011 my_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars,
1012                       const GArray *contacts,
1013                       DBusGMethodInvocation *context)
1014 {
1015   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
1016   TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
1017   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1018       TP_HANDLE_TYPE_CONTACT);
1019   GError *error = NULL;
1020   GHashTable *result;
1021   guint i;
1022
1023   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1024
1025   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
1026     {
1027       dbus_g_method_return_error (context, error);
1028       g_error_free (error);
1029       return;
1030     }
1031
1032   result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
1033
1034   for (i = 0; i < contacts->len; i++)
1035     {
1036       TpHandle handle = g_array_index (contacts, TpHandle, i);
1037       AvatarData *a = g_hash_table_lookup (self->priv->avatars,
1038           GUINT_TO_POINTER (handle));
1039
1040       if (a == NULL || a->token == NULL)
1041         {
1042           /* we're expected to do a round-trip to the server to find out
1043            * their token, so we have to give some sort of result. Assume
1044            * no avatar, here */
1045           a = avatar_data_new (NULL, NULL, "");
1046           g_hash_table_insert (self->priv->avatars,
1047               GUINT_TO_POINTER (handle), a);
1048           tp_svc_connection_interface_avatars_emit_avatar_updated (self,
1049               handle, a->token);
1050         }
1051
1052       g_hash_table_insert (result, GUINT_TO_POINTER (handle),
1053           a->token);
1054     }
1055
1056   tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens (
1057       context, result);
1058   g_hash_table_destroy (result);
1059 }
1060
1061 static void
1062 my_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars,
1063                             const GArray *contacts,
1064                             DBusGMethodInvocation *context)
1065 {
1066   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
1067   TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
1068   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1069       TP_HANDLE_TYPE_CONTACT);
1070   GError *error = NULL;
1071   GHashTable *result;
1072   guint i;
1073
1074   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1075
1076   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
1077     {
1078       dbus_g_method_return_error (context, error);
1079       g_error_free (error);
1080       return;
1081     }
1082
1083   result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
1084
1085   for (i = 0; i < contacts->len; i++)
1086     {
1087       TpHandle handle = g_array_index (contacts, TpHandle, i);
1088       AvatarData *a = g_hash_table_lookup (self->priv->avatars,
1089           GUINT_TO_POINTER (handle));
1090       const gchar *token = a ? a->token : NULL;
1091
1092       g_hash_table_insert (result, GUINT_TO_POINTER (handle),
1093           (gchar *) (token != NULL ? token : ""));
1094     }
1095
1096   tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens (
1097       context, result);
1098   g_hash_table_destroy (result);
1099 }
1100
1101 static void
1102 my_request_avatars (TpSvcConnectionInterfaceAvatars *avatars,
1103     const GArray *contacts,
1104     DBusGMethodInvocation *context)
1105 {
1106   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
1107   TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
1108   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1109       TP_HANDLE_TYPE_CONTACT);
1110   GError *error = NULL;
1111   guint i;
1112
1113   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1114
1115   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
1116     {
1117       dbus_g_method_return_error (context, error);
1118       g_error_free (error);
1119       return;
1120     }
1121
1122   for (i = 0; i < contacts->len; i++)
1123     {
1124       TpHandle handle = g_array_index (contacts, TpHandle, i);
1125       AvatarData *a = g_hash_table_lookup (self->priv->avatars,
1126           GUINT_TO_POINTER (handle));
1127
1128       if (a != NULL)
1129         tp_svc_connection_interface_avatars_emit_avatar_retrieved (self, handle,
1130             a->token, a->data, a->mime_type);
1131     }
1132
1133   tp_svc_connection_interface_avatars_return_from_request_avatars (context);
1134 }
1135
1136 static void
1137 conn_avatars_properties_getter (GObject *object,
1138                                 GQuark interface,
1139                                 GQuark name,
1140                                 GValue *value,
1141                                 gpointer getter_data)
1142 {
1143   GQuark q_mime_types = g_quark_from_static_string (
1144       "SupportedAvatarMIMETypes");
1145
1146   if (name == q_mime_types)
1147     {
1148       g_value_set_static_boxed (value, mime_types);
1149     }
1150   else
1151     {
1152       g_value_set_uint (value, GPOINTER_TO_UINT (getter_data));
1153     }
1154 }
1155
1156 static void
1157 init_avatars (gpointer g_iface,
1158               gpointer iface_data)
1159 {
1160   TpSvcConnectionInterfaceAvatarsClass *klass = g_iface;
1161
1162 #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x (\
1163     klass, my_##x)
1164   /* IMPLEMENT(get_avatar_requirements); */
1165   IMPLEMENT(get_avatar_tokens);
1166   IMPLEMENT(get_known_avatar_tokens);
1167   /* IMPLEMENT(request_avatar); */
1168   IMPLEMENT(request_avatars);
1169   /* IMPLEMENT(set_avatar); */
1170   /* IMPLEMENT(clear_avatar); */
1171 #undef IMPLEMENT
1172 }
1173
1174 static void
1175 my_get_locations (TpSvcConnectionInterfaceLocation *avatars,
1176     const GArray *contacts,
1177     DBusGMethodInvocation *context)
1178 {
1179   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
1180   TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
1181   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1182       TP_HANDLE_TYPE_CONTACT);
1183   GError *error = NULL;
1184   GHashTable *result;
1185   guint i;
1186
1187   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1188
1189   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
1190     {
1191       dbus_g_method_return_error (context, error);
1192       g_error_free (error);
1193       return;
1194     }
1195
1196   result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
1197
1198   for (i = 0; i < contacts->len; i++)
1199     {
1200       TpHandle handle = g_array_index (contacts, TpHandle, i);
1201       GHashTable *location = g_hash_table_lookup (self->priv->locations,
1202           GUINT_TO_POINTER (handle));
1203
1204       if (location != NULL)
1205         {
1206           g_hash_table_insert (result, GUINT_TO_POINTER (handle), location);
1207         }
1208     }
1209
1210   tp_svc_connection_interface_location_return_from_get_locations (
1211       context, result);
1212   g_hash_table_destroy (result);
1213 }
1214
1215 static void
1216 init_location (gpointer g_iface,
1217     gpointer iface_data)
1218 {
1219   TpSvcConnectionInterfaceLocationClass *klass = g_iface;
1220
1221 #define IMPLEMENT(x) tp_svc_connection_interface_location_implement_##x (\
1222     klass, my_##x)
1223   IMPLEMENT(get_locations);
1224 #undef IMPLEMENT
1225 }
1226
1227 static void
1228 my_get_contact_capabilities (TpSvcConnectionInterfaceContactCapabilities *obj,
1229     const GArray *contacts,
1230     DBusGMethodInvocation *context)
1231 {
1232   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj);
1233   TpBaseConnection *base = TP_BASE_CONNECTION (obj);
1234   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1235       TP_HANDLE_TYPE_CONTACT);
1236   GError *error = NULL;
1237   GHashTable *result;
1238   guint i;
1239
1240   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1241
1242   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
1243     {
1244       dbus_g_method_return_error (context, error);
1245       g_error_free (error);
1246       return;
1247     }
1248
1249   result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
1250
1251   for (i = 0; i < contacts->len; i++)
1252     {
1253       TpHandle handle = g_array_index (contacts, TpHandle, i);
1254       GPtrArray *arr = g_hash_table_lookup (self->priv->capabilities,
1255           GUINT_TO_POINTER (handle));
1256
1257       if (arr != NULL)
1258         {
1259           g_hash_table_insert (result, GUINT_TO_POINTER (handle), arr);
1260         }
1261     }
1262
1263   tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities (
1264       context, result);
1265
1266   g_hash_table_destroy (result);
1267 }
1268
1269 static void
1270 init_contact_caps (gpointer g_iface,
1271     gpointer iface_data)
1272 {
1273   TpSvcConnectionInterfaceContactCapabilitiesClass *klass = g_iface;
1274
1275 #define IMPLEMENT(x) tp_svc_connection_interface_contact_capabilities_implement_##x (\
1276     klass, my_##x)
1277   IMPLEMENT(get_contact_capabilities);
1278 #undef IMPLEMENT
1279 }
1280
1281 static GPtrArray *
1282 lookup_contact_info (TpTestsContactsConnection *self,
1283     TpHandle handle)
1284 {
1285   GPtrArray *ret = g_hash_table_lookup (self->priv->contact_info,
1286       GUINT_TO_POINTER (handle));
1287
1288   if (ret == NULL && self->priv->default_contact_info != NULL)
1289     {
1290       ret = self->priv->default_contact_info;
1291       g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle),
1292           g_ptr_array_ref (ret));
1293     }
1294
1295   return ret;
1296 }
1297
1298 static void
1299 my_refresh_contact_info (TpSvcConnectionInterfaceContactInfo *obj,
1300     const GArray *contacts,
1301     DBusGMethodInvocation *context)
1302 {
1303   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj);
1304   TpBaseConnection *base = TP_BASE_CONNECTION (obj);
1305   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1306       TP_HANDLE_TYPE_CONTACT);
1307   GError *error = NULL;
1308   guint i;
1309
1310   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1311
1312   if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
1313     {
1314       dbus_g_method_return_error (context, error);
1315       g_error_free (error);
1316       return;
1317     }
1318
1319   for (i = 0; i < contacts->len; i++)
1320     {
1321       TpHandle handle = g_array_index (contacts, guint, i);
1322       GPtrArray *arr = lookup_contact_info (self, handle);
1323
1324       tp_svc_connection_interface_contact_info_emit_contact_info_changed (self,
1325           handle, arr);
1326     }
1327
1328   tp_svc_connection_interface_contact_info_return_from_refresh_contact_info (
1329       context);
1330 }
1331
1332 static void
1333 my_request_contact_info (TpSvcConnectionInterfaceContactInfo *obj,
1334     guint handle,
1335     DBusGMethodInvocation *context)
1336 {
1337   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj);
1338   TpBaseConnection *base = TP_BASE_CONNECTION (obj);
1339   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
1340       TP_HANDLE_TYPE_CONTACT);
1341   GError *error = NULL;
1342   GPtrArray *ret;
1343
1344   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1345
1346   if (!tp_handle_is_valid (contact_repo, handle, &error))
1347     {
1348       dbus_g_method_return_error (context, error);
1349       g_error_free (error);
1350       return;
1351     }
1352
1353   ret = lookup_contact_info (self, handle);
1354
1355   tp_svc_connection_interface_contact_info_return_from_request_contact_info (
1356       context, ret);
1357 }
1358
1359 static void
1360 my_set_contact_info (TpSvcConnectionInterfaceContactInfo *obj,
1361     const GPtrArray *info,
1362     DBusGMethodInvocation *context)
1363 {
1364   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj);
1365   TpBaseConnection *base = TP_BASE_CONNECTION (obj);
1366   GPtrArray *copy;
1367   guint i;
1368   TpHandle self_handle;
1369
1370   TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
1371
1372   /* Deep copy info */
1373   copy = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free);
1374   for (i = 0; i < info->len; i++)
1375     g_ptr_array_add (copy, g_value_array_copy (g_ptr_array_index (info, i)));
1376
1377   self_handle = tp_base_connection_get_self_handle (base);
1378   g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (self_handle),
1379       copy);
1380
1381   tp_svc_connection_interface_contact_info_return_from_set_contact_info (
1382       context);
1383 }
1384
1385 static void
1386 init_contact_info (gpointer g_iface,
1387     gpointer iface_data)
1388 {
1389   TpSvcConnectionInterfaceContactInfoClass *klass = g_iface;
1390
1391 #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x (\
1392     klass, my_##x)
1393   IMPLEMENT (refresh_contact_info);
1394   IMPLEMENT (request_contact_info);
1395   IMPLEMENT (set_contact_info);
1396 #undef IMPLEMENT
1397 }
1398
1399 /* =============== Legacy version (no Contacts interface) ================= */
1400
1401 G_DEFINE_TYPE (TpTestsLegacyContactsConnection,
1402     tp_tests_legacy_contacts_connection, TP_TESTS_TYPE_CONTACTS_CONNECTION);
1403
1404 static void
1405 tp_tests_legacy_contacts_connection_init (TpTestsLegacyContactsConnection *self)
1406 {
1407 }
1408
1409 static void
1410 tp_tests_legacy_contacts_connection_class_init (
1411     TpTestsLegacyContactsConnectionClass *klass)
1412 {
1413   /* Leave Contacts out of the interfaces we say are present, so clients
1414    * won't use it */
1415   static const gchar *interfaces_always_present[] = {
1416       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
1417       TP_IFACE_CONNECTION_INTERFACE_AVATARS,
1418       TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
1419       TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
1420       TP_IFACE_CONNECTION_INTERFACE_LOCATION,
1421       TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
1422       NULL };
1423   TpBaseConnectionClass *base_class =
1424       (TpBaseConnectionClass *) klass;
1425
1426   base_class->interfaces_always_present = interfaces_always_present;
1427 }
1428
1429 /* =============== No Requests and no ContactCapabilities ================= */
1430
1431 G_DEFINE_TYPE (TpTestsNoRequestsConnection, tp_tests_no_requests_connection,
1432     TP_TESTS_TYPE_CONTACTS_CONNECTION);
1433
1434 static void
1435 tp_tests_no_requests_connection_init (TpTestsNoRequestsConnection *self)
1436 {
1437 }
1438
1439 static void
1440 tp_tests_no_requests_connection_class_init (
1441     TpTestsNoRequestsConnectionClass *klass)
1442 {
1443   static const gchar *interfaces_always_present[] = {
1444       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
1445       TP_IFACE_CONNECTION_INTERFACE_AVATARS,
1446       TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
1447       TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
1448       TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
1449       TP_IFACE_CONNECTION_INTERFACE_LOCATION,
1450       NULL };
1451   TpBaseConnectionClass *base_class =
1452       (TpBaseConnectionClass *) klass;
1453
1454   base_class->interfaces_always_present = interfaces_always_present;
1455 }
1456