2 * @file telepathy-buddy.c
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 */
40 struct telepathy_buddy_entry
{
41 const gchar
*uri
; /* borrowed from telepathy_buddy->uri */
42 const gchar
*group
; /* borrowed from contact_list->groups key */
48 * Contact List class - data structures
50 typedef struct _SipeContactListClass
{
51 TpBaseContactListClass parent_class
;
52 } SipeContactListClass
;
54 typedef struct _SipeContactList
{
55 TpBaseContactList parent
;
57 TpBaseConnection
*connection
;
58 TpHandleRepoIface
*contact_repo
;
59 TpHandleSet
*contacts
;
62 GHashTable
*buddy_handles
;
65 gboolean initial_received
;
69 * Contact List class - type macros
71 static GType
sipe_contact_list_get_type(void) G_GNUC_CONST
;
72 #define SIPE_TYPE_CONTACT_LIST \
73 (sipe_contact_list_get_type())
74 #define SIPE_CONTACT_LIST(obj) \
75 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONTACT_LIST, \
80 * Contact List class - type definition
82 static void contact_group_list_iface_init(TpContactGroupListInterface
*);
83 G_DEFINE_TYPE_WITH_CODE(SipeContactList
,
85 TP_TYPE_BASE_CONTACT_LIST
,
86 G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST
,
87 contact_group_list_iface_init
);
92 * Contact List class - instance methods
94 static TpHandleSet
*dup_contacts(TpBaseContactList
*contact_list
)
96 SipeContactList
*self
= SIPE_CONTACT_LIST(contact_list
);
97 return(tp_handle_set_copy(self
->contacts
));
100 static void dup_states(SIPE_UNUSED_PARAMETER TpBaseContactList
*contact_list
,
101 SIPE_UNUSED_PARAMETER TpHandle contact
,
102 TpSubscriptionState
*subscribe
,
103 TpSubscriptionState
*publish
,
104 gchar
**publish_request
)
107 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
110 *subscribe
= TP_SUBSCRIPTION_STATE_YES
;
112 *publish
= TP_SUBSCRIPTION_STATE_YES
;
114 *publish_request
= g_strdup("");
117 static void sipe_contact_list_constructed(GObject
*object
)
119 SipeContactList
*self
= SIPE_CONTACT_LIST(object
);
120 void (*chain_up
)(GObject
*) = G_OBJECT_CLASS(sipe_contact_list_parent_class
)->constructed
;
125 g_object_get(self
, "connection", &self
->connection
, NULL
);
126 self
->contact_repo
= tp_base_connection_get_handles(self
->connection
,
127 TP_HANDLE_TYPE_CONTACT
);
128 self
->contacts
= tp_handle_set_new(self
->contact_repo
);
131 static void sipe_contact_list_dispose(GObject
*object
)
133 SipeContactList
*self
= SIPE_CONTACT_LIST(object
);
134 void (*chain_up
)(GObject
*) = G_OBJECT_CLASS(sipe_contact_list_parent_class
)->dispose
;
136 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dispose");
138 tp_clear_pointer(&self
->contacts
, tp_handle_set_destroy
);
139 tp_clear_object(&self
->connection
);
140 /* NOTE: the order is important due to borrowing of keys! */
141 tp_clear_pointer(&self
->buddy_handles
, g_hash_table_unref
);
142 tp_clear_pointer(&self
->buddies
, g_hash_table_unref
);
143 tp_clear_pointer(&self
->groups
, g_hash_table_unref
);
150 * Contact List class - type implementation
152 static void sipe_contact_list_class_init(SipeContactListClass
*klass
)
154 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
155 TpBaseContactListClass
*base_class
= TP_BASE_CONTACT_LIST_CLASS(klass
);
157 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::class_init");
159 object_class
->constructed
= sipe_contact_list_constructed
;
160 object_class
->dispose
= sipe_contact_list_dispose
;
162 base_class
->dup_contacts
= dup_contacts
;
163 base_class
->dup_states
= dup_states
;
166 static void buddy_free(gpointer data
);
167 static void sipe_contact_list_init(SIPE_UNUSED_PARAMETER SipeContactList
*self
)
169 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::init");
171 self
->buddies
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
173 self
->buddy_handles
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
174 self
->groups
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
177 self
->initial_received
= FALSE
;
181 * Contact List class - interface implementation
185 static GStrv
dup_groups(TpBaseContactList
*contact_list
)
187 SipeContactList
*self
= SIPE_CONTACT_LIST(contact_list
);
188 GPtrArray
*groups
= g_ptr_array_sized_new(
189 g_hash_table_size(self
->groups
) + 1);
193 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_groups called");
195 g_hash_table_iter_init(&iter
, self
->groups
);
196 while (g_hash_table_iter_next(&iter
, &name
, NULL
))
197 g_ptr_array_add(groups
, g_strdup(name
));
198 g_ptr_array_add(groups
, NULL
);
200 return((GStrv
) g_ptr_array_free(groups
, FALSE
));
203 static TpHandleSet
*dup_group_members(TpBaseContactList
*contact_list
,
204 const gchar
*group_name
)
206 SipeContactList
*self
= SIPE_CONTACT_LIST(contact_list
);
207 TpHandleSet
*members
= tp_handle_set_new(self
->contact_repo
);
209 struct telepathy_buddy
*buddy
;
211 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_group_members called");
213 g_hash_table_iter_init(&iter
, self
->buddies
);
214 while (g_hash_table_iter_next(&iter
, NULL
, (gpointer
) &buddy
))
215 if (g_hash_table_lookup(buddy
->groups
, group_name
))
216 tp_handle_set_add(members
, buddy
->handle
);
221 static GStrv
dup_contact_groups(TpBaseContactList
*contact_list
,
224 SipeContactList
*self
= SIPE_CONTACT_LIST(contact_list
);
225 GPtrArray
*groups
= g_ptr_array_sized_new(
226 g_hash_table_size(self
->groups
) + 1);
227 struct telepathy_buddy
*buddy
= g_hash_table_lookup(self
->buddy_handles
,
228 GUINT_TO_POINTER(contact
));
230 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_contact_groups called");
234 const gchar
*group_name
;
236 g_hash_table_iter_init(&iter
, buddy
->groups
);
237 while (g_hash_table_iter_next(&iter
,
238 (gpointer
) &group_name
,
240 g_ptr_array_add(groups
, g_strdup(group_name
));
242 g_ptr_array_add(groups
, NULL
);
244 return((GStrv
) g_ptr_array_free(groups
, FALSE
));
247 static void contact_group_list_iface_init(TpContactGroupListInterface
*iface
)
249 iface
->dup_groups
= dup_groups
;
250 iface
->dup_group_members
= dup_group_members
;
251 iface
->dup_contact_groups
= dup_contact_groups
;
254 /* create new contact list object */
255 SipeContactList
*sipe_telepathy_contact_list_new(TpBaseConnection
*connection
)
257 return(g_object_new(SIPE_TYPE_CONTACT_LIST
,
258 "connection", connection
,
263 * Backend adaptor functions
265 sipe_backend_buddy
sipe_backend_buddy_find(struct sipe_core_public
*sipe_public
,
266 const gchar
*buddy_name
,
267 const gchar
*group_name
)
269 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
270 struct telepathy_buddy
*buddy
= g_hash_table_lookup(telepathy_private
->contact_list
->buddies
,
276 return(g_hash_table_lookup(buddy
->groups
, group_name
));
278 /* just return the first entry */
281 g_hash_table_iter_init(&iter
, buddy
->groups
);
282 g_hash_table_iter_next(&iter
, NULL
, &value
);
287 static GSList
*buddy_add_all(struct telepathy_buddy
*buddy
, GSList
*list
)
290 struct telepathy_buddy_entry
*buddy_entry
;
295 g_hash_table_iter_init(&iter
, buddy
->groups
);
296 while (g_hash_table_iter_next(&iter
, NULL
, (gpointer
) &buddy_entry
))
297 list
= g_slist_prepend(list
, buddy_entry
);
302 GSList
*sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
,
303 const gchar
*buddy_name
,
304 const gchar
*group_name
)
306 GSList
*result
= NULL
;
308 /* NOTE: group_name != NULL not implemented in purple either */
310 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
311 GHashTable
*buddies
= telepathy_private
->contact_list
->buddies
;
314 result
= buddy_add_all(g_hash_table_lookup(buddies
,
318 GHashTableIter biter
;
319 struct telepathy_buddy
*buddy
;
321 g_hash_table_iter_init(&biter
, telepathy_private
->contact_list
->buddies
);
322 while (g_hash_table_iter_next(&biter
, NULL
, (gpointer
) &buddy
))
323 result
= buddy_add_all(buddy
, result
);
330 gchar
*sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
,
331 const sipe_backend_buddy who
)
333 return(g_strdup(((struct telepathy_buddy_entry
*) who
)->uri
));
336 gchar
*sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
,
337 const sipe_backend_buddy who
)
339 return(g_strdup(((struct telepathy_buddy_entry
*) who
)->alias
));
342 gchar
*sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
,
343 const sipe_backend_buddy who
)
345 return(g_strdup(((struct telepathy_buddy_entry
*) who
)->group
));
348 void sipe_backend_buddy_set_alias(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
,
349 const sipe_backend_buddy who
,
352 struct telepathy_buddy_entry
*buddy_entry
= who
;
353 g_free(buddy_entry
->alias
);
354 buddy_entry
->alias
= g_strdup(alias
);
356 /* @TODO: emit signal? */
359 void sipe_backend_buddy_list_processing_finish(struct sipe_core_public
*sipe_public
)
361 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
362 SipeContactList
*contact_list
= telepathy_private
->contact_list
;
364 if (!contact_list
->initial_received
) {
365 /* we can only call this once */
366 contact_list
->initial_received
= TRUE
;
367 SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called");
368 tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list
));
372 static void buddy_free(gpointer data
)
374 struct telepathy_buddy
*buddy
= data
;
375 g_hash_table_destroy(buddy
->groups
);
379 static void buddy_entry_free(gpointer data
)
381 struct telepathy_buddy_entry
*buddy_entry
= data
;
382 g_free(buddy_entry
->alias
);
386 sipe_backend_buddy
sipe_backend_buddy_add(struct sipe_core_public
*sipe_public
,
389 const gchar
*group_name
)
391 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
392 SipeContactList
*contact_list
= telepathy_private
->contact_list
;
393 const gchar
*group
= g_hash_table_lookup(contact_list
->groups
,
395 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddies
,
397 struct telepathy_buddy_entry
*buddy_entry
;
403 buddy
= g_new0(struct telepathy_buddy
, 1);
404 buddy
->uri
= g_strdup(name
); /* reused as key */
405 buddy
->groups
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
406 NULL
, buddy_entry_free
);
407 buddy
->handle
= tp_handle_ensure(contact_list
->contact_repo
,
408 buddy
->uri
, NULL
, NULL
);
409 tp_handle_set_add(contact_list
->contacts
, buddy
->handle
);
410 g_hash_table_insert(contact_list
->buddies
,
411 (gchar
*) buddy
->uri
, /* owned by hash table */
413 g_hash_table_insert(contact_list
->buddy_handles
,
414 GUINT_TO_POINTER(buddy
->handle
),
418 buddy_entry
= g_hash_table_lookup(buddy
->groups
, group
);
420 buddy_entry
= g_new0(struct telepathy_buddy_entry
, 1);
421 buddy_entry
->uri
= buddy
->uri
;
422 buddy_entry
->group
= group
;
423 buddy_entry
->alias
= g_strdup(alias
);
424 g_hash_table_insert(buddy
->groups
,
425 (gchar
*) group
, /* key is borrowed */
429 if (contact_list
->initial_received
) {
430 /* @TODO: emit signal? */
436 void sipe_backend_buddy_remove(struct sipe_core_public
*sipe_public
,
437 const sipe_backend_buddy who
)
439 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
440 SipeContactList
*contact_list
= telepathy_private
->contact_list
;
441 struct telepathy_buddy_entry
*remove_entry
= who
;
442 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddies
,
446 struct telepathy_buddy_entry
*buddy_entry
= g_hash_table_lookup(buddy
->groups
,
447 remove_entry
->group
);
449 if (buddy_entry
== remove_entry
) {
450 /* match found -> remove this entry */
451 g_hash_table_remove(buddy
->groups
,
452 remove_entry
->group
);
454 if (g_hash_table_size(buddy
->groups
) == 0) {
455 /* removed from last group -> drop this buddy */
456 tp_handle_set_remove(contact_list
->contacts
,
458 g_hash_table_remove(contact_list
->buddy_handles
,
459 GUINT_TO_POINTER(buddy
->handle
));
460 g_hash_table_remove(contact_list
->buddies
,
465 if (contact_list
->initial_received
) {
466 /* @TODO: emit signal? */
472 gboolean
sipe_backend_buddy_group_add(struct sipe_core_public
*sipe_public
,
473 const gchar
*group_name
)
475 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
476 SipeContactList
*contact_list
= telepathy_private
->contact_list
;
477 gchar
*group
= g_hash_table_lookup(contact_list
->groups
,
481 group
= g_strdup(group_name
);
482 g_hash_table_insert(contact_list
->groups
, group
, group
);
483 tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list
),
488 return(group
!= NULL
);