telepathy: simplify telepathy_buddy_entry handling
[siplcs.git] / src / telepathy / telepathy-buddy.c
blob63c716a123271397e9c81068948ec4c4d5f48e47
1 /**
2 * @file telepathy-buddy.c
4 * pidgin-sipe
6 * Copyright (C) 2012 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <glib-object.h>
24 #include <telepathy-glib/base-connection.h>
25 #include <telepathy-glib/base-contact-list.h>
26 #include <telepathy-glib/telepathy-glib.h>
28 #include "sipe-backend.h"
29 #include "sipe-common.h"
30 #include "sipe-core.h"
32 #include "telepathy-private.h"
34 struct telepathy_buddy {
35 const gchar *uri; /* borrowed from contact_list->buddies key */
36 GHashTable *groups; /* keys are borrowed from contact_list->groups */
37 TpHandle handle;
38 /* @TODO: server vs. local alias - what do we need to support? */
39 gchar *alias;
42 struct telepathy_buddy_entry {
43 struct telepathy_buddy *buddy; /* pointer to parent */
44 const gchar *group; /* borrowed from contact_list->groups key */
47 G_BEGIN_DECLS
49 * Contact List class - data structures
51 typedef struct _SipeContactListClass {
52 TpBaseContactListClass parent_class;
53 } SipeContactListClass;
55 typedef struct _SipeContactList {
56 TpBaseContactList parent;
58 TpBaseConnection *connection;
59 TpHandleRepoIface *contact_repo;
60 TpHandleSet *contacts;
62 GHashTable *buddies;
63 GHashTable *buddy_handles;
64 GHashTable *groups;
66 gboolean initial_received;
67 } SipeContactList;
70 * Contact List class - type macros
72 static GType sipe_contact_list_get_type(void) G_GNUC_CONST;
73 #define SIPE_TYPE_CONTACT_LIST \
74 (sipe_contact_list_get_type())
75 #define SIPE_CONTACT_LIST(obj) \
76 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONTACT_LIST, \
77 SipeContactList))
78 G_END_DECLS
81 * Contact List class - type definition
83 static void contact_group_list_iface_init(TpContactGroupListInterface *);
84 G_DEFINE_TYPE_WITH_CODE(SipeContactList,
85 sipe_contact_list,
86 TP_TYPE_BASE_CONTACT_LIST,
87 G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST,
88 contact_group_list_iface_init);
93 * Contact List class - instance methods
95 static TpHandleSet *dup_contacts(TpBaseContactList *contact_list)
97 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
98 return(tp_handle_set_copy(self->contacts));
101 static void dup_states(SIPE_UNUSED_PARAMETER TpBaseContactList *contact_list,
102 SIPE_UNUSED_PARAMETER TpHandle contact,
103 TpSubscriptionState *subscribe,
104 TpSubscriptionState *publish,
105 gchar **publish_request)
107 /* @TODO */
108 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
110 if (subscribe)
111 *subscribe = TP_SUBSCRIPTION_STATE_YES;
112 if (publish)
113 *publish = TP_SUBSCRIPTION_STATE_YES;
114 if (publish_request)
115 *publish_request = g_strdup("");
118 static void sipe_contact_list_constructed(GObject *object)
120 SipeContactList *self = SIPE_CONTACT_LIST(object);
121 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->constructed;
123 if (chain_up)
124 chain_up(object);
126 g_object_get(self, "connection", &self->connection, NULL);
127 self->contact_repo = tp_base_connection_get_handles(self->connection,
128 TP_HANDLE_TYPE_CONTACT);
129 self->contacts = tp_handle_set_new(self->contact_repo);
132 static void sipe_contact_list_dispose(GObject *object)
134 SipeContactList *self = SIPE_CONTACT_LIST(object);
135 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->dispose;
137 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dispose");
139 tp_clear_pointer(&self->contacts, tp_handle_set_destroy);
140 tp_clear_object(&self->connection);
141 /* NOTE: the order is important due to borrowing of keys! */
142 tp_clear_pointer(&self->buddy_handles, g_hash_table_unref);
143 tp_clear_pointer(&self->buddies, g_hash_table_unref);
144 tp_clear_pointer(&self->groups, g_hash_table_unref);
146 if (chain_up)
147 chain_up(object);
151 * Contact List class - type implementation
153 static void sipe_contact_list_class_init(SipeContactListClass *klass)
155 GObjectClass *object_class = G_OBJECT_CLASS(klass);
156 TpBaseContactListClass *base_class = TP_BASE_CONTACT_LIST_CLASS(klass);
158 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::class_init");
160 object_class->constructed = sipe_contact_list_constructed;
161 object_class->dispose = sipe_contact_list_dispose;
163 base_class->dup_contacts = dup_contacts;
164 base_class->dup_states = dup_states;
167 static void buddy_free(gpointer data);
168 static void sipe_contact_list_init(SIPE_UNUSED_PARAMETER SipeContactList *self)
170 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::init");
172 self->buddies = g_hash_table_new_full(g_str_hash, g_str_equal,
173 g_free, buddy_free);
174 self->buddy_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
175 self->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
176 g_free, NULL);
178 self->initial_received = FALSE;
182 * Contact List class - interface implementation
184 * Contact groups
186 static GStrv dup_groups(TpBaseContactList *contact_list)
188 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
189 GPtrArray *groups = g_ptr_array_sized_new(
190 g_hash_table_size(self->groups) + 1);
191 GHashTableIter iter;
192 gpointer name;
194 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_groups called");
196 g_hash_table_iter_init(&iter, self->groups);
197 while (g_hash_table_iter_next(&iter, &name, NULL))
198 g_ptr_array_add(groups, g_strdup(name));
199 g_ptr_array_add(groups, NULL);
201 return((GStrv) g_ptr_array_free(groups, FALSE));
204 static TpHandleSet *dup_group_members(TpBaseContactList *contact_list,
205 const gchar *group_name)
207 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
208 TpHandleSet *members = tp_handle_set_new(self->contact_repo);
209 GHashTableIter iter;
210 struct telepathy_buddy *buddy;
212 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_group_members called");
214 g_hash_table_iter_init(&iter, self->buddies);
215 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy))
216 if (g_hash_table_lookup(buddy->groups, group_name))
217 tp_handle_set_add(members, buddy->handle);
219 return(members);
222 static GStrv dup_contact_groups(TpBaseContactList *contact_list,
223 TpHandle contact)
225 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
226 GPtrArray *groups = g_ptr_array_sized_new(
227 g_hash_table_size(self->groups) + 1);
228 struct telepathy_buddy *buddy = g_hash_table_lookup(self->buddy_handles,
229 GUINT_TO_POINTER(contact));
231 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_contact_groups called");
233 if (buddy) {
234 GHashTableIter iter;
235 const gchar *group_name;
237 g_hash_table_iter_init(&iter, buddy->groups);
238 while (g_hash_table_iter_next(&iter,
239 (gpointer) &group_name,
240 NULL))
241 g_ptr_array_add(groups, g_strdup(group_name));
243 g_ptr_array_add(groups, NULL);
245 return((GStrv) g_ptr_array_free(groups, FALSE));
248 static void contact_group_list_iface_init(TpContactGroupListInterface *iface)
250 iface->dup_groups = dup_groups;
251 iface->dup_group_members = dup_group_members;
252 iface->dup_contact_groups = dup_contact_groups;
255 /* create new contact list object */
256 SipeContactList *sipe_telepathy_contact_list_new(TpBaseConnection *connection)
258 return(g_object_new(SIPE_TYPE_CONTACT_LIST,
259 "connection", connection,
260 NULL));
263 /* get & set alias for a contact */
264 const gchar *sipe_telepathy_buddy_get_alias(SipeContactList *contact_list,
265 const TpHandle contact)
267 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
268 GUINT_TO_POINTER(contact));
269 if (!buddy)
270 return(NULL);
271 return(buddy->alias);
274 static void update_alias(struct telepathy_buddy *buddy,
275 const gchar *alias)
277 if (buddy) {
278 g_free(buddy->alias);
279 buddy->alias = g_strdup(alias);
283 void sipe_telepathy_buddy_set_alias(struct _SipeContactList *contact_list,
284 const guint contact,
285 const gchar *alias)
287 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
288 GUINT_TO_POINTER(contact));
289 update_alias(buddy, alias);
293 * Backend adaptor functions
295 sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public,
296 const gchar *buddy_name,
297 const gchar *group_name)
299 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
300 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
301 buddy_name);
302 if (!buddy)
303 return(NULL);
305 if (group_name) {
306 return(g_hash_table_lookup(buddy->groups, group_name));
307 } else {
308 /* just return the first entry */
309 GHashTableIter iter;
310 gpointer value;
311 g_hash_table_iter_init(&iter, buddy->groups);
312 g_hash_table_iter_next(&iter, NULL, &value);
313 return(value);
317 static GSList *buddy_add_all(struct telepathy_buddy *buddy, GSList *list)
319 GHashTableIter iter;
320 struct telepathy_buddy_entry *buddy_entry;
322 if (!buddy)
323 return(list);
325 g_hash_table_iter_init(&iter, buddy->groups);
326 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy_entry))
327 list = g_slist_prepend(list, buddy_entry);
329 return(list);
332 GSList *sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
333 const gchar *buddy_name,
334 const gchar *group_name)
336 GSList *result = NULL;
338 /* NOTE: group_name != NULL not implemented in purple either */
339 if (!group_name) {
340 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
341 GHashTable *buddies = telepathy_private->contact_list->buddies;
343 if (buddy_name) {
344 result = buddy_add_all(g_hash_table_lookup(buddies,
345 buddy_name),
346 result);
347 } else {
348 GHashTableIter biter;
349 struct telepathy_buddy *buddy;
351 g_hash_table_iter_init(&biter, telepathy_private->contact_list->buddies);
352 while (g_hash_table_iter_next(&biter, NULL, (gpointer) &buddy))
353 result = buddy_add_all(buddy, result);
357 return(result);
360 gchar *sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
361 const sipe_backend_buddy who)
363 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->uri));
366 gchar *sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
367 const sipe_backend_buddy who)
369 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->alias));
372 gchar *sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
373 const sipe_backend_buddy who)
375 return(g_strdup(((struct telepathy_buddy_entry *) who)->group));
378 void sipe_backend_buddy_set_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
379 const sipe_backend_buddy who,
380 const gchar *alias)
382 struct telepathy_buddy_entry *buddy_entry = who;
383 update_alias(buddy_entry->buddy, alias);
385 /* @TODO: emit signal? */
388 void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public)
390 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
391 SipeContactList *contact_list = telepathy_private->contact_list;
393 if (!contact_list->initial_received) {
394 /* we can only call this once */
395 contact_list->initial_received = TRUE;
396 SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called");
397 tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list));
401 static void buddy_free(gpointer data)
403 struct telepathy_buddy *buddy = data;
404 g_hash_table_destroy(buddy->groups);
405 g_free(buddy->alias);
406 g_free(buddy);
409 sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public,
410 const gchar *name,
411 const gchar *alias,
412 const gchar *group_name)
414 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
415 SipeContactList *contact_list = telepathy_private->contact_list;
416 const gchar *group = g_hash_table_lookup(contact_list->groups,
417 group_name);
418 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
419 name);
420 struct telepathy_buddy_entry *buddy_entry;
422 if (!group)
423 return(NULL);
425 if (!buddy) {
426 buddy = g_new0(struct telepathy_buddy, 1);
427 buddy->uri = g_strdup(name); /* reused as key */
428 buddy->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
429 NULL, g_free);
430 buddy->alias = g_strdup(alias);
431 buddy->handle = tp_handle_ensure(contact_list->contact_repo,
432 buddy->uri, NULL, NULL);
433 tp_handle_set_add(contact_list->contacts, buddy->handle);
434 g_hash_table_insert(contact_list->buddies,
435 (gchar *) buddy->uri, /* owned by hash table */
436 buddy);
437 g_hash_table_insert(contact_list->buddy_handles,
438 GUINT_TO_POINTER(buddy->handle),
439 buddy);
442 buddy_entry = g_hash_table_lookup(buddy->groups, group);
443 if (!buddy_entry) {
444 buddy_entry = g_new0(struct telepathy_buddy_entry, 1);
445 buddy_entry->buddy = buddy;
446 buddy_entry->group = group;
447 g_hash_table_insert(buddy->groups,
448 (gchar *) group, /* key is borrowed */
449 buddy_entry);
452 if (contact_list->initial_received) {
453 /* @TODO: emit signal? */
456 return(buddy_entry);
459 void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public,
460 const sipe_backend_buddy who)
462 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
463 SipeContactList *contact_list = telepathy_private->contact_list;
464 struct telepathy_buddy_entry *remove_entry = who;
465 struct telepathy_buddy *buddy = remove_entry->buddy;
467 g_hash_table_remove(buddy->groups,
468 remove_entry->group);
469 /* remove_entry is invalid */
471 if (g_hash_table_size(buddy->groups) == 0) {
472 /* removed from last group -> drop this buddy */
473 tp_handle_set_remove(contact_list->contacts,
474 buddy->handle);
475 g_hash_table_remove(contact_list->buddy_handles,
476 GUINT_TO_POINTER(buddy->handle));
477 g_hash_table_remove(contact_list->buddies,
478 buddy->uri);
482 if (contact_list->initial_received) {
483 /* @TODO: emit signal? */
487 gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public,
488 const gchar *group_name)
490 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
491 SipeContactList *contact_list = telepathy_private->contact_list;
492 gchar *group = g_hash_table_lookup(contact_list->groups,
493 group_name);
495 if (!group) {
496 group = g_strdup(group_name);
497 g_hash_table_insert(contact_list->groups, group, group);
498 tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list),
499 &group_name,
503 return(group != NULL);
508 Local Variables:
509 mode: c
510 c-file-style: "bsd"
511 indent-tabs-mode: t
512 tab-width: 8
513 End: