Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / telepathy / telepathy-buddy.c
blob4ecfb7df28ae5e7ad3b38a6a03211d7866b6d60f
1 /**
2 * @file telepathy-buddy.c
4 * pidgin-sipe
6 * Copyright (C) 2012-2019 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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <string.h>
29 #include <glib-object.h>
30 #include <glib/gstdio.h>
31 #include <telepathy-glib/base-connection.h>
32 #include <telepathy-glib/base-contact-list.h>
33 #include <telepathy-glib/telepathy-glib.h>
35 #include "sipe-backend.h"
36 #include "sipe-common.h"
37 #include "sipe-core.h"
39 #include "telepathy-private.h"
41 #define SIPE_INFO_FIELD_MAX (SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY + 1)
43 struct telepathy_buddy {
44 const gchar *uri; /* borrowed from contact_list->buddies key */
45 GHashTable *groups; /* key: group name, value: buddy_entry */
46 /* keys are borrowed from contact_list->groups */
47 TpHandle handle;
48 /* includes alias as stored on the server */
49 gchar *info[SIPE_INFO_FIELD_MAX];
50 gchar *hash; /* photo hash */
51 guint activity;
54 struct telepathy_buddy_entry {
55 struct telepathy_buddy *buddy; /* pointer to parent */
56 const gchar *group; /* borrowed from contact_list->groups key */
59 G_BEGIN_DECLS
61 * Contact List class - data structures
63 typedef struct _SipeContactListClass {
64 TpBaseContactListClass parent_class;
65 } SipeContactListClass;
67 typedef struct _SipeContactList {
68 TpBaseContactList parent;
70 TpBaseConnection *connection;
71 TpHandleRepoIface *contact_repo;
72 TpHandleSet *contacts;
74 GHashTable *buddies; /* key: SIP URI, value: buddy */
75 GHashTable *buddy_handles; /* key: TpHandle, value: buddy */
76 GHashTable *groups; /* key: group name, value: buddy */
78 gboolean initial_received;
79 } SipeContactList;
82 * Contact List class - type macros
84 static GType sipe_contact_list_get_type(void) G_GNUC_CONST;
85 #define SIPE_TYPE_CONTACT_LIST \
86 (sipe_contact_list_get_type())
87 #define SIPE_CONTACT_LIST(obj) \
88 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONTACT_LIST, \
89 SipeContactList))
90 G_END_DECLS
93 * Contact List class - type definition
95 static void contact_group_list_iface_init(TpContactGroupListInterface *);
96 G_DEFINE_TYPE_WITH_CODE(SipeContactList,
97 sipe_contact_list,
98 TP_TYPE_BASE_CONTACT_LIST,
99 G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST,
100 contact_group_list_iface_init);
105 * Contact List class - instance methods
107 static TpHandleSet *dup_contacts(TpBaseContactList *contact_list)
109 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
110 return(tp_handle_set_copy(self->contacts));
113 static void dup_states(SIPE_UNUSED_PARAMETER TpBaseContactList *contact_list,
114 SIPE_UNUSED_PARAMETER TpHandle contact,
115 TpSubscriptionState *subscribe,
116 TpSubscriptionState *publish,
117 gchar **publish_request)
119 /* @TODO */
120 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
122 if (subscribe)
123 *subscribe = TP_SUBSCRIPTION_STATE_YES;
124 if (publish)
125 *publish = TP_SUBSCRIPTION_STATE_YES;
126 if (publish_request)
127 *publish_request = g_strdup("");
130 static void sipe_contact_list_constructed(GObject *object)
132 SipeContactList *self = SIPE_CONTACT_LIST(object);
133 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->constructed;
135 if (chain_up)
136 chain_up(object);
138 g_object_get(self, "connection", &self->connection, NULL);
139 self->contact_repo = tp_base_connection_get_handles(self->connection,
140 TP_HANDLE_TYPE_CONTACT);
141 self->contacts = tp_handle_set_new(self->contact_repo);
144 static void sipe_contact_list_dispose(GObject *object)
146 SipeContactList *self = SIPE_CONTACT_LIST(object);
147 void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_contact_list_parent_class)->dispose;
149 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dispose");
151 tp_clear_pointer(&self->contacts, tp_handle_set_destroy);
152 tp_clear_object(&self->connection);
153 /* NOTE: the order is important due to borrowing of keys! */
154 tp_clear_pointer(&self->buddy_handles, g_hash_table_unref);
155 tp_clear_pointer(&self->buddies, g_hash_table_unref);
156 tp_clear_pointer(&self->groups, g_hash_table_unref);
158 if (chain_up)
159 chain_up(object);
163 * Contact List class - type implementation
165 static void sipe_contact_list_class_init(SipeContactListClass *klass)
167 GObjectClass *object_class = G_OBJECT_CLASS(klass);
168 TpBaseContactListClass *base_class = TP_BASE_CONTACT_LIST_CLASS(klass);
170 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::class_init");
172 object_class->constructed = sipe_contact_list_constructed;
173 object_class->dispose = sipe_contact_list_dispose;
175 base_class->dup_contacts = dup_contacts;
176 base_class->dup_states = dup_states;
179 static void buddy_free(gpointer data);
180 static void sipe_contact_list_init(SIPE_UNUSED_PARAMETER SipeContactList *self)
182 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::init");
184 self->buddies = g_hash_table_new_full(g_str_hash, g_str_equal,
185 g_free, buddy_free);
186 self->buddy_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
187 self->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
188 g_free, NULL);
190 self->initial_received = FALSE;
194 * Contact List class - interface implementation
196 * Contact groups
198 static GStrv dup_groups(TpBaseContactList *contact_list)
200 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
201 GPtrArray *groups = g_ptr_array_sized_new(
202 g_hash_table_size(self->groups) + 1);
203 GHashTableIter iter;
204 gpointer name;
206 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_groups called");
208 g_hash_table_iter_init(&iter, self->groups);
209 while (g_hash_table_iter_next(&iter, &name, NULL))
210 g_ptr_array_add(groups, g_strdup(name));
211 g_ptr_array_add(groups, NULL);
213 return((GStrv) g_ptr_array_free(groups, FALSE));
216 static TpHandleSet *dup_group_members(TpBaseContactList *contact_list,
217 const gchar *group_name)
219 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
220 TpHandleSet *members = tp_handle_set_new(self->contact_repo);
221 GHashTableIter iter;
222 struct telepathy_buddy *buddy;
224 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_group_members called");
226 g_hash_table_iter_init(&iter, self->buddies);
227 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy))
228 if (g_hash_table_lookup(buddy->groups, group_name))
229 tp_handle_set_add(members, buddy->handle);
231 return(members);
234 static GStrv dup_contact_groups(TpBaseContactList *contact_list,
235 TpHandle contact)
237 SipeContactList *self = SIPE_CONTACT_LIST(contact_list);
238 GPtrArray *groups = g_ptr_array_sized_new(
239 g_hash_table_size(self->groups) + 1);
240 struct telepathy_buddy *buddy = g_hash_table_lookup(self->buddy_handles,
241 GUINT_TO_POINTER(contact));
243 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_contact_groups called");
245 if (buddy) {
246 GHashTableIter iter;
247 const gchar *group_name;
249 g_hash_table_iter_init(&iter, buddy->groups);
250 while (g_hash_table_iter_next(&iter,
251 (gpointer) &group_name,
252 NULL))
253 g_ptr_array_add(groups, g_strdup(group_name));
255 g_ptr_array_add(groups, NULL);
257 return((GStrv) g_ptr_array_free(groups, FALSE));
260 static void contact_group_list_iface_init(TpContactGroupListInterface *iface)
262 #define IMPLEMENT(x) iface->x = x
263 IMPLEMENT(dup_groups);
264 IMPLEMENT(dup_group_members);
265 IMPLEMENT(dup_contact_groups);
266 #undef IMPLEMENT
269 /* create new contact list object */
270 SipeContactList *sipe_telepathy_contact_list_new(TpBaseConnection *connection)
272 return(g_object_new(SIPE_TYPE_CONTACT_LIST,
273 "connection", connection,
274 NULL));
277 /* get & set alias for a contact */
278 const gchar *sipe_telepathy_buddy_get_alias(SipeContactList *contact_list,
279 TpHandle contact)
281 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
282 GUINT_TO_POINTER(contact));
283 if (!buddy)
284 return(NULL);
285 return(buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]);
288 static void update_alias(struct telepathy_buddy *buddy,
289 const gchar *alias)
291 if (buddy) {
292 g_free(buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]);
293 buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME] = g_strdup(alias);
297 void sipe_telepathy_buddy_set_alias(SipeContactList *contact_list,
298 const guint contact,
299 const gchar *alias)
301 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
302 GUINT_TO_POINTER(contact));
303 update_alias(buddy, alias);
305 /* tell core about the alias change */
306 if (buddy) {
307 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(contact_list->connection));
308 sipe_core_group_set_alias(telepathy_private->public,
309 buddy->uri,
310 alias);
314 /* get photo hash for a contact */
315 const gchar *sipe_telepathy_buddy_get_hash(struct _SipeContactList *contact_list,
316 const guint contact)
318 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
319 GUINT_TO_POINTER(contact));
320 if (!buddy)
321 return(NULL);
322 return(buddy->hash);
325 /* get presence status for a contact */
326 guint sipe_telepathy_buddy_get_presence(SipeContactList *contact_list,
327 const TpHandle contact)
329 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddy_handles,
330 GUINT_TO_POINTER(contact));
331 if (!buddy)
332 return(SIPE_ACTIVITY_UNSET);
333 return(buddy->activity);
336 /* @TODO: are other MIME types supported by OCS? */
337 static const char * mimetypes[] = {
338 "image/jpeg",
339 NULL
342 /* @TODO: are these correct or even needed? */
343 #define AVATAR_MIN_PX 16
344 #define AVATAR_MAX_PX 256
345 #define AVATAR_MAX_BYTES 32768
347 static void get_avatar_requirements(TpSvcConnectionInterfaceAvatars *iface,
348 DBusGMethodInvocation *context)
350 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(TP_BASE_CONNECTION(iface),
351 context);
353 tp_svc_connection_interface_avatars_return_from_get_avatar_requirements(
354 context,
355 mimetypes,
356 AVATAR_MIN_PX, AVATAR_MIN_PX,
357 AVATAR_MAX_PX, AVATAR_MAX_PX,
358 AVATAR_MAX_BYTES);
361 void sipe_telepathy_avatars_iface_init(gpointer g_iface,
362 SIPE_UNUSED_PARAMETER gpointer iface_data)
364 TpSvcConnectionInterfaceAvatarsClass *klass = g_iface;
366 #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x( \
367 klass, x)
368 IMPLEMENT(get_avatar_requirements);
369 /* Information is provided by server: can't implement
370 IMPLEMENT(get_avatar_tokens);
371 IMPLEMENT(get_known_avatar_tokens);
372 IMPLEMENT(request_avatar);
373 IMPLEMENT(request_avatars);
374 IMPLEMENT(set_avatar);
375 IMPLEMENT(clear_avatar); */
376 #undef IMPLEMENT
379 static const gchar *const sipe_to_vcard_field[SIPE_INFO_FIELD_MAX] = {
380 /* SIPE_BUDDY_INFO_DISPLAY_NAME */ "fn",
381 /* SIPE_BUDDY_INFO_JOB_TITLE */ "title",
382 /* SIPE_BUDDY_INFO_CITY */ NULL,
383 /* SIPE_BUDDY_INFO_STATE */ NULL,
384 /* SIPE_BUDDY_INFO_OFFICE */ NULL,
385 /* SIPE_BUDDY_INFO_DEPARTMENT */ NULL,
386 /* SIPE_BUDDY_INFO_COUNTRY */ NULL,
387 /* SIPE_BUDDY_INFO_WORK_PHONE */ "tel",
388 /* SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY */ NULL,
389 /* SIPE_BUDDY_INFO_COMPANY */ "org",
390 /* SIPE_BUDDY_INFO_EMAIL */ "email",
391 /* SIPE_BUDDY_INFO_SITE */ NULL,
392 /* SIPE_BUDDY_INFO_ZIPCODE */ NULL,
393 /* SIPE_BUDDY_INFO_STREET */ NULL,
394 /* SIPE_BUDDY_INFO_MOBILE_PHONE */ NULL,
395 /* SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY */ NULL,
396 /* SIPE_BUDDY_INFO_HOME_PHONE */ NULL,
397 /* SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY */ NULL,
398 /* SIPE_BUDDY_INFO_OTHER_PHONE */ NULL,
399 /* SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY */ NULL,
400 /* SIPE_BUDDY_INFO_CUSTOM1_PHONE */ NULL,
401 /* SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY */ NULL,
404 static GPtrArray *convert_contact_info(struct telepathy_buddy *buddy)
406 GPtrArray *info = NULL;
408 if (buddy) {
409 guint i;
411 info = dbus_g_type_specialized_construct(
412 TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
414 for (i = 0; i < SIPE_INFO_FIELD_MAX; i++) {
415 const gchar *name = sipe_to_vcard_field[i];
416 const gchar *value = buddy->info[i];
418 if (name && value) {
419 const gchar *const field_values[2] = { value, NULL };
421 SIPE_DEBUG_INFO("SipeContactInfo::convert_contact_info: %s: (%2d)%s = '%s'",
422 buddy->uri, i, name, value);
424 g_ptr_array_add(info,
425 tp_value_array_build(3,
426 G_TYPE_STRING, name,
427 G_TYPE_STRV, NULL,
428 G_TYPE_STRV, field_values,
429 G_TYPE_INVALID));
434 return(info);
437 static void get_contact_info(TpSvcConnectionInterfaceContactInfo *iface,
438 const GArray *contacts,
439 DBusGMethodInvocation *context)
441 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(iface));
442 GHashTable *buddies = telepathy_private->contact_list->buddy_handles;
443 TpBaseConnection *base = TP_BASE_CONNECTION(iface);
444 TpHandleRepoIface *repo = tp_base_connection_get_handles(base,
445 TP_HANDLE_TYPE_CONTACT);
446 GError *error = NULL;
447 GHashTable *infos;
448 guint i;
450 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
452 SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::get_contact_info called");
454 if (!tp_handles_are_valid(repo, contacts, FALSE, &error)) {
455 dbus_g_method_return_error(context, error);
456 g_error_free(error);
457 return;
460 infos = dbus_g_type_specialized_construct(TP_HASH_TYPE_CONTACT_INFO_MAP);
462 for (i = 0; i < contacts->len; i++) {
463 TpHandle contact = g_array_index(contacts, TpHandle, i);
464 struct telepathy_buddy *buddy = g_hash_table_lookup(buddies,
465 GUINT_TO_POINTER(contact));
466 GPtrArray *info = convert_contact_info(buddy);
468 if (info)
469 g_hash_table_insert(infos,
470 GUINT_TO_POINTER(contact),
471 info);
474 tp_svc_connection_interface_contact_info_return_from_get_contact_info(context,
475 infos);
476 g_boxed_free(TP_HASH_TYPE_CONTACT_INFO_MAP, infos);
479 static void request_contact_info(TpSvcConnectionInterfaceContactInfo *iface,
480 guint contact,
481 DBusGMethodInvocation *context)
483 struct sipe_backend_private *telepathy_private = sipe_telepathy_connection_private(G_OBJECT(iface));
484 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddy_handles,
485 GUINT_TO_POINTER(contact));
486 TpBaseConnection *base = TP_BASE_CONNECTION(iface);
487 TpHandleRepoIface *repo = tp_base_connection_get_handles(base,
488 TP_HANDLE_TYPE_CONTACT);
489 GError *error = NULL;
490 GPtrArray *info;
492 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
494 SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::request_contact_info called");
496 if (!tp_handle_is_valid(repo, contact, &error)) {
497 dbus_g_method_return_error(context, error);
498 g_error_free(error);
499 return;
502 info = convert_contact_info(buddy);
503 if (!info) {
504 dbus_g_method_return_error(context, error);
505 if (error)
506 g_error_free(error);
507 return;
510 tp_svc_connection_interface_contact_info_return_from_request_contact_info(context,
511 info);
512 g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info);
515 void sipe_telepathy_contact_info_iface_init(gpointer g_iface,
516 SIPE_UNUSED_PARAMETER gpointer iface_data)
518 TpSvcConnectionInterfaceContactInfoClass *klass = g_iface;
520 #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x( \
521 klass, x)
522 IMPLEMENT(get_contact_info);
523 /* Information is provided by the server: can't implement
524 IMPLEMENT(refresh_contact_info); */
525 IMPLEMENT(request_contact_info);
526 /* Information is provided by the server: can't implement
527 IMPLEMENT(set_contact_info); */
528 #undef IMPLEMENT
531 GPtrArray *sipe_telepathy_contact_info_fields(void)
533 GPtrArray *fields = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_FIELD_SPECS);
534 guint i;
536 SIPE_DEBUG_INFO_NOFORMAT("SipeContactInfo::contact_info_fields called");
538 for (i = 0; i <= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY; i++) {
539 const gchar *vcard_name = sipe_to_vcard_field[i];
540 GValueArray *va;
542 /* unsupported field */
543 if (!vcard_name)
544 continue;
546 va = tp_value_array_build(4,
547 G_TYPE_STRING, vcard_name,
548 G_TYPE_STRV, NULL,
549 G_TYPE_UINT, 0, /* tp_flags */
550 G_TYPE_UINT, 1, /* max_times */
551 G_TYPE_INVALID);
552 g_ptr_array_add (fields, va);
555 return(fields);
558 /* TpDBusPropertiesMixinPropImpl is a broken typedef */
559 gpointer sipe_telepathy_contact_info_props(void)
561 static TpDBusPropertiesMixinPropImpl props[] = {
563 .name = "ContactInfoFlags",
564 .getter_data = GUINT_TO_POINTER(0),
565 /* @TODO .getter_data = GUINT_TO_POINTER(TP_CONTACT_INFO_FLAG_CAN_SET), */
566 .setter_data = NULL,
569 .name = "SupportedFields",
570 .getter_data = NULL,
571 .setter_data = NULL,
574 .name = NULL
577 return(props);
581 * Backend adaptor functions
583 sipe_backend_buddy sipe_backend_buddy_find(struct sipe_core_public *sipe_public,
584 const gchar *buddy_name,
585 const gchar *group_name)
587 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
588 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
589 buddy_name);
590 if (!buddy)
591 return(NULL);
593 if (group_name) {
594 return(g_hash_table_lookup(buddy->groups, group_name));
595 } else {
596 /* just return the first entry */
597 GHashTableIter iter;
598 gpointer value = NULL;
599 g_hash_table_iter_init(&iter, buddy->groups);
600 /* make Coverity happy: as buddy != NULL this can't fail */
601 (void) g_hash_table_iter_next(&iter, NULL, &value);
602 return(value);
606 static GSList *buddy_add_all(struct telepathy_buddy *buddy, GSList *list)
608 GHashTableIter iter;
609 struct telepathy_buddy_entry *buddy_entry;
611 if (!buddy)
612 return(list);
614 g_hash_table_iter_init(&iter, buddy->groups);
615 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &buddy_entry))
616 list = g_slist_prepend(list, buddy_entry);
618 return(list);
621 GSList *sipe_backend_buddy_find_all(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
622 const gchar *buddy_name,
623 const gchar *group_name)
625 GSList *result = NULL;
627 /* NOTE: group_name != NULL not implemented in purple either */
628 if (!group_name) {
629 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
630 GHashTable *buddies = telepathy_private->contact_list->buddies;
632 if (buddy_name) {
633 result = buddy_add_all(g_hash_table_lookup(buddies,
634 buddy_name),
635 result);
636 } else {
637 GHashTableIter biter;
638 struct telepathy_buddy *buddy;
640 g_hash_table_iter_init(&biter, telepathy_private->contact_list->buddies);
641 while (g_hash_table_iter_next(&biter, NULL, (gpointer) &buddy))
642 result = buddy_add_all(buddy, result);
646 return(result);
649 gchar *sipe_backend_buddy_get_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
650 const sipe_backend_buddy who)
652 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->uri));
655 gchar *sipe_backend_buddy_get_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
656 const sipe_backend_buddy who)
658 return(g_strdup(((struct telepathy_buddy_entry *) who)->buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME]));
661 gchar *sipe_backend_buddy_get_server_alias(struct sipe_core_public *sipe_public,
662 const sipe_backend_buddy who)
664 /* server alias is the same as alias */
665 return(sipe_backend_buddy_get_alias(sipe_public, who));
668 gchar *sipe_backend_buddy_get_local_alias(struct sipe_core_public *sipe_public,
669 const sipe_backend_buddy who)
671 /* server alias is the same as alias */
672 return(sipe_backend_buddy_get_alias(sipe_public, who));
675 gchar *sipe_backend_buddy_get_group_name(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
676 const sipe_backend_buddy who)
678 return(g_strdup(((struct telepathy_buddy_entry *) who)->group));
681 gchar *sipe_backend_buddy_get_string(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
682 sipe_backend_buddy who,
683 const sipe_buddy_info_fields key)
685 struct telepathy_buddy_entry *buddy_entry = who;
686 struct telepathy_buddy *buddy = buddy_entry->buddy;
688 if (key >= SIPE_INFO_FIELD_MAX)
689 return(NULL);
690 return(g_strdup(buddy->info[key]));
693 void sipe_backend_buddy_set_string(struct sipe_core_public *sipe_public,
694 sipe_backend_buddy who,
695 const sipe_buddy_info_fields key,
696 const gchar *val)
698 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
699 SipeContactList *contact_list = telepathy_private->contact_list;
700 struct telepathy_buddy_entry *buddy_entry = who;
701 struct telepathy_buddy *buddy = buddy_entry->buddy;
703 if (key >= SIPE_INFO_FIELD_MAX)
704 return;
706 SIPE_DEBUG_INFO("sipe_backend_buddy_set_string: %s replacing info %d: %s -> %s",
707 buddy->uri, key,
708 buddy->info[key] ? buddy->info[key]: "<UNDEFINED>",
709 val);
711 g_free(buddy->info[key]);
712 buddy->info[key] = g_strdup(val);
714 if (contact_list->initial_received) {
715 /* @TODO: emit signal? */
719 void sipe_backend_buddy_refresh_properties(struct sipe_core_public *sipe_public,
720 const gchar *uri)
722 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
723 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
724 uri);
725 GPtrArray *info = convert_contact_info(buddy);
727 if (info) {
728 tp_svc_connection_interface_contact_info_emit_contact_info_changed(telepathy_private->connection,
729 buddy->handle,
730 info);
731 g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info);
735 guint sipe_backend_buddy_get_status(struct sipe_core_public *sipe_public,
736 const gchar *uri)
738 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
739 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
740 uri);
742 if (!buddy)
743 return(SIPE_ACTIVITY_UNSET);
744 return(buddy->activity);
747 void sipe_backend_buddy_set_alias(struct sipe_core_public *sipe_public,
748 const sipe_backend_buddy who,
749 const gchar *alias)
751 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
752 SipeContactList *contact_list = telepathy_private->contact_list;
753 struct telepathy_buddy_entry *buddy_entry = who;
754 struct telepathy_buddy *buddy = buddy_entry->buddy;
756 update_alias(buddy, alias);
758 if (contact_list->initial_received) {
759 SIPE_DEBUG_INFO("sipe_backend_buddy_set_alias: %s changed to '%s'",
760 buddy->uri, alias);
761 sipe_telepathy_connection_alias_updated(contact_list->connection,
762 buddy->handle,
763 alias);
767 void sipe_backend_buddy_set_server_alias(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
768 SIPE_UNUSED_PARAMETER const sipe_backend_buddy who,
769 SIPE_UNUSED_PARAMETER const gchar *alias)
771 /* server alias is the same as alias. Ignore this */
774 void sipe_backend_buddy_list_processing_finish(struct sipe_core_public *sipe_public)
776 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
777 SipeContactList *contact_list = telepathy_private->contact_list;
779 if (!contact_list->initial_received) {
780 /* we can only call this once */
781 contact_list->initial_received = TRUE;
782 SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_buddy_list_processing_finish called");
783 tp_base_contact_list_set_list_received(TP_BASE_CONTACT_LIST(contact_list));
787 static void buddy_free(gpointer data)
789 struct telepathy_buddy *buddy = data;
790 guint i;
791 g_hash_table_destroy(buddy->groups);
792 for (i = 0; i < SIPE_INFO_FIELD_MAX; i++)
793 g_free(buddy->info[i]);
794 g_free(buddy->hash);
795 g_free(buddy);
798 sipe_backend_buddy sipe_backend_buddy_add(struct sipe_core_public *sipe_public,
799 const gchar *name,
800 const gchar *alias,
801 const gchar *group_name)
803 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
804 SipeContactList *contact_list = telepathy_private->contact_list;
805 const gchar *group = g_hash_table_lookup(contact_list->groups,
806 group_name);
807 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
808 name);
809 struct telepathy_buddy_entry *buddy_entry;
811 if (!group)
812 return(NULL);
814 if (!buddy) {
815 buddy = g_new0(struct telepathy_buddy, 1);
816 buddy->uri = g_strdup(name); /* reused as key */
817 buddy->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
818 NULL, g_free);
819 buddy->info[SIPE_BUDDY_INFO_DISPLAY_NAME] = g_strdup(alias);
820 buddy->hash = NULL;
821 buddy->activity = SIPE_ACTIVITY_OFFLINE;
822 buddy->handle = tp_handle_ensure(contact_list->contact_repo,
823 buddy->uri, NULL, NULL);
824 tp_handle_set_add(contact_list->contacts, buddy->handle);
825 g_hash_table_insert(contact_list->buddies,
826 (gchar *) buddy->uri, /* owned by hash table */
827 buddy);
828 g_hash_table_insert(contact_list->buddy_handles,
829 GUINT_TO_POINTER(buddy->handle),
830 buddy);
833 buddy_entry = g_hash_table_lookup(buddy->groups, group);
834 if (!buddy_entry) {
835 buddy_entry = g_new0(struct telepathy_buddy_entry, 1);
836 buddy_entry->buddy = buddy;
837 buddy_entry->group = group;
838 g_hash_table_insert(buddy->groups,
839 (gchar *) group, /* key is borrowed */
840 buddy_entry);
843 if (contact_list->initial_received) {
844 /* @TODO: emit signal? */
847 return(buddy_entry);
850 void sipe_backend_buddy_remove(struct sipe_core_public *sipe_public,
851 const sipe_backend_buddy who)
853 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
854 SipeContactList *contact_list = telepathy_private->contact_list;
855 struct telepathy_buddy_entry *remove_entry = who;
856 struct telepathy_buddy *buddy = remove_entry->buddy;
858 g_hash_table_remove(buddy->groups,
859 remove_entry->group);
860 /* remove_entry is invalid */
862 if (g_hash_table_size(buddy->groups) == 0) {
863 /* removed from last group -> drop this buddy */
864 tp_handle_set_remove(contact_list->contacts,
865 buddy->handle);
866 g_hash_table_remove(contact_list->buddy_handles,
867 GUINT_TO_POINTER(buddy->handle));
868 g_hash_table_remove(contact_list->buddies,
869 buddy->uri);
873 if (contact_list->initial_received) {
874 /* @TODO: emit signal? */
878 void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public,
879 const gchar *uri,
880 guint activity,
881 SIPE_UNUSED_PARAMETER time_t last_active)
883 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
884 SipeContactList *contact_list = telepathy_private->contact_list;
885 struct telepathy_buddy *buddy = g_hash_table_lookup(contact_list->buddies,
886 uri);
887 TpPresenceStatus *status;
889 if (!buddy)
890 return;
891 buddy->activity = activity;
893 SIPE_DEBUG_INFO("sipe_backend_buddy_set_status: %s to %d", uri, activity);
895 /* emit status update signal */
896 status = tp_presence_status_new(activity, NULL);
897 tp_presence_mixin_emit_one_presence_update(G_OBJECT(telepathy_private->connection),
898 buddy->handle, status);
899 tp_presence_status_free(status);
902 gboolean sipe_backend_uses_photo(void)
904 return(TRUE);
907 static void buddy_photo_updated(struct sipe_backend_private *telepathy_private,
908 struct telepathy_buddy *buddy,
909 const gchar *photo,
910 gsize photo_len)
912 GArray *array = g_array_new(FALSE, FALSE, sizeof(gchar));
914 SIPE_DEBUG_INFO("buddy_photo_updated: %s (%" G_GSIZE_FORMAT ")",
915 buddy->uri, photo_len);
917 g_array_append_vals(array, photo, photo_len);
919 tp_svc_connection_interface_avatars_emit_avatar_updated(telepathy_private->connection,
920 buddy->handle,
921 buddy->hash);
922 tp_svc_connection_interface_avatars_emit_avatar_retrieved(telepathy_private->connection,
923 buddy->handle,
924 buddy->hash,
925 array,
926 /* @TODO: is this correct? */
927 "image/jpeg");
928 g_array_unref(array);
931 void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public,
932 const gchar *uri,
933 gpointer image_data,
934 gsize image_len,
935 const gchar *photo_hash)
937 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
938 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
939 uri);
941 if (buddy) {
942 gchar *hash_file = g_build_filename(telepathy_private->cache_dir,
943 uri,
944 NULL);
946 /* does this buddy already have a photo? -> delete it */
947 if (buddy->hash) {
948 char *photo_file = g_build_filename(telepathy_private->cache_dir,
949 buddy->hash,
950 NULL);
951 (void) g_remove(photo_file);
952 g_free(photo_file);
953 g_free(buddy->hash);
954 buddy->hash = NULL;
957 /* update hash file */
958 if (g_file_set_contents(hash_file,
959 photo_hash,
960 strlen(photo_hash),
961 NULL)) {
962 gchar *photo_file = g_build_filename(telepathy_private->cache_dir,
963 photo_hash,
964 NULL);
965 buddy->hash = g_strdup(photo_hash);
966 g_file_set_contents(photo_file,
967 image_data,
968 image_len,
969 NULL);
971 buddy_photo_updated(telepathy_private,
972 buddy,
973 image_data,
974 image_len);
976 g_free(photo_file);
979 g_free(hash_file);
982 g_free(image_data);
985 const gchar *sipe_backend_buddy_get_photo_hash(struct sipe_core_public *sipe_public,
986 const gchar *uri)
988 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
989 struct telepathy_buddy *buddy = g_hash_table_lookup(telepathy_private->contact_list->buddies,
990 uri);
992 if (!buddy)
993 return(NULL);
995 if (!buddy->hash) {
996 gchar *hash_file = g_build_filename(telepathy_private->cache_dir,
997 uri,
998 NULL);
999 /* returned memory is owned & freed by buddy */
1000 if (g_file_get_contents(hash_file, &buddy->hash, NULL, NULL)) {
1001 gchar *photo_file = g_build_filename(telepathy_private->cache_dir,
1002 buddy->hash,
1003 NULL);
1004 gchar *image_data = NULL;
1005 gsize image_len;
1007 if (g_file_get_contents(photo_file,
1008 &image_data,
1009 &image_len,
1010 NULL))
1011 buddy_photo_updated(telepathy_private,
1012 buddy,
1013 image_data,
1014 image_len);
1015 g_free(image_data);
1016 g_free(photo_file);
1018 g_free(hash_file);
1021 return(buddy->hash);
1024 gboolean sipe_backend_buddy_group_add(struct sipe_core_public *sipe_public,
1025 const gchar *group_name)
1027 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
1028 SipeContactList *contact_list = telepathy_private->contact_list;
1029 gchar *group = g_hash_table_lookup(contact_list->groups,
1030 group_name);
1032 if (!group) {
1033 group = g_strdup(group_name);
1034 g_hash_table_insert(contact_list->groups, group, group);
1035 tp_base_contact_list_groups_created(TP_BASE_CONTACT_LIST(contact_list),
1036 &group_name,
1040 return(group != NULL);
1043 void sipe_backend_buddy_group_remove(struct sipe_core_public *sipe_public,
1044 const gchar *group_name)
1046 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
1047 SipeContactList *contact_list = telepathy_private->contact_list;
1049 g_hash_table_remove(contact_list->groups, group_name);
1051 if (contact_list->initial_received) {
1052 /* @TODO: emit signal? */
1057 Local Variables:
1058 mode: c
1059 c-file-style: "bsd"
1060 indent-tabs-mode: t
1061 tab-width: 8
1062 End: