purple: work around broken dbus-server.h
[siplcs.git] / src / telepathy / telepathy-buddy.c
blobf30d1cfbae182b93b181c631dbe0e46614fdd60e
1 /**
2 * @file telepathy-buddy.c
4 * pidgin-sipe
6 * Copyright (C) 2012-2017 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 <string.h>
25 #include <glib-object.h>
26 #include <glib/gstdio.h>
27 #include <telepathy-glib/base-connection.h>
28 #include <telepathy-glib/base-contact-list.h>
29 #include <telepathy-glib/telepathy-glib.h>
31 #include "sipe-backend.h"
32 #include "sipe-common.h"
33 #include "sipe-core.h"
35 #include "telepathy-private.h"
37 #define SIPE_INFO_FIELD_MAX (SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY + 1)
39 struct telepathy_buddy {
40 const gchar *uri; /* borrowed from contact_list->buddies key */
41 GHashTable *groups; /* key: group name, value: buddy_entry */
42 /* keys are borrowed from contact_list->groups */
43 TpHandle handle;
44 /* includes alias as stored on the server */
45 gchar *info[SIPE_INFO_FIELD_MAX];
46 gchar *hash; /* photo hash */
47 guint activity;
50 struct telepathy_buddy_entry {
51 struct telepathy_buddy *buddy; /* pointer to parent */
52 const gchar *group; /* borrowed from contact_list->groups key */
55 G_BEGIN_DECLS
57 * Contact List class - data structures
59 typedef struct _SipeContactListClass {
60 TpBaseContactListClass parent_class;
61 } SipeContactListClass;
63 typedef struct _SipeContactList {
64 TpBaseContactList parent;
66 TpBaseConnection *connection;
67 TpHandleRepoIface *contact_repo;
68 TpHandleSet *contacts;
70 GHashTable *buddies; /* key: SIP URI, value: buddy */
71 GHashTable *buddy_handles; /* key: TpHandle, value: buddy */
72 GHashTable *groups; /* key: group name, value: buddy */
74 gboolean initial_received;
75 } SipeContactList;
78 * Contact List class - type macros
80 static GType sipe_contact_list_get_type(void) G_GNUC_CONST;
81 #define SIPE_TYPE_CONTACT_LIST \
82 (sipe_contact_list_get_type())
83 #define SIPE_CONTACT_LIST(obj) \
84 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONTACT_LIST, \
85 SipeContactList))
86 G_END_DECLS
89 * Contact List class - type definition
91 static void contact_group_list_iface_init(TpContactGroupListInterface *);
92 G_DEFINE_TYPE_WITH_CODE(SipeContactList,
93 sipe_contact_list,
94 TP_TYPE_BASE_CONTACT_LIST,
95 G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST,
96 contact_group_list_iface_init);
101 * Contact List class - instance methods
103 static TpHandleSet *dup_contacts(TpBaseContactList *contact_list)
105 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
106 return(tp_handle_set_copy(self->contacts));
109 static void dup_states(SIPE_UNUSED_PARAMETER TpBaseContactList *contact_list,
110 SIPE_UNUSED_PARAMETER TpHandle contact,
111 TpSubscriptionState *subscribe,
112 TpSubscriptionState *publish,
113 gchar **publish_request)
115 /* @TODO */
116 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
118 if (subscribe)
119 *subscribe = TP_SUBSCRIPTION_STATE_YES;
120 if (publish)
121 *publish = TP_SUBSCRIPTION_STATE_YES;
122 if (publish_request)
123 *publish_request = g_strdup("");
126 static void sipe_contact_list_constructed(GObject *object)
128 SipeContactList *self = SIPE_CONTACT_LIST(object);
129 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->constructed;
131 if (chain_up)
132 chain_up(object);
134 g_object_get(self, "connection", &self->connection, NULL);
135 self->contact_repo = tp_base_connection_get_handles(self->connection,
136 TP_HANDLE_TYPE_CONTACT);
137 self->contacts = tp_handle_set_new(self->contact_repo);
140 static void sipe_contact_list_dispose(GObject *object)
142 SipeContactList *self = SIPE_CONTACT_LIST(object);
143 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->dispose;
145 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dispose");
147 tp_clear_pointer(&self->contacts, tp_handle_set_destroy);
148 tp_clear_object(&self->connection);
149 /* NOTE: the order is important due to borrowing of keys! */
150 tp_clear_pointer(&self->buddy_handles, g_hash_table_unref);
151 tp_clear_pointer(&self->buddies, g_hash_table_unref);
152 tp_clear_pointer(&self->groups, g_hash_table_unref);
154 if (chain_up)
155 chain_up(object);
159 * Contact List class - type implementation
161 static void sipe_contact_list_class_init(SipeContactListClass *klass)
163 GObjectClass *object_class = G_OBJECT_CLASS(klass);
164 TpBaseContactListClass *base_class = TP_BASE_CONTACT_LIST_CLASS(klass);
166 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::class_init");
168 object_class->constructed = sipe_contact_list_constructed;
169 object_class->dispose = sipe_contact_list_dispose;
171 base_class->dup_contacts = dup_contacts;
172 base_class->dup_states = dup_states;
175 static void buddy_free(gpointer data);
176 static void sipe_contact_list_init(SIPE_UNUSED_PARAMETER SipeContactList *self)
178 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::init");
180 self->buddies = g_hash_table_new_full(g_str_hash, g_str_equal,
181 g_free, buddy_free);
182 self->buddy_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
183 self->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
184 g_free, NULL);
186 self->initial_received = FALSE;
190 * Contact List class - interface implementation
192 * Contact groups
194 static GStrv dup_groups(TpBaseContactList *contact_list)
196 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
197 GPtrArray *groups = g_ptr_array_sized_new(
198 g_hash_table_size(self->groups) + 1);
199 GHashTableIter iter;
200 gpointer name;
202 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_groups called");
204 g_hash_table_iter_init(&iter, self->groups);
205 while (g_hash_table_iter_next(&iter, &name, NULL))
206 g_ptr_array_add(groups, g_strdup(name));
207 g_ptr_array_add(groups, NULL);
209 return((GStrv) g_ptr_array_free(groups, FALSE));
212 static TpHandleSet *dup_group_members(TpBaseContactList *contact_list,
213 const gchar *group_name)
215 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
216 TpHandleSet *members = tp_handle_set_new(self->contact_repo);
217 GHashTableIter iter;
218 struct telepathy_buddy *buddy;
220 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_group_members called");
222 g_hash_table_iter_init(&iter, self->buddies);
223 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy))
224 if (g_hash_table_lookup(buddy->groups, group_name))
225 tp_handle_set_add(members, buddy->handle);
227 return(members);
230 static GStrv dup_contact_groups(TpBaseContactList *contact_list,
231 TpHandle contact)
233 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
234 GPtrArray *groups = g_ptr_array_sized_new(
235 g_hash_table_size(self->groups) + 1);
236 struct telepathy_buddy *buddy = g_hash_table_lookup(self->buddy_handles,
237 GUINT_TO_POINTER(contact));
239 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_contact_groups called");
241 if (buddy) {
242 GHashTableIter iter;
243 const gchar *group_name;
245 g_hash_table_iter_init(&iter, buddy->groups);
246 while (g_hash_table_iter_next(&iter,
247 (gpointer) &group_name,
248 NULL))
249 g_ptr_array_add(groups, g_strdup(group_name));
251 g_ptr_array_add(groups, NULL);
253 return((GStrv) g_ptr_array_free(groups, FALSE));
256 static void contact_group_list_iface_init(TpContactGroupListInterface *iface)
258 #define IMPLEMENT(x) iface->x = x
259 IMPLEMENT(dup_groups);
260 IMPLEMENT(dup_group_members);
261 IMPLEMENT(dup_contact_groups);
262 #undef IMPLEMENT
265 /* create new contact list object */
266 SipeContactList *sipe_telepathy_contact_list_new(TpBaseConnection *connection)
268 return(g_object_new(SIPE_TYPE_CONTACT_LIST,
269 "connection", connection,
270 NULL));
273 /* get & set alias for a contact */
274 const gchar *sipe_telepathy_buddy_get_alias(SipeContactList *contact_list,
275 TpHandle contact)
277 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
278 GUINT_TO_POINTER(contact));
279 if (!buddy)
280 return(NULL);
281 return(buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]);
284 static void update_alias(struct telepathy_buddy *buddy,
285 const gchar *alias)
287 if (buddy) {
288 g_free(buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]);
289 buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME] = g_strdup(alias);
293 void sipe_telepathy_buddy_set_alias(SipeContactList *contact_list,
294 const guint contact,
295 const gchar *alias)
297 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
298 GUINT_TO_POINTER(contact));
299 update_alias(buddy, alias);
301 /* tell core about the alias change */
302 if (buddy) {
303 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(contact_list->connection));
304 sipe_core_group_set_alias(telepathy_private->public,
305 buddy->uri,
306 alias);
310 /* get photo hash for a contact */
311 const gchar *sipe_telepathy_buddy_get_hash(struct _SipeContactList *contact_list,
312 const guint contact)
314 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
315 GUINT_TO_POINTER(contact));
316 if (!buddy)
317 return(NULL);
318 return(buddy->hash);
321 /* get presence status for a contact */
322 guint sipe_telepathy_buddy_get_presence(SipeContactList *contact_list,
323 const TpHandle contact)
325 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
326 GUINT_TO_POINTER(contact));
327 if (!buddy)
328 return(SIPE_ACTIVITY_UNSET);
329 return(buddy->activity);
332 /* @TODO: are other MIME types supported by OCS? */
333 static const char * mimetypes[] = {
334 "image/jpeg",
335 NULL
338 /* @TODO: are these correct or even needed? */
339 #define AVATAR_MIN_PX 16
340 #define AVATAR_MAX_PX 256
341 #define AVATAR_MAX_BYTES 32768
343 static void get_avatar_requirements(TpSvcConnectionInterfaceAvatars *iface,
344 DBusGMethodInvocation *context)
346 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(TP_BASE_CONNECTION(iface),
347 context);
349 tp_svc_connection_interface_avatars_return_from_get_avatar_requirements(
350 context,
351 mimetypes,
352 AVATAR_MIN_PX, AVATAR_MIN_PX,
353 AVATAR_MAX_PX, AVATAR_MAX_PX,
354 AVATAR_MAX_BYTES);
357 void sipe_telepathy_avatars_iface_init(gpointer g_iface,
358 SIPE_UNUSED_PARAMETER gpointer iface_data)
360 TpSvcConnectionInterfaceAvatarsClass *klass = g_iface;
362 #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x( \
363 klass, x)
364 IMPLEMENT(get_avatar_requirements);
365 /* Information is provided by server: can't implement
366 IMPLEMENT(get_avatar_tokens);
367 IMPLEMENT(get_known_avatar_tokens);
368 IMPLEMENT(request_avatar);
369 IMPLEMENT(request_avatars);
370 IMPLEMENT(set_avatar);
371 IMPLEMENT(clear_avatar); */
372 #undef IMPLEMENT
375 static const gchar *const sipe_to_vcard_field[SIPE_INFO_FIELD_MAX] = {
376 /* SIPE_BUDDY_INFO_DISPLAY_NAME */ "fn",
377 /* SIPE_BUDDY_INFO_JOB_TITLE */ "title",
378 /* SIPE_BUDDY_INFO_CITY */ NULL,
379 /* SIPE_BUDDY_INFO_STATE */ NULL,
380 /* SIPE_BUDDY_INFO_OFFICE */ NULL,
381 /* SIPE_BUDDY_INFO_DEPARTMENT */ NULL,
382 /* SIPE_BUDDY_INFO_COUNTRY */ NULL,
383 /* SIPE_BUDDY_INFO_WORK_PHONE */ "tel",
384 /* SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY */ NULL,
385 /* SIPE_BUDDY_INFO_COMPANY */ "org",
386 /* SIPE_BUDDY_INFO_EMAIL */ "email",
387 /* SIPE_BUDDY_INFO_SITE */ NULL,
388 /* SIPE_BUDDY_INFO_ZIPCODE */ NULL,
389 /* SIPE_BUDDY_INFO_STREET */ NULL,
390 /* SIPE_BUDDY_INFO_MOBILE_PHONE */ NULL,
391 /* SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY */ NULL,
392 /* SIPE_BUDDY_INFO_HOME_PHONE */ NULL,
393 /* SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY */ NULL,
394 /* SIPE_BUDDY_INFO_OTHER_PHONE */ NULL,
395 /* SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY */ NULL,
396 /* SIPE_BUDDY_INFO_CUSTOM1_PHONE */ NULL,
397 /* SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY */ NULL,
400 static GPtrArray *convert_contact_info(struct telepathy_buddy *buddy)
402 GPtrArray *info = NULL;
404 if (buddy) {
405 guint i;
407 info = dbus_g_type_specialized_construct(
408 TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
410 for (i = 0; i < SIPE_INFO_FIELD_MAX; i++) {
411 const gchar *name = sipe_to_vcard_field[i];
412 const gchar *value = buddy->info[i];
414 if (name && value) {
415 const gchar *const field_values[2] = { value, NULL };
417 SIPE_DEBUG_INFO("SipeContactInfo::convert_contact_info: %s: (%2d)%s = '%s'",
418 buddy->uri, i, name, value);
420 g_ptr_array_add(info,
421 tp_value_array_build(3,
422 G_TYPE_STRING, name,
423 G_TYPE_STRV, NULL,
424 G_TYPE_STRV, field_values,
425 G_TYPE_INVALID));
430 return(info);
433 static void get_contact_info(TpSvcConnectionInterfaceContactInfo *iface,
434 const GArray *contacts,
435 DBusGMethodInvocation *context)
437 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(iface));
438 GHashTable *buddies = telepathy_private->contact_list->buddy_handles;
439 TpBaseConnection *base = TP_BASE_CONNECTION(iface);
440 TpHandleRepoIface *repo = tp_base_connection_get_handles(base,
441 TP_HANDLE_TYPE_CONTACT);
442 GError *error = NULL;
443 GHashTable *infos;
444 guint i;
446 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
448 SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::get_contact_info called");
450 if (!tp_handles_are_valid(repo, contacts, FALSE, &error)) {
451 dbus_g_method_return_error(context, error);
452 g_error_free(error);
453 return;
456 infos = dbus_g_type_specialized_construct(TP_HASH_TYPE_CONTACT_INFO_MAP);
458 for (i = 0; i < contacts->len; i++) {
459 TpHandle contact = g_array_index(contacts, TpHandle, i);
460 struct telepathy_buddy *buddy = g_hash_table_lookup(buddies,
461 GUINT_TO_POINTER(contact));
462 GPtrArray *info = convert_contact_info(buddy);
464 if (info)
465 g_hash_table_insert(infos,
466 GUINT_TO_POINTER(contact),
467 info);
470 tp_svc_connection_interface_contact_info_return_from_get_contact_info(context,
471 infos);
472 g_boxed_free(TP_HASH_TYPE_CONTACT_INFO_MAP, infos);
475 static void request_contact_info(TpSvcConnectionInterfaceContactInfo *iface,
476 guint contact,
477 DBusGMethodInvocation *context)
479 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(iface));
480 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddy_handles,
481 GUINT_TO_POINTER(contact));
482 TpBaseConnection *base = TP_BASE_CONNECTION(iface);
483 TpHandleRepoIface *repo = tp_base_connection_get_handles(base,
484 TP_HANDLE_TYPE_CONTACT);
485 GError *error = NULL;
486 GPtrArray *info;
488 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
490 SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::request_contact_info called");
492 if (!tp_handle_is_valid(repo, contact, &error)) {
493 dbus_g_method_return_error(context, error);
494 g_error_free(error);
495 return;
498 info = convert_contact_info(buddy);
499 if (!info) {
500 dbus_g_method_return_error(context, error);
501 if (error)
502 g_error_free(error);
503 return;
506 tp_svc_connection_interface_contact_info_return_from_request_contact_info(context,
507 info);
508 g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info);
511 void sipe_telepathy_contact_info_iface_init(gpointer g_iface,
512 SIPE_UNUSED_PARAMETER gpointer iface_data)
514 TpSvcConnectionInterfaceContactInfoClass *klass = g_iface;
516 #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x( \
517 klass, x)
518 IMPLEMENT(get_contact_info);
519 /* Information is provided by the server: can't implement
520 IMPLEMENT(refresh_contact_info); */
521 IMPLEMENT(request_contact_info);
522 /* Information is provided by the server: can't implement
523 IMPLEMENT(set_contact_info); */
524 #undef IMPLEMENT
527 GPtrArray *sipe_telepathy_contact_info_fields(void)
529 GPtrArray *fields = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_FIELD_SPECS);
530 guint i;
532 SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::contact_info_fields called");
534 for (i = 0; i <= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY; i++) {
535 const gchar *vcard_name = sipe_to_vcard_field[i];
536 GValueArray *va;
538 /* unsupported field */
539 if (!vcard_name)
540 continue;
542 va = tp_value_array_build(4,
543 G_TYPE_STRING, vcard_name,
544 G_TYPE_STRV, NULL,
545 G_TYPE_UINT, 0, /* tp_flags */
546 G_TYPE_UINT, 1, /* max_times */
547 G_TYPE_INVALID);
548 g_ptr_array_add (fields, va);
551 return(fields);
554 /* TpDBusPropertiesMixinPropImpl is a broken typedef */
555 gpointer sipe_telepathy_contact_info_props(void)
557 static TpDBusPropertiesMixinPropImpl props[] = {
559 .name = "ContactInfoFlags",
560 .getter_data = GUINT_TO_POINTER(0),
561 /* @TODO .getter_data = GUINT_TO_POINTER(TP_CONTACT_INFO_FLAG_CAN_SET), */
562 .setter_data = NULL,
565 .name = "SupportedFields",
566 .getter_data = NULL,
567 .setter_data = NULL,
570 .name = NULL
573 return(props);
577 * Backend adaptor functions
579 sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public,
580 const gchar *buddy_name,
581 const gchar *group_name)
583 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
584 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
585 buddy_name);
586 if (!buddy)
587 return(NULL);
589 if (group_name) {
590 return(g_hash_table_lookup(buddy->groups, group_name));
591 } else {
592 /* just return the first entry */
593 GHashTableIter iter;
594 gpointer value = NULL;
595 g_hash_table_iter_init(&iter, buddy->groups);
596 /* make Coverity happy: as buddy != NULL this can't fail */
597 (void) g_hash_table_iter_next(&iter, NULL, &value);
598 return(value);
602 static GSList *buddy_add_all(struct telepathy_buddy *buddy, GSList *list)
604 GHashTableIter iter;
605 struct telepathy_buddy_entry *buddy_entry;
607 if (!buddy)
608 return(list);
610 g_hash_table_iter_init(&iter, buddy->groups);
611 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy_entry))
612 list = g_slist_prepend(list, buddy_entry);
614 return(list);
617 GSList *sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
618 const gchar *buddy_name,
619 const gchar *group_name)
621 GSList *result = NULL;
623 /* NOTE: group_name != NULL not implemented in purple either */
624 if (!group_name) {
625 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
626 GHashTable *buddies = telepathy_private->contact_list->buddies;
628 if (buddy_name) {
629 result = buddy_add_all(g_hash_table_lookup(buddies,
630 buddy_name),
631 result);
632 } else {
633 GHashTableIter biter;
634 struct telepathy_buddy *buddy;
636 g_hash_table_iter_init(&biter, telepathy_private->contact_list->buddies);
637 while (g_hash_table_iter_next(&biter, NULL, (gpointer) &buddy))
638 result = buddy_add_all(buddy, result);
642 return(result);
645 gchar *sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
646 const sipe_backend_buddy who)
648 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->uri));
651 gchar *sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
652 const sipe_backend_buddy who)
654 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]));
657 gchar *sipe_backend_buddy_get_server_alias(struct sipe_core_public *sipe_public,
658 const sipe_backend_buddy who)
660 /* server alias is the same as alias */
661 return(sipe_backend_buddy_get_alias(sipe_public, who));
664 gchar *sipe_backend_buddy_get_local_alias(struct sipe_core_public *sipe_public,
665 const sipe_backend_buddy who)
667 /* server alias is the same as alias */
668 return(sipe_backend_buddy_get_alias(sipe_public, who));
671 gchar *sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
672 const sipe_backend_buddy who)
674 return(g_strdup(((struct telepathy_buddy_entry *) who)->group));
677 gchar *sipe_backend_buddy_get_string(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
678 sipe_backend_buddy who,
679 const sipe_buddy_info_fields key)
681 struct telepathy_buddy_entry *buddy_entry = who;
682 struct telepathy_buddy *buddy = buddy_entry->buddy;
684 if (key >= SIPE_INFO_FIELD_MAX)
685 return(NULL);
686 return(g_strdup(buddy->info[key]));
689 void sipe_backend_buddy_set_string(struct sipe_core_public *sipe_public,
690 sipe_backend_buddy who,
691 const sipe_buddy_info_fields key,
692 const gchar *val)
694 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
695 SipeContactList *contact_list = telepathy_private->contact_list;
696 struct telepathy_buddy_entry *buddy_entry = who;
697 struct telepathy_buddy *buddy = buddy_entry->buddy;
699 if (key >= SIPE_INFO_FIELD_MAX)
700 return;
702 SIPE_DEBUG_INFO("sipe_backend_buddy_set_string: %s replacing info %d: %s -> %s",
703 buddy->uri, key,
704 buddy->info[key] ? buddy->info[key]: "<UNDEFINED>",
705 val);
707 g_free(buddy->info[key]);
708 buddy->info[key] = g_strdup(val);
710 if (contact_list->initial_received) {
711 /* @TODO: emit signal? */
715 void sipe_backend_buddy_refresh_properties(struct sipe_core_public *sipe_public,
716 const gchar *uri)
718 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
719 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
720 uri);
721 GPtrArray *info = convert_contact_info(buddy);
723 if (info) {
724 tp_svc_connection_interface_contact_info_emit_contact_info_changed(telepathy_private->connection,
725 buddy->handle,
726 info);
727 g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info);
731 guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public,
732 const gchar *uri)
734 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
735 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
736 uri);
738 if (!buddy)
739 return(SIPE_ACTIVITY_UNSET);
740 return(buddy->activity);
743 void sipe_backend_buddy_set_alias(struct sipe_core_public *sipe_public,
744 const sipe_backend_buddy who,
745 const gchar *alias)
747 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
748 SipeContactList *contact_list = telepathy_private->contact_list;
749 struct telepathy_buddy_entry *buddy_entry = who;
750 struct telepathy_buddy *buddy = buddy_entry->buddy;
752 update_alias(buddy, alias);
754 if (contact_list->initial_received) {
755 SIPE_DEBUG_INFO("sipe_backend_buddy_set_alias: %s changed to '%s'",
756 buddy->uri, alias);
757 sipe_telepathy_connection_alias_updated(contact_list->connection,
758 buddy->handle,
759 alias);
763 void sipe_backend_buddy_set_server_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
764 SIPE_UNUSED_PARAMETER const sipe_backend_buddy who,
765 SIPE_UNUSED_PARAMETER const gchar *alias)
767 /* server alias is the same as alias. Ignore this */
770 void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public)
772 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
773 SipeContactList *contact_list = telepathy_private->contact_list;
775 if (!contact_list->initial_received) {
776 /* we can only call this once */
777 contact_list->initial_received = TRUE;
778 SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called");
779 tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list));
783 static void buddy_free(gpointer data)
785 struct telepathy_buddy *buddy = data;
786 guint i;
787 g_hash_table_destroy(buddy->groups);
788 for (i = 0; i < SIPE_INFO_FIELD_MAX; i++)
789 g_free(buddy->info[i]);
790 g_free(buddy->hash);
791 g_free(buddy);
794 sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public,
795 const gchar *name,
796 const gchar *alias,
797 const gchar *group_name)
799 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
800 SipeContactList *contact_list = telepathy_private->contact_list;
801 const gchar *group = g_hash_table_lookup(contact_list->groups,
802 group_name);
803 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
804 name);
805 struct telepathy_buddy_entry *buddy_entry;
807 if (!group)
808 return(NULL);
810 if (!buddy) {
811 buddy = g_new0(struct telepathy_buddy, 1);
812 buddy->uri = g_strdup(name); /* reused as key */
813 buddy->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
814 NULL, g_free);
815 buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME] = g_strdup(alias);
816 buddy->hash = NULL;
817 buddy->activity = SIPE_ACTIVITY_OFFLINE;
818 buddy->handle = tp_handle_ensure(contact_list->contact_repo,
819 buddy->uri, NULL, NULL);
820 tp_handle_set_add(contact_list->contacts, buddy->handle);
821 g_hash_table_insert(contact_list->buddies,
822 (gchar *) buddy->uri, /* owned by hash table */
823 buddy);
824 g_hash_table_insert(contact_list->buddy_handles,
825 GUINT_TO_POINTER(buddy->handle),
826 buddy);
829 buddy_entry = g_hash_table_lookup(buddy->groups, group);
830 if (!buddy_entry) {
831 buddy_entry = g_new0(struct telepathy_buddy_entry, 1);
832 buddy_entry->buddy = buddy;
833 buddy_entry->group = group;
834 g_hash_table_insert(buddy->groups,
835 (gchar *) group, /* key is borrowed */
836 buddy_entry);
839 if (contact_list->initial_received) {
840 /* @TODO: emit signal? */
843 return(buddy_entry);
846 void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public,
847 const sipe_backend_buddy who)
849 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
850 SipeContactList *contact_list = telepathy_private->contact_list;
851 struct telepathy_buddy_entry *remove_entry = who;
852 struct telepathy_buddy *buddy = remove_entry->buddy;
854 g_hash_table_remove(buddy->groups,
855 remove_entry->group);
856 /* remove_entry is invalid */
858 if (g_hash_table_size(buddy->groups) == 0) {
859 /* removed from last group -> drop this buddy */
860 tp_handle_set_remove(contact_list->contacts,
861 buddy->handle);
862 g_hash_table_remove(contact_list->buddy_handles,
863 GUINT_TO_POINTER(buddy->handle));
864 g_hash_table_remove(contact_list->buddies,
865 buddy->uri);
869 if (contact_list->initial_received) {
870 /* @TODO: emit signal? */
874 void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public,
875 const gchar *uri,
876 guint activity)
878 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
879 SipeContactList *contact_list = telepathy_private->contact_list;
880 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
881 uri);
882 TpPresenceStatus *status;
884 if (!buddy)
885 return;
886 buddy->activity = activity;
888 SIPE_DEBUG_INFO("sipe_backend_buddy_set_status: %s to %d", uri, activity);
890 /* emit status update signal */
891 status = tp_presence_status_new(activity, NULL);
892 tp_presence_mixin_emit_one_presence_update(G_OBJECT(telepathy_private->connection),
893 buddy->handle, status);
894 tp_presence_status_free(status);
897 gboolean sipe_backend_uses_photo(void)
899 return(TRUE);
902 static void buddy_photo_updated(struct sipe_backend_private *telepathy_private,
903 struct telepathy_buddy *buddy,
904 const gchar *photo,
905 gsize photo_len)
907 GArray *array = g_array_new(FALSE, FALSE, sizeof(gchar));
909 SIPE_DEBUG_INFO("buddy_photo_updated: %s (%" G_GSIZE_FORMAT ")",
910 buddy->uri, photo_len);
912 g_array_append_vals(array, photo, photo_len);
914 tp_svc_connection_interface_avatars_emit_avatar_updated(telepathy_private->connection,
915 buddy->handle,
916 buddy->hash);
917 tp_svc_connection_interface_avatars_emit_avatar_retrieved(telepathy_private->connection,
918 buddy->handle,
919 buddy->hash,
920 array,
921 /* @TODO: is this correct? */
922 "image/jpeg");
923 g_array_unref(array);
926 void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public,
927 const gchar *uri,
928 gpointer image_data,
929 gsize image_len,
930 const gchar *photo_hash)
932 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
933 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
934 uri);
936 if (buddy) {
937 gchar *hash_file = g_build_filename(telepathy_private->cache_dir,
938 uri,
939 NULL);
941 /* does this buddy already have a photo? -> delete it */
942 if (buddy->hash) {
943 char *photo_file = g_build_filename(telepathy_private->cache_dir,
944 buddy->hash,
945 NULL);
946 (void) g_remove(photo_file);
947 g_free(photo_file);
948 g_free(buddy->hash);
949 buddy->hash = NULL;
952 /* update hash file */
953 if (g_file_set_contents(hash_file,
954 photo_hash,
955 strlen(photo_hash),
956 NULL)) {
957 gchar *photo_file = g_build_filename(telepathy_private->cache_dir,
958 photo_hash,
959 NULL);
960 buddy->hash = g_strdup(photo_hash);
961 g_file_set_contents(photo_file,
962 image_data,
963 image_len,
964 NULL);
966 buddy_photo_updated(telepathy_private,
967 buddy,
968 image_data,
969 image_len);
971 g_free(photo_file);
974 g_free(hash_file);
977 g_free(image_data);
980 const gchar *sipe_backend_buddy_get_photo_hash(struct sipe_core_public *sipe_public,
981 const gchar *uri)
983 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
984 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
985 uri);
987 if (!buddy)
988 return(NULL);
990 if (!buddy->hash) {
991 gchar *hash_file = g_build_filename(telepathy_private->cache_dir,
992 uri,
993 NULL);
994 /* returned memory is owned & freed by buddy */
995 if (g_file_get_contents(hash_file, &buddy->hash, NULL, NULL)) {
996 gchar *photo_file = g_build_filename(telepathy_private->cache_dir,
997 buddy->hash,
998 NULL);
999 gchar *image_data = NULL;
1000 gsize image_len;
1002 if (g_file_get_contents(photo_file,
1003 &image_data,
1004 &image_len,
1005 NULL))
1006 buddy_photo_updated(telepathy_private,
1007 buddy,
1008 image_data,
1009 image_len);
1010 g_free(image_data);
1011 g_free(photo_file);
1013 g_free(hash_file);
1016 return(buddy->hash);
1019 gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public,
1020 const gchar *group_name)
1022 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
1023 SipeContactList *contact_list = telepathy_private->contact_list;
1024 gchar *group = g_hash_table_lookup(contact_list->groups,
1025 group_name);
1027 if (!group) {
1028 group = g_strdup(group_name);
1029 g_hash_table_insert(contact_list->groups, group, group);
1030 tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list),
1031 &group_name,
1035 return(group != NULL);
1038 void sipe_backend_buddy_group_remove(struct sipe_core_public *sipe_public,
1039 const gchar *group_name)
1041 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
1042 SipeContactList *contact_list = telepathy_private->contact_list;
1044 g_hash_table_remove(contact_list->groups, group_name);
1046 if (contact_list->initial_received) {
1047 /* @TODO: emit signal? */
1052 Local Variables:
1053 mode: c
1054 c-file-style: "bsd"
1055 indent-tabs-mode: t
1056 tab-width: 8
1057 End: