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
; /* key: group name, value: buddy_entry */
37 /* keys are borrowed from contact_list->groups */
39 /* @TODO: server vs. local alias - what do we need to support? */
44 struct telepathy_buddy_entry
{
45 struct telepathy_buddy
*buddy
; /* pointer to parent */
46 const gchar
*group
; /* borrowed from contact_list->groups key */
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
;
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, \
83 * Contact List class - type definition
85 static void contact_group_list_iface_init(TpContactGroupListInterface
*);
86 G_DEFINE_TYPE_WITH_CODE(SipeContactList
,
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
)
110 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
113 *subscribe
= TP_SUBSCRIPTION_STATE_YES
;
115 *publish
= TP_SUBSCRIPTION_STATE_YES
;
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
;
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
);
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
,
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
,
180 self
->initial_received
= FALSE
;
184 * Contact List class - interface implementation
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);
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
);
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
);
224 static GStrv
dup_contact_groups(TpBaseContactList
*contact_list
,
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");
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
,
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
,
265 /* get & set alias for a contact */
266 const gchar
*sipe_telepathy_buddy_get_alias(SipeContactList
*contact_list
,
269 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddy_handles
,
270 GUINT_TO_POINTER(contact
));
273 return(buddy
->alias
);
276 static void update_alias(struct telepathy_buddy
*buddy
,
280 g_free(buddy
->alias
);
281 buddy
->alias
= g_strdup(alias
);
285 void sipe_telepathy_buddy_set_alias(SipeContactList
*contact_list
,
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 */
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,
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
));
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
,
326 return(g_hash_table_lookup(buddy
->groups
, group_name
));
328 /* just return the first entry */
331 g_hash_table_iter_init(&iter
, buddy
->groups
);
332 g_hash_table_iter_next(&iter
, NULL
, &value
);
337 static GSList
*buddy_add_all(struct telepathy_buddy
*buddy
, GSList
*list
)
340 struct telepathy_buddy_entry
*buddy_entry
;
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
);
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 */
360 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
361 GHashTable
*buddies
= telepathy_private
->contact_list
->buddies
;
364 result
= buddy_add_all(g_hash_table_lookup(buddies
,
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
);
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
,
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
,
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
,
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
);
441 sipe_backend_buddy
sipe_backend_buddy_add(struct sipe_core_public
*sipe_public
,
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
,
450 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddies
,
452 struct telepathy_buddy_entry
*buddy_entry
;
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
,
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 */
470 g_hash_table_insert(contact_list
->buddy_handles
,
471 GUINT_TO_POINTER(buddy
->handle
),
475 buddy_entry
= g_hash_table_lookup(buddy
->groups
, group
);
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 */
485 if (contact_list
->initial_received
) {
486 /* @TODO: emit signal? */
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
,
508 g_hash_table_remove(contact_list
->buddy_handles
,
509 GUINT_TO_POINTER(buddy
->handle
));
510 g_hash_table_remove(contact_list
->buddies
,
515 if (contact_list
->initial_received
) {
516 /* @TODO: emit signal? */
520 void sipe_backend_buddy_set_status(struct sipe_core_public
*sipe_public
,
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
,
528 TpPresenceStatus
*status
;
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
,
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
),
559 return(group
!= NULL
);