telepathy: implement buddy status handling
[siplcs.git] / src / telepathy / telepathy-buddy.c
blob54b98a705616cc4120f3061a7387037c688f4887
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);
294 /* get presence status for a contact */
295 guint sipe_telepathy_buddy_get_presence(SipeContactList *contact_list,
296 const TpHandle contact)
298 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
299 GUINT_TO_POINTER(contact));
300 if (!buddy)
301 return(SIPE_ACTIVITY_UNSET);
302 return(buddy->activity);
306 * Backend adaptor functions
308 sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public,
309 const gchar *buddy_name,
310 const gchar *group_name)
312 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
313 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
314 buddy_name);
315 if (!buddy)
316 return(NULL);
318 if (group_name) {
319 return(g_hash_table_lookup(buddy->groups, group_name));
320 } else {
321 /* just return the first entry */
322 GHashTableIter iter;
323 gpointer value;
324 g_hash_table_iter_init(&iter, buddy->groups);
325 g_hash_table_iter_next(&iter, NULL, &value);
326 return(value);
330 static GSList *buddy_add_all(struct telepathy_buddy *buddy, GSList *list)
332 GHashTableIter iter;
333 struct telepathy_buddy_entry *buddy_entry;
335 if (!buddy)
336 return(list);
338 g_hash_table_iter_init(&iter, buddy->groups);
339 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy_entry))
340 list = g_slist_prepend(list, buddy_entry);
342 return(list);
345 GSList *sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
346 const gchar *buddy_name,
347 const gchar *group_name)
349 GSList *result = NULL;
351 /* NOTE: group_name != NULL not implemented in purple either */
352 if (!group_name) {
353 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
354 GHashTable *buddies = telepathy_private->contact_list->buddies;
356 if (buddy_name) {
357 result = buddy_add_all(g_hash_table_lookup(buddies,
358 buddy_name),
359 result);
360 } else {
361 GHashTableIter biter;
362 struct telepathy_buddy *buddy;
364 g_hash_table_iter_init(&biter, telepathy_private->contact_list->buddies);
365 while (g_hash_table_iter_next(&biter, NULL, (gpointer) &buddy))
366 result = buddy_add_all(buddy, result);
370 return(result);
373 gchar *sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
374 const sipe_backend_buddy who)
376 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->uri));
379 gchar *sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
380 const sipe_backend_buddy who)
382 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->alias));
385 gchar *sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
386 const sipe_backend_buddy who)
388 return(g_strdup(((struct telepathy_buddy_entry *) who)->group));
391 guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public,
392 const gchar *uri)
394 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
395 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
396 uri);
398 if (!buddy)
399 return(SIPE_ACTIVITY_UNSET);
400 return(buddy->activity);
403 void sipe_backend_buddy_set_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
404 const sipe_backend_buddy who,
405 const gchar *alias)
407 struct telepathy_buddy_entry *buddy_entry = who;
408 update_alias(buddy_entry->buddy, alias);
410 /* @TODO: emit signal? */
413 void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public)
415 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
416 SipeContactList *contact_list = telepathy_private->contact_list;
418 if (!contact_list->initial_received) {
419 /* we can only call this once */
420 contact_list->initial_received = TRUE;
421 SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called");
422 tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list));
426 static void buddy_free(gpointer data)
428 struct telepathy_buddy *buddy = data;
429 g_hash_table_destroy(buddy->groups);
430 g_free(buddy->alias);
431 g_free(buddy);
434 sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public,
435 const gchar *name,
436 const gchar *alias,
437 const gchar *group_name)
439 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
440 SipeContactList *contact_list = telepathy_private->contact_list;
441 const gchar *group = g_hash_table_lookup(contact_list->groups,
442 group_name);
443 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
444 name);
445 struct telepathy_buddy_entry *buddy_entry;
447 if (!group)
448 return(NULL);
450 if (!buddy) {
451 buddy = g_new0(struct telepathy_buddy, 1);
452 buddy->uri = g_strdup(name); /* reused as key */
453 buddy->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
454 NULL, g_free);
455 buddy->alias = g_strdup(alias);
456 buddy->activity = SIPE_ACTIVITY_OFFLINE;
457 buddy->handle = tp_handle_ensure(contact_list->contact_repo,
458 buddy->uri, NULL, NULL);
459 tp_handle_set_add(contact_list->contacts, buddy->handle);
460 g_hash_table_insert(contact_list->buddies,
461 (gchar *) buddy->uri, /* owned by hash table */
462 buddy);
463 g_hash_table_insert(contact_list->buddy_handles,
464 GUINT_TO_POINTER(buddy->handle),
465 buddy);
468 buddy_entry = g_hash_table_lookup(buddy->groups, group);
469 if (!buddy_entry) {
470 buddy_entry = g_new0(struct telepathy_buddy_entry, 1);
471 buddy_entry->buddy = buddy;
472 buddy_entry->group = group;
473 g_hash_table_insert(buddy->groups,
474 (gchar *) group, /* key is borrowed */
475 buddy_entry);
478 if (contact_list->initial_received) {
479 /* @TODO: emit signal? */
482 return(buddy_entry);
485 void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public,
486 const sipe_backend_buddy who)
488 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
489 SipeContactList *contact_list = telepathy_private->contact_list;
490 struct telepathy_buddy_entry *remove_entry = who;
491 struct telepathy_buddy *buddy = remove_entry->buddy;
493 g_hash_table_remove(buddy->groups,
494 remove_entry->group);
495 /* remove_entry is invalid */
497 if (g_hash_table_size(buddy->groups) == 0) {
498 /* removed from last group -> drop this buddy */
499 tp_handle_set_remove(contact_list->contacts,
500 buddy->handle);
501 g_hash_table_remove(contact_list->buddy_handles,
502 GUINT_TO_POINTER(buddy->handle));
503 g_hash_table_remove(contact_list->buddies,
504 buddy->uri);
508 if (contact_list->initial_received) {
509 /* @TODO: emit signal? */
513 void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public,
514 const gchar *uri,
515 guint activity)
517 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
518 SipeContactList *contact_list = telepathy_private->contact_list;
519 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
520 uri);
521 TpPresenceStatus *status;
523 if (!buddy)
524 return;
525 buddy->activity = activity;
527 SIPE_DEBUG_INFO("sipe_backend_buddy_set_status: %s to %d", uri, activity);
529 /* emit status update signal */
530 status = tp_presence_status_new(activity, NULL);
531 tp_presence_mixin_emit_one_presence_update(G_OBJECT(telepathy_private->connection),
532 buddy->handle, status);
533 tp_presence_status_free(status);
536 gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public,
537 const gchar *group_name)
539 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
540 SipeContactList *contact_list = telepathy_private->contact_list;
541 gchar *group = g_hash_table_lookup(contact_list->groups,
542 group_name);
544 if (!group) {
545 group = g_strdup(group_name);
546 g_hash_table_insert(contact_list->groups, group, group);
547 tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list),
548 &group_name,
552 return(group != NULL);
557 Local Variables:
558 mode: c
559 c-file-style: "bsd"
560 indent-tabs-mode: t
561 tab-width: 8
562 End: