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 */
38 /* @TODO: server vs. local alias - what do we need to support? */
42 struct telepathy_buddy_entry
{
43 struct telepathy_buddy
*buddy
; /* pointer to parent */
44 const gchar
*group
; /* borrowed from contact_list->groups key */
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
;
63 GHashTable
*buddy_handles
;
66 gboolean initial_received
;
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, \
81 * Contact List class - type definition
83 static void contact_group_list_iface_init(TpContactGroupListInterface
*);
84 G_DEFINE_TYPE_WITH_CODE(SipeContactList
,
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
)
108 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
111 *subscribe
= TP_SUBSCRIPTION_STATE_YES
;
113 *publish
= TP_SUBSCRIPTION_STATE_YES
;
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
;
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
);
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
,
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
,
178 self
->initial_received
= FALSE
;
182 * Contact List class - interface implementation
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);
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
);
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
);
222 static GStrv
dup_contact_groups(TpBaseContactList
*contact_list
,
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");
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
,
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
,
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
));
271 return(buddy
->alias
);
274 static void update_alias(struct telepathy_buddy
*buddy
,
278 g_free(buddy
->alias
);
279 buddy
->alias
= g_strdup(alias
);
283 void sipe_telepathy_buddy_set_alias(struct _SipeContactList
*contact_list
,
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
,
306 return(g_hash_table_lookup(buddy
->groups
, group_name
));
308 /* just return the first entry */
311 g_hash_table_iter_init(&iter
, buddy
->groups
);
312 g_hash_table_iter_next(&iter
, NULL
, &value
);
317 static GSList
*buddy_add_all(struct telepathy_buddy
*buddy
, GSList
*list
)
320 struct telepathy_buddy_entry
*buddy_entry
;
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
);
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 */
340 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
341 GHashTable
*buddies
= telepathy_private
->contact_list
->buddies
;
344 result
= buddy_add_all(g_hash_table_lookup(buddies
,
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
);
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
,
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
);
409 sipe_backend_buddy
sipe_backend_buddy_add(struct sipe_core_public
*sipe_public
,
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
,
418 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddies
,
420 struct telepathy_buddy_entry
*buddy_entry
;
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
,
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 */
437 g_hash_table_insert(contact_list
->buddy_handles
,
438 GUINT_TO_POINTER(buddy
->handle
),
442 buddy_entry
= g_hash_table_lookup(buddy
->groups
, group
);
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 */
452 if (contact_list
->initial_received
) {
453 /* @TODO: emit signal? */
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
,
475 g_hash_table_remove(contact_list
->buddy_handles
,
476 GUINT_TO_POINTER(buddy
->handle
));
477 g_hash_table_remove(contact_list
->buddies
,
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
,
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
),
503 return(group
!= NULL
);