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
);
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
));
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
,
319 return(g_hash_table_lookup(buddy
->groups
, group_name
));
321 /* just return the first entry */
324 g_hash_table_iter_init(&iter
, buddy
->groups
);
325 g_hash_table_iter_next(&iter
, NULL
, &value
);
330 static GSList
*buddy_add_all(struct telepathy_buddy
*buddy
, GSList
*list
)
333 struct telepathy_buddy_entry
*buddy_entry
;
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
);
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 */
353 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
354 GHashTable
*buddies
= telepathy_private
->contact_list
->buddies
;
357 result
= buddy_add_all(g_hash_table_lookup(buddies
,
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
);
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
,
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
,
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
,
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
);
434 sipe_backend_buddy
sipe_backend_buddy_add(struct sipe_core_public
*sipe_public
,
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
,
443 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddies
,
445 struct telepathy_buddy_entry
*buddy_entry
;
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
,
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 */
463 g_hash_table_insert(contact_list
->buddy_handles
,
464 GUINT_TO_POINTER(buddy
->handle
),
468 buddy_entry
= g_hash_table_lookup(buddy
->groups
, group
);
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 */
478 if (contact_list
->initial_received
) {
479 /* @TODO: emit signal? */
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
,
501 g_hash_table_remove(contact_list
->buddy_handles
,
502 GUINT_TO_POINTER(buddy
->handle
));
503 g_hash_table_remove(contact_list
->buddies
,
508 if (contact_list
->initial_received
) {
509 /* @TODO: emit signal? */
513 void sipe_backend_buddy_set_status(struct sipe_core_public
*sipe_public
,
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
,
521 TpPresenceStatus
*status
;
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
,
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
),
552 return(group
!= NULL
);