telepathy: forward alias changes to core
[siplcs.git] / src / telepathy / telepathy-buddy.c
blob3d22f497e34c966f3758aa91ef54d77f7c5d6bb6
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; /* key: group name, value: buddy_entry */
37 /* keys are borrowed from contact_list->groups */
38 TpHandle handle;
39 /* @TODO: server vs. local alias - what do we need to support? */
40 gchar *alias;
41 guint activity;
44 struct telepathy_buddy_entry {
45 struct telepathy_buddy *buddy; /* pointer to parent */
46 const gchar *group; /* borrowed from contact_list->groups key */
49 G_BEGIN_DECLS
51 * Contact List class - data structures
53 typedef struct _SipeContactListClass {
54 TpBaseContactListClass parent_class;
55 } SipeContactListClass;
57 typedef struct _SipeContactList {
58 TpBaseContactList parent;
60 TpBaseConnection *connection;
61 TpHandleRepoIface *contact_repo;
62 TpHandleSet *contacts;
64 GHashTable *buddies; /* key: SIP URI, value: buddy */
65 GHashTable *buddy_handles; /* key: TpHandle, value: buddy */
66 GHashTable *groups; /* key: group name, value: buddy */
68 gboolean initial_received;
69 } SipeContactList;
72 * Contact List class - type macros
74 static GType sipe_contact_list_get_type(void) G_GNUC_CONST;
75 #define SIPE_TYPE_CONTACT_LIST \
76 (sipe_contact_list_get_type())
77 #define SIPE_CONTACT_LIST(obj) \
78 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONTACT_LIST, \
79 SipeContactList))
80 G_END_DECLS
83 * Contact List class - type definition
85 static void contact_group_list_iface_init(TpContactGroupListInterface *);
86 G_DEFINE_TYPE_WITH_CODE(SipeContactList,
87 sipe_contact_list,
88 TP_TYPE_BASE_CONTACT_LIST,
89 G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST,
90 contact_group_list_iface_init);
95 * Contact List class - instance methods
97 static TpHandleSet *dup_contacts(TpBaseContactList *contact_list)
99 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
100 return(tp_handle_set_copy(self->contacts));
103 static void dup_states(SIPE_UNUSED_PARAMETER TpBaseContactList *contact_list,
104 SIPE_UNUSED_PARAMETER TpHandle contact,
105 TpSubscriptionState *subscribe,
106 TpSubscriptionState *publish,
107 gchar **publish_request)
109 /* @TODO */
110 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
112 if (subscribe)
113 *subscribe = TP_SUBSCRIPTION_STATE_YES;
114 if (publish)
115 *publish = TP_SUBSCRIPTION_STATE_YES;
116 if (publish_request)
117 *publish_request = g_strdup("");
120 static void sipe_contact_list_constructed(GObject *object)
122 SipeContactList *self = SIPE_CONTACT_LIST(object);
123 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->constructed;
125 if (chain_up)
126 chain_up(object);
128 g_object_get(self, "connection", &self->connection, NULL);
129 self->contact_repo = tp_base_connection_get_handles(self->connection,
130 TP_HANDLE_TYPE_CONTACT);
131 self->contacts = tp_handle_set_new(self->contact_repo);
134 static void sipe_contact_list_dispose(GObject *object)
136 SipeContactList *self = SIPE_CONTACT_LIST(object);
137 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->dispose;
139 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dispose");
141 tp_clear_pointer(&self->contacts, tp_handle_set_destroy);
142 tp_clear_object(&self->connection);
143 /* NOTE: the order is important due to borrowing of keys! */
144 tp_clear_pointer(&self->buddy_handles, g_hash_table_unref);
145 tp_clear_pointer(&self->buddies, g_hash_table_unref);
146 tp_clear_pointer(&self->groups, g_hash_table_unref);
148 if (chain_up)
149 chain_up(object);
153 * Contact List class - type implementation
155 static void sipe_contact_list_class_init(SipeContactListClass *klass)
157 GObjectClass *object_class = G_OBJECT_CLASS(klass);
158 TpBaseContactListClass *base_class = TP_BASE_CONTACT_LIST_CLASS(klass);
160 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::class_init");
162 object_class->constructed = sipe_contact_list_constructed;
163 object_class->dispose = sipe_contact_list_dispose;
165 base_class->dup_contacts = dup_contacts;
166 base_class->dup_states = dup_states;
169 static void buddy_free(gpointer data);
170 static void sipe_contact_list_init(SIPE_UNUSED_PARAMETER SipeContactList *self)
172 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::init");
174 self->buddies = g_hash_table_new_full(g_str_hash, g_str_equal,
175 g_free, buddy_free);
176 self->buddy_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
177 self->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
178 g_free, NULL);
180 self->initial_received = FALSE;
184 * Contact List class - interface implementation
186 * Contact groups
188 static GStrv dup_groups(TpBaseContactList *contact_list)
190 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
191 GPtrArray *groups = g_ptr_array_sized_new(
192 g_hash_table_size(self->groups) + 1);
193 GHashTableIter iter;
194 gpointer name;
196 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_groups called");
198 g_hash_table_iter_init(&iter, self->groups);
199 while (g_hash_table_iter_next(&iter, &name, NULL))
200 g_ptr_array_add(groups, g_strdup(name));
201 g_ptr_array_add(groups, NULL);
203 return((GStrv) g_ptr_array_free(groups, FALSE));
206 static TpHandleSet *dup_group_members(TpBaseContactList *contact_list,
207 const gchar *group_name)
209 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
210 TpHandleSet *members = tp_handle_set_new(self->contact_repo);
211 GHashTableIter iter;
212 struct telepathy_buddy *buddy;
214 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_group_members called");
216 g_hash_table_iter_init(&iter, self->buddies);
217 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy))
218 if (g_hash_table_lookup(buddy->groups, group_name))
219 tp_handle_set_add(members, buddy->handle);
221 return(members);
224 static GStrv dup_contact_groups(TpBaseContactList *contact_list,
225 TpHandle contact)
227 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
228 GPtrArray *groups = g_ptr_array_sized_new(
229 g_hash_table_size(self->groups) + 1);
230 struct telepathy_buddy *buddy = g_hash_table_lookup(self->buddy_handles,
231 GUINT_TO_POINTER(contact));
233 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_contact_groups called");
235 if (buddy) {
236 GHashTableIter iter;
237 const gchar *group_name;
239 g_hash_table_iter_init(&iter, buddy->groups);
240 while (g_hash_table_iter_next(&iter,
241 (gpointer) &group_name,
242 NULL))
243 g_ptr_array_add(groups, g_strdup(group_name));
245 g_ptr_array_add(groups, NULL);
247 return((GStrv) g_ptr_array_free(groups, FALSE));
250 static void contact_group_list_iface_init(TpContactGroupListInterface *iface)
252 iface->dup_groups = dup_groups;
253 iface->dup_group_members = dup_group_members;
254 iface->dup_contact_groups = dup_contact_groups;
257 /* create new contact list object */
258 SipeContactList *sipe_telepathy_contact_list_new(TpBaseConnection *connection)
260 return(g_object_new(SIPE_TYPE_CONTACT_LIST,
261 "connection", connection,
262 NULL));
265 /* get & set alias for a contact */
266 const gchar *sipe_telepathy_buddy_get_alias(SipeContactList *contact_list,
267 TpHandle contact)
269 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
270 GUINT_TO_POINTER(contact));
271 if (!buddy)
272 return(NULL);
273 return(buddy->alias);
276 static void update_alias(struct telepathy_buddy *buddy,
277 const gchar *alias)
279 if (buddy) {
280 g_free(buddy->alias);
281 buddy->alias = g_strdup(alias);
285 void sipe_telepathy_buddy_set_alias(SipeContactList *contact_list,
286 const guint contact,
287 const gchar *alias)
289 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
290 GUINT_TO_POINTER(contact));
291 update_alias(buddy, alias);
293 /* tell core about the alias change */
294 if (buddy) {
295 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(contact_list->connection));
296 sipe_core_group_set_user(telepathy_private->public,
297 buddy->uri);
301 /* get presence status for a contact */
302 guint sipe_telepathy_buddy_get_presence(SipeContactList *contact_list,
303 const TpHandle contact)
305 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
306 GUINT_TO_POINTER(contact));
307 if (!buddy)
308 return(SIPE_ACTIVITY_UNSET);
309 return(buddy->activity);
313 * Backend adaptor functions
315 sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public,
316 const gchar *buddy_name,
317 const gchar *group_name)
319 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
320 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
321 buddy_name);
322 if (!buddy)
323 return(NULL);
325 if (group_name) {
326 return(g_hash_table_lookup(buddy->groups, group_name));
327 } else {
328 /* just return the first entry */
329 GHashTableIter iter;
330 gpointer value;
331 g_hash_table_iter_init(&iter, buddy->groups);
332 g_hash_table_iter_next(&iter, NULL, &value);
333 return(value);
337 static GSList *buddy_add_all(struct telepathy_buddy *buddy, GSList *list)
339 GHashTableIter iter;
340 struct telepathy_buddy_entry *buddy_entry;
342 if (!buddy)
343 return(list);
345 g_hash_table_iter_init(&iter, buddy->groups);
346 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy_entry))
347 list = g_slist_prepend(list, buddy_entry);
349 return(list);
352 GSList *sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
353 const gchar *buddy_name,
354 const gchar *group_name)
356 GSList *result = NULL;
358 /* NOTE: group_name != NULL not implemented in purple either */
359 if (!group_name) {
360 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
361 GHashTable *buddies = telepathy_private->contact_list->buddies;
363 if (buddy_name) {
364 result = buddy_add_all(g_hash_table_lookup(buddies,
365 buddy_name),
366 result);
367 } else {
368 GHashTableIter biter;
369 struct telepathy_buddy *buddy;
371 g_hash_table_iter_init(&biter, telepathy_private->contact_list->buddies);
372 while (g_hash_table_iter_next(&biter, NULL, (gpointer) &buddy))
373 result = buddy_add_all(buddy, result);
377 return(result);
380 gchar *sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
381 const sipe_backend_buddy who)
383 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->uri));
386 gchar *sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
387 const sipe_backend_buddy who)
389 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->alias));
392 gchar *sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
393 const sipe_backend_buddy who)
395 return(g_strdup(((struct telepathy_buddy_entry *) who)->group));
398 guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public,
399 const gchar *uri)
401 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
402 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
403 uri);
405 if (!buddy)
406 return(SIPE_ACTIVITY_UNSET);
407 return(buddy->activity);
410 void sipe_backend_buddy_set_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
411 const sipe_backend_buddy who,
412 const gchar *alias)
414 struct telepathy_buddy_entry *buddy_entry = who;
415 update_alias(buddy_entry->buddy, alias);
417 /* @TODO: emit signal? */
420 void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public)
422 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
423 SipeContactList *contact_list = telepathy_private->contact_list;
425 if (!contact_list->initial_received) {
426 /* we can only call this once */
427 contact_list->initial_received = TRUE;
428 SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called");
429 tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list));
433 static void buddy_free(gpointer data)
435 struct telepathy_buddy *buddy = data;
436 g_hash_table_destroy(buddy->groups);
437 g_free(buddy->alias);
438 g_free(buddy);
441 sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public,
442 const gchar *name,
443 const gchar *alias,
444 const gchar *group_name)
446 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
447 SipeContactList *contact_list = telepathy_private->contact_list;
448 const gchar *group = g_hash_table_lookup(contact_list->groups,
449 group_name);
450 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
451 name);
452 struct telepathy_buddy_entry *buddy_entry;
454 if (!group)
455 return(NULL);
457 if (!buddy) {
458 buddy = g_new0(struct telepathy_buddy, 1);
459 buddy->uri = g_strdup(name); /* reused as key */
460 buddy->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
461 NULL, g_free);
462 buddy->alias = g_strdup(alias);
463 buddy->activity = SIPE_ACTIVITY_OFFLINE;
464 buddy->handle = tp_handle_ensure(contact_list->contact_repo,
465 buddy->uri, NULL, NULL);
466 tp_handle_set_add(contact_list->contacts, buddy->handle);
467 g_hash_table_insert(contact_list->buddies,
468 (gchar *) buddy->uri, /* owned by hash table */
469 buddy);
470 g_hash_table_insert(contact_list->buddy_handles,
471 GUINT_TO_POINTER(buddy->handle),
472 buddy);
475 buddy_entry = g_hash_table_lookup(buddy->groups, group);
476 if (!buddy_entry) {
477 buddy_entry = g_new0(struct telepathy_buddy_entry, 1);
478 buddy_entry->buddy = buddy;
479 buddy_entry->group = group;
480 g_hash_table_insert(buddy->groups,
481 (gchar *) group, /* key is borrowed */
482 buddy_entry);
485 if (contact_list->initial_received) {
486 /* @TODO: emit signal? */
489 return(buddy_entry);
492 void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public,
493 const sipe_backend_buddy who)
495 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
496 SipeContactList *contact_list = telepathy_private->contact_list;
497 struct telepathy_buddy_entry *remove_entry = who;
498 struct telepathy_buddy *buddy = remove_entry->buddy;
500 g_hash_table_remove(buddy->groups,
501 remove_entry->group);
502 /* remove_entry is invalid */
504 if (g_hash_table_size(buddy->groups) == 0) {
505 /* removed from last group -> drop this buddy */
506 tp_handle_set_remove(contact_list->contacts,
507 buddy->handle);
508 g_hash_table_remove(contact_list->buddy_handles,
509 GUINT_TO_POINTER(buddy->handle));
510 g_hash_table_remove(contact_list->buddies,
511 buddy->uri);
515 if (contact_list->initial_received) {
516 /* @TODO: emit signal? */
520 void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public,
521 const gchar *uri,
522 guint activity)
524 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
525 SipeContactList *contact_list = telepathy_private->contact_list;
526 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
527 uri);
528 TpPresenceStatus *status;
530 if (!buddy)
531 return;
532 buddy->activity = activity;
534 SIPE_DEBUG_INFO("sipe_backend_buddy_set_status: %s to %d", uri, activity);
536 /* emit status update signal */
537 status = tp_presence_status_new(activity, NULL);
538 tp_presence_mixin_emit_one_presence_update(G_OBJECT(telepathy_private->connection),
539 buddy->handle, status);
540 tp_presence_status_free(status);
543 gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public,
544 const gchar *group_name)
546 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
547 SipeContactList *contact_list = telepathy_private->contact_list;
548 gchar *group = g_hash_table_lookup(contact_list->groups,
549 group_name);
551 if (!group) {
552 group = g_strdup(group_name);
553 g_hash_table_insert(contact_list->groups, group, group);
554 tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list),
555 &group_name,
559 return(group != NULL);
564 Local Variables:
565 mode: c
566 c-file-style: "bsd"
567 indent-tabs-mode: t
568 tab-width: 8
569 End: