2 * @file telepathy-buddy.c
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
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 */
48 /* includes alias as stored on the server */
49 gchar
*info
[SIPE_INFO_FIELD_MAX
];
50 gchar
*hash
; /* photo hash */
54 struct telepathy_buddy_entry
{
55 struct telepathy_buddy
*buddy
; /* pointer to parent */
56 const gchar
*group
; /* borrowed from contact_list->groups key */
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
;
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, \
93 * Contact List class - type definition
95 static void contact_group_list_iface_init(TpContactGroupListInterface
*);
96 G_DEFINE_TYPE_WITH_CODE(SipeContactList
,
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
)
120 SIPE_DEBUG_INFO_NOFORMAT("SipeContactList::dup_states - NOT IMPLEMENTED");
123 *subscribe
= TP_SUBSCRIPTION_STATE_YES
;
125 *publish
= TP_SUBSCRIPTION_STATE_YES
;
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
;
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
);
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
,
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
,
190 self
->initial_received
= FALSE
;
194 * Contact List class - interface implementation
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);
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
);
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
);
234 static GStrv
dup_contact_groups(TpBaseContactList
*contact_list
,
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");
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
,
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
);
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
,
277 /* get & set alias for a contact */
278 const gchar
*sipe_telepathy_buddy_get_alias(SipeContactList
*contact_list
,
281 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddy_handles
,
282 GUINT_TO_POINTER(contact
));
285 return(buddy
->info
[SIPE_BUDDY_INFO_DISPLAY_NAME
]);
288 static void update_alias(struct telepathy_buddy
*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
,
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 */
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,
314 /* get photo hash for a contact */
315 const gchar
*sipe_telepathy_buddy_get_hash(struct _SipeContactList
*contact_list
,
318 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddy_handles
,
319 GUINT_TO_POINTER(contact
));
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
));
332 return(SIPE_ACTIVITY_UNSET
);
333 return(buddy
->activity
);
336 /* @TODO: are other MIME types supported by OCS? */
337 static const char * mimetypes
[] = {
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
),
353 tp_svc_connection_interface_avatars_return_from_get_avatar_requirements(
356 AVATAR_MIN_PX
, AVATAR_MIN_PX
,
357 AVATAR_MAX_PX
, AVATAR_MAX_PX
,
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( \
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); */
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
;
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
];
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,
428 G_TYPE_STRV
, field_values
,
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
;
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
);
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
);
469 g_hash_table_insert(infos
,
470 GUINT_TO_POINTER(contact
),
474 tp_svc_connection_interface_contact_info_return_from_get_contact_info(context
,
476 g_boxed_free(TP_HASH_TYPE_CONTACT_INFO_MAP
, infos
);
479 static void request_contact_info(TpSvcConnectionInterfaceContactInfo
*iface
,
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
;
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
);
502 info
= convert_contact_info(buddy
);
504 dbus_g_method_return_error(context
, error
);
510 tp_svc_connection_interface_contact_info_return_from_request_contact_info(context
,
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( \
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); */
531 GPtrArray
*sipe_telepathy_contact_info_fields(void)
533 GPtrArray
*fields
= dbus_g_type_specialized_construct(TP_ARRAY_TYPE_FIELD_SPECS
);
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
];
542 /* unsupported field */
546 va
= tp_value_array_build(4,
547 G_TYPE_STRING
, vcard_name
,
549 G_TYPE_UINT
, 0, /* tp_flags */
550 G_TYPE_UINT
, 1, /* max_times */
552 g_ptr_array_add (fields
, va
);
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), */
569 .name
= "SupportedFields",
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
,
594 return(g_hash_table_lookup(buddy
->groups
, group_name
));
596 /* just return the first entry */
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
);
606 static GSList
*buddy_add_all(struct telepathy_buddy
*buddy
, GSList
*list
)
609 struct telepathy_buddy_entry
*buddy_entry
;
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
);
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 */
629 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
630 GHashTable
*buddies
= telepathy_private
->contact_list
->buddies
;
633 result
= buddy_add_all(g_hash_table_lookup(buddies
,
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
);
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
)
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
,
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
)
706 SIPE_DEBUG_INFO("sipe_backend_buddy_set_string: %s replacing info %d: %s -> %s",
708 buddy
->info
[key
] ? buddy
->info
[key
]: "<UNDEFINED>",
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
,
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
,
725 GPtrArray
*info
= convert_contact_info(buddy
);
728 tp_svc_connection_interface_contact_info_emit_contact_info_changed(telepathy_private
->connection
,
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
,
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
,
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
,
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'",
761 sipe_telepathy_connection_alias_updated(contact_list
->connection
,
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
;
791 g_hash_table_destroy(buddy
->groups
);
792 for (i
= 0; i
< SIPE_INFO_FIELD_MAX
; i
++)
793 g_free(buddy
->info
[i
]);
798 sipe_backend_buddy
sipe_backend_buddy_add(struct sipe_core_public
*sipe_public
,
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
,
807 struct telepathy_buddy
*buddy
= g_hash_table_lookup(contact_list
->buddies
,
809 struct telepathy_buddy_entry
*buddy_entry
;
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
,
819 buddy
->info
[SIPE_BUDDY_INFO_DISPLAY_NAME
] = g_strdup(alias
);
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 */
828 g_hash_table_insert(contact_list
->buddy_handles
,
829 GUINT_TO_POINTER(buddy
->handle
),
833 buddy_entry
= g_hash_table_lookup(buddy
->groups
, group
);
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 */
843 if (contact_list
->initial_received
) {
844 /* @TODO: emit signal? */
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
,
866 g_hash_table_remove(contact_list
->buddy_handles
,
867 GUINT_TO_POINTER(buddy
->handle
));
868 g_hash_table_remove(contact_list
->buddies
,
873 if (contact_list
->initial_received
) {
874 /* @TODO: emit signal? */
878 void sipe_backend_buddy_set_status(struct sipe_core_public
*sipe_public
,
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
,
887 TpPresenceStatus
*status
;
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)
907 static void buddy_photo_updated(struct sipe_backend_private
*telepathy_private
,
908 struct telepathy_buddy
*buddy
,
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
,
922 tp_svc_connection_interface_avatars_emit_avatar_retrieved(telepathy_private
->connection
,
926 /* @TODO: is this correct? */
928 g_array_unref(array
);
931 void sipe_backend_buddy_set_photo(struct sipe_core_public
*sipe_public
,
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
,
942 gchar
*hash_file
= g_build_filename(telepathy_private
->cache_dir
,
946 /* does this buddy already have a photo? -> delete it */
948 char *photo_file
= g_build_filename(telepathy_private
->cache_dir
,
951 (void) g_remove(photo_file
);
957 /* update hash file */
958 if (g_file_set_contents(hash_file
,
962 gchar
*photo_file
= g_build_filename(telepathy_private
->cache_dir
,
965 buddy
->hash
= g_strdup(photo_hash
);
966 g_file_set_contents(photo_file
,
971 buddy_photo_updated(telepathy_private
,
985 const gchar
*sipe_backend_buddy_get_photo_hash(struct sipe_core_public
*sipe_public
,
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
,
996 gchar
*hash_file
= g_build_filename(telepathy_private
->cache_dir
,
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
,
1004 gchar
*image_data
= NULL
;
1007 if (g_file_get_contents(photo_file
,
1011 buddy_photo_updated(telepathy_private
,
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
,
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
),
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? */