ucs: add support to remove buddy from list
[siplcs.git] / src / core / sipe-buddy.c
blob75a890a02584f0ac0466102ce7ce594f6e74f0ae
1 /**
2 * @file sipe-buddy.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2013 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 <stdlib.h>
28 #include <string.h>
29 #include <time.h>
31 #include <glib.h>
33 #include "sipe-common.h"
34 #include "sipmsg.h"
35 #include "sip-csta.h"
36 #include "sip-soap.h"
37 #include "sip-transport.h"
38 #include "sipe-backend.h"
39 #include "sipe-buddy.h"
40 #include "sipe-cal.h"
41 #include "sipe-chat.h"
42 #include "sipe-conf.h"
43 #include "sipe-core.h"
44 #include "sipe-core-private.h"
45 #include "sipe-group.h"
46 #include "sipe-http.h"
47 #include "sipe-im.h"
48 #include "sipe-nls.h"
49 #include "sipe-ocs2005.h"
50 #include "sipe-ocs2007.h"
51 #include "sipe-schedule.h"
52 #include "sipe-session.h"
53 #include "sipe-status.h"
54 #include "sipe-subscriptions.h"
55 #include "sipe-svc.h"
56 #include "sipe-ucs.h"
57 #include "sipe-utils.h"
58 #include "sipe-webticket.h"
59 #include "sipe-xml.h"
61 struct sipe_buddies {
62 GHashTable *uri;
63 GHashTable *exchange_key;
65 /* Pending photo download HTTP requests */
66 GSList *pending_photo_requests;
69 struct photo_response_data {
70 gchar *who;
71 gchar *photo_hash;
72 struct sipe_http_request *request;
75 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
76 const gchar *uri);
77 static void photo_response_data_free(struct photo_response_data *data);
79 struct sipe_buddy *sipe_buddy_add(struct sipe_core_private *sipe_private,
80 const gchar *uri,
81 const gchar *exchange_key,
82 const gchar *change_key)
84 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
85 gchar *normalized_uri = g_ascii_strdown(uri, -1);
86 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
87 normalized_uri);
89 if (!buddy) {
90 struct sipe_buddies *buddies = sipe_private->buddies;
92 buddy = g_new0(struct sipe_buddy, 1);
93 buddy->name = normalized_uri;
94 g_hash_table_insert(buddies->uri,
95 buddy->name,
96 buddy);
98 if (exchange_key) {
99 buddy->exchange_key = g_strdup(exchange_key);
100 g_hash_table_insert(buddies->exchange_key,
101 buddy->exchange_key,
102 buddy);
104 if (change_key)
105 buddy->change_key = g_strdup(change_key);
108 SIPE_DEBUG_INFO("sipe_buddy_add: Added buddy %s", normalized_uri);
110 buddy_fetch_photo(sipe_private, normalized_uri);
112 normalized_uri = NULL; /* buddy takes ownership */
113 } else {
114 SIPE_DEBUG_INFO("sipe_buddy_add: Buddy %s already exists", normalized_uri);
116 g_free(normalized_uri);
118 return(buddy);
121 void sipe_buddy_add_to_group(struct sipe_core_private *sipe_private,
122 struct sipe_buddy *buddy,
123 struct sipe_group *group,
124 const gchar *alias)
126 const gchar *uri = buddy->name;
127 const gchar *group_name = group->name;
128 sipe_backend_buddy bb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
129 uri,
130 group_name);
132 if (!bb) {
133 bb = sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
134 uri,
135 alias,
136 group_name);
137 SIPE_DEBUG_INFO("sipe_buddy_add_to_group: created backend buddy '%s' with alias '%s'",
138 uri, alias ? alias : "<NONE>");
142 if (!is_empty(alias)) {
143 gchar *old_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
144 bb);
146 if (sipe_strcase_equal(sipe_get_no_sip_uri(uri),
147 old_alias)) {
148 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
150 alias);
151 SIPE_DEBUG_INFO("sipe_buddy_add_to_group: replaced alias for buddy '%s': old '%s' new '%s'",
152 uri, old_alias, alias);
154 g_free(old_alias);
157 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
158 group,
159 (GCompareFunc) sipe_group_compare,
160 NULL);
161 SIPE_DEBUG_INFO("sipe_buddy_add_to_group: added buddy %s to group %s",
162 uri, group_name);
165 void sipe_buddy_cleanup_local_list(struct sipe_core_private *sipe_private)
167 GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC,
168 NULL,
169 NULL);
170 GSList *entry = buddies;
172 SIPE_DEBUG_INFO("sipe_buddy_cleanup_local_list: overall %d backend buddies (including clones)",
173 g_slist_length(buddies));
174 SIPE_DEBUG_INFO("sipe_buddy_cleanup_local_list: %d sipe buddies (unique)",
175 sipe_buddy_count(sipe_private));
176 while (entry) {
177 sipe_backend_buddy bb = entry->data;
178 gchar *bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC,
179 bb);
180 gchar *gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC,
181 bb);
182 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
183 bname);
184 gboolean in_sipe_groups = FALSE;
186 if (buddy) {
187 GSList *entry2 = buddy->groups;
189 while (entry2) {
190 struct sipe_group *group = entry2->data;
191 if (sipe_strequal(group->name, gname)) {
192 in_sipe_groups = TRUE;
193 break;
195 entry2 = entry2->next;
199 if (!in_sipe_groups) {
200 SIPE_DEBUG_INFO("sipe_buddy_cleanup_local_list: REMOVING '%s' from local group '%s', as buddy is not in that group on remote contact list",
201 bname, gname);
202 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, bb);
205 g_free(gname);
206 g_free(bname);
208 entry = entry->next;
211 g_slist_free(buddies);
214 struct sipe_buddy *sipe_buddy_find_by_uri(struct sipe_core_private *sipe_private,
215 const gchar *uri)
217 return(g_hash_table_lookup(sipe_private->buddies->uri, uri));
220 struct sipe_buddy *sipe_buddy_find_by_exchange_key(struct sipe_core_private *sipe_private,
221 const gchar *exchange_key)
223 return(g_hash_table_lookup(sipe_private->buddies->exchange_key,
224 exchange_key));
227 void sipe_buddy_foreach(struct sipe_core_private *sipe_private,
228 GHFunc callback,
229 gpointer callback_data)
231 g_hash_table_foreach(sipe_private->buddies->uri,
232 callback,
233 callback_data);
236 static void buddy_free(struct sipe_buddy *buddy)
238 #ifndef _WIN32
240 * We are calling g_hash_table_foreach_steal(). That means that no
241 * key/value deallocation functions are called. Therefore the glib
242 * hash code does not touch the key (buddy->name) or value (buddy)
243 * of the to-be-deleted hash node at all. It follows that we
245 * - MUST free the memory for the key ourselves and
246 * - ARE allowed to do it in this function
248 * Conclusion: glib must be broken on the Windows platform if sipe
249 * crashes with SIGTRAP when closing. You'll have to live
250 * with the memory leak until this is fixed.
252 g_free(buddy->name);
253 #endif
254 g_free(buddy->exchange_key);
255 g_free(buddy->change_key);
256 g_free(buddy->activity);
257 g_free(buddy->meeting_subject);
258 g_free(buddy->meeting_location);
259 g_free(buddy->note);
261 g_free(buddy->cal_start_time);
262 g_free(buddy->cal_free_busy_base64);
263 g_free(buddy->cal_free_busy);
264 g_free(buddy->last_non_cal_activity);
266 sipe_cal_free_working_hours(buddy->cal_working_hours);
268 g_free(buddy->device_name);
269 g_slist_free(buddy->groups);
270 g_free(buddy);
273 static gboolean buddy_free_cb(SIPE_UNUSED_PARAMETER gpointer key,
274 gpointer buddy,
275 SIPE_UNUSED_PARAMETER gpointer user_data)
277 buddy_free(buddy);
278 /* We must return TRUE as the key/value have already been deleted */
279 return(TRUE);
282 void sipe_buddy_free(struct sipe_core_private *sipe_private)
284 struct sipe_buddies *buddies = sipe_private->buddies;
286 g_hash_table_foreach_steal(buddies->uri,
287 buddy_free_cb,
288 NULL);
290 /* core is being deallocated, remove all its pending photo requests */
291 while (buddies->pending_photo_requests) {
292 struct photo_response_data *data =
293 buddies->pending_photo_requests->data;
294 buddies->pending_photo_requests =
295 g_slist_remove(buddies->pending_photo_requests, data);
296 photo_response_data_free(data);
299 g_hash_table_destroy(buddies->uri);
300 g_hash_table_destroy(buddies->exchange_key);
301 g_free(buddies);
302 sipe_private->buddies = NULL;
305 gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public,
306 const gchar *uri,
307 guint activity,
308 const gchar *status_text)
310 struct sipe_buddy *sbuddy;
311 const char *activity_str;
313 if (!sipe_public) return NULL; /* happens on pidgin exit */
315 sbuddy = sipe_buddy_find_by_uri(SIPE_CORE_PRIVATE, uri);
316 if (!sbuddy) return NULL;
318 activity_str = sbuddy->activity ? sbuddy->activity :
319 (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
320 status_text : NULL;
322 if (activity_str && sbuddy->note) {
323 return g_strdup_printf("%s - <i>%s</i>", activity_str, sbuddy->note);
324 } else if (activity_str) {
325 return g_strdup(activity_str);
326 } else if (sbuddy->note) {
327 return g_strdup_printf("<i>%s</i>", sbuddy->note);
328 } else {
329 return NULL;
333 gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private,
334 const gchar *with)
336 sipe_backend_buddy pbuddy;
337 gchar *alias = NULL;
338 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
339 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
341 return alias;
344 void sipe_core_buddy_group(struct sipe_core_public *sipe_public,
345 const gchar *who,
346 const gchar *old_group_name,
347 const gchar *new_group_name)
349 struct sipe_buddy * buddy = sipe_buddy_find_by_uri(SIPE_CORE_PRIVATE,
350 who);
351 struct sipe_group * old_group = NULL;
352 struct sipe_group * new_group;
354 SIPE_DEBUG_INFO("sipe_core_buddy_group: who:%s old_group_name:%s new_group_name:%s",
355 who ? who : "", old_group_name ? old_group_name : "", new_group_name ? new_group_name : "");
357 if(!buddy) { // buddy not in roaming list
358 return;
361 if (old_group_name) {
362 old_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, old_group_name);
364 new_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, new_group_name);
366 if (old_group) {
367 buddy->groups = g_slist_remove(buddy->groups, old_group);
368 SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy %s removed from old group %s", who, old_group_name);
371 if (!new_group) {
372 sipe_group_create(SIPE_CORE_PRIVATE, new_group_name, who);
373 } else {
374 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
375 new_group,
376 (GCompareFunc)sipe_group_compare,
377 NULL);
378 sipe_group_update_buddy(SIPE_CORE_PRIVATE, buddy);
382 void sipe_core_buddy_add(struct sipe_core_public *sipe_public,
383 const gchar *uri,
384 const gchar *group_name)
386 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
388 if (!sipe_buddy_find_by_uri(sipe_private, uri)) {
389 struct sipe_buddy *b = sipe_buddy_add(sipe_private,
390 uri,
391 NULL,
392 NULL);
393 b->just_added = TRUE;
395 sipe_subscribe_presence_single_cb(sipe_private, b->name);
397 } else {
398 SIPE_DEBUG_INFO("sipe_core_buddy_add: buddy %s already in internal list",
399 uri);
402 sipe_core_buddy_group(sipe_public,
403 uri,
404 NULL,
405 group_name);
408 void sipe_buddy_remove(struct sipe_core_private *sipe_private,
409 struct sipe_buddy *buddy)
411 struct sipe_buddies *buddies = sipe_private->buddies;
412 gchar *action_name = sipe_utils_presence_key(buddy->name);
413 sipe_schedule_cancel(sipe_private, action_name);
414 g_free(action_name);
416 g_hash_table_remove(buddies->uri, buddy->name);
417 if (buddy->exchange_key)
418 g_hash_table_remove(buddies->exchange_key,
419 buddy->exchange_key);
421 buddy_free(buddy);
425 * Unassociates buddy from group first.
426 * Then see if no groups left, removes buddy completely.
427 * Otherwise updates buddy groups on server.
429 void sipe_core_buddy_remove(struct sipe_core_public *sipe_public,
430 const gchar *uri,
431 const gchar *group_name)
433 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
434 struct sipe_buddy *b = sipe_buddy_find_by_uri(sipe_private,
435 uri);
436 struct sipe_group *g = NULL;
438 if (!b) return;
440 if (group_name) {
441 g = sipe_group_find_by_name(sipe_private, group_name);
442 if (g) {
443 b->groups = g_slist_remove(b->groups, g);
444 SIPE_DEBUG_INFO("sipe_core_buddy_remove: buddy %s removed from group %s",
445 uri, g->name);
449 if (g_slist_length(b->groups) < 1) {
451 if (sipe_ucs_is_migrated(sipe_private)) {
452 sipe_ucs_group_remove_buddy(sipe_private,
455 } else {
456 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
457 b->name);
458 sip_soap_request(sipe_private,
459 "deleteContact",
460 request);
461 g_free(request);
464 sipe_buddy_remove(sipe_private, b);
465 } else {
466 if (sipe_ucs_is_migrated(sipe_private)) {
467 sipe_ucs_group_remove_buddy(sipe_private,
470 } else
471 /* updates groups on server */
472 sipe_group_update_buddy(sipe_private, b);
476 void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
477 const gchar *uri,
478 guint activity)
480 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
481 struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private,
482 uri);
484 if (!sbuddy) return;
486 /* Check if on 2005 system contact's calendar,
487 * then set/preserve it.
489 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
490 sipe_backend_buddy_set_status(sipe_public, uri, activity);
491 } else {
492 sipe_ocs2005_apply_calendar_status(sipe_private,
493 sbuddy,
494 sipe_status_activity_to_token(activity));
498 void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public,
499 const gchar *uri,
500 const gchar *status_name,
501 gboolean is_online,
502 struct sipe_backend_buddy_tooltip *tooltip)
504 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
505 gchar *note = NULL;
506 gboolean is_oof_note = FALSE;
507 const gchar *activity = NULL;
508 gchar *calendar = NULL;
509 const gchar *meeting_subject = NULL;
510 const gchar *meeting_location = NULL;
511 gchar *access_text = NULL;
513 #define SIPE_ADD_BUDDY_INFO(l, t) \
515 gchar *tmp = g_markup_escape_text((t), -1); \
516 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), tmp); \
517 g_free(tmp); \
519 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) \
520 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), (t))
522 if (sipe_public) { /* happens on pidgin exit */
523 struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private,
524 uri);
525 if (sbuddy) {
526 note = sbuddy->note;
527 is_oof_note = sbuddy->is_oof_note;
528 activity = sbuddy->activity;
529 calendar = sipe_cal_get_description(sbuddy);
530 meeting_subject = sbuddy->meeting_subject;
531 meeting_location = sbuddy->meeting_location;
533 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
534 gboolean is_group_access = FALSE;
535 const int container_id = sipe_ocs2007_find_access_level(sipe_private,
536 "user",
537 sipe_get_no_sip_uri(uri),
538 &is_group_access);
539 const char *access_level = sipe_ocs2007_access_level_name(container_id);
540 access_text = is_group_access ?
541 g_strdup(access_level) :
542 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT,
543 access_level);
547 if (is_online) {
548 const gchar *status_str = activity ? activity : status_name;
550 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
552 if (is_online && !is_empty(calendar)) {
553 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
555 g_free(calendar);
556 if (!is_empty(meeting_location)) {
557 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", uri, meeting_location);
558 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
560 if (!is_empty(meeting_subject)) {
561 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", uri, meeting_subject);
562 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
564 if (note) {
565 gchar *note_italics = g_strdup_printf("<i>%s</i>", note);
566 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", uri, note);
567 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
568 note_italics);
569 g_free(note_italics);
571 if (access_text) {
572 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
573 g_free(access_text);
577 void sipe_buddy_update_property(struct sipe_core_private *sipe_private,
578 const char *uri,
579 sipe_buddy_info_fields propkey,
580 char *property_value)
582 GSList *buddies, *entry;
584 if (property_value)
585 property_value = g_strstrip(property_value);
587 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
588 while (entry) {
589 gchar *prop_str;
590 sipe_backend_buddy p_buddy = entry->data;
592 /* for Display Name */
593 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
594 gchar *alias;
595 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
596 if (property_value && sipe_is_bad_alias(uri, alias)) {
597 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
598 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
600 g_free(alias);
602 alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
603 if (!is_empty(property_value) &&
604 (!sipe_strequal(property_value, alias) || is_empty(alias)) )
606 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
607 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
609 g_free(alias);
611 /* for other properties */
612 else {
613 if (!is_empty(property_value)) {
614 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
615 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
616 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
618 g_free(prop_str);
622 entry = entry->next;
624 g_slist_free(buddies);
628 struct ms_dlx_data;
629 struct ms_dlx_data {
630 GSList *search_rows;
631 gchar *other;
632 guint max_returns;
633 sipe_svc_callback *callback;
634 struct sipe_svc_session *session;
635 gchar *wsse_security;
636 struct sipe_backend_search_token *token;
637 /* must call ms_dlx_free() */
638 void (*failed_callback)(struct sipe_core_private *sipe_private,
639 struct ms_dlx_data *mdd);
642 static void ms_dlx_free(struct ms_dlx_data *mdd)
644 sipe_utils_slist_free_full(mdd->search_rows, g_free);
645 sipe_svc_session_close(mdd->session);
646 g_free(mdd->other);
647 g_free(mdd->wsse_security);
648 g_free(mdd);
651 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
652 #define DLX_SEARCH_ITEM \
653 "<AbEntryRequest.ChangeSearchQuery>" \
654 " <SearchOn>%s</SearchOn>" \
655 " <Value>%s</Value>" \
656 "</AbEntryRequest.ChangeSearchQuery>"
658 static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) {
659 gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1);
660 guint i = 0;
661 gchar *query = NULL;
663 while (query_rows) {
664 gchar *attr;
665 gchar *value;
667 attr = query_rows->data;
668 query_rows = g_slist_next(query_rows);
669 value = query_rows->data;
670 query_rows = g_slist_next(query_rows);
672 if (!attr || !value)
673 break;
675 attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW,
676 attr, value);
678 attrs[i] = NULL;
680 if (i) {
681 query = g_strjoinv(NULL, attrs);
682 SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s",
683 query ? query : "");
686 g_strfreev(attrs);
688 return query;
691 static void ms_dlx_webticket(struct sipe_core_private *sipe_private,
692 const gchar *base_uri,
693 const gchar *auth_uri,
694 const gchar *wsse_security,
695 SIPE_UNUSED_PARAMETER const gchar *failure_msg,
696 gpointer callback_data)
698 struct ms_dlx_data *mdd = callback_data;
700 if (wsse_security) {
701 gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
703 SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
704 base_uri);
706 if (sipe_svc_ab_entry_request(sipe_private,
707 mdd->session,
708 auth_uri,
709 wsse_security,
710 query,
711 g_slist_length(mdd->search_rows) / 2,
712 mdd->max_returns,
713 mdd->callback,
714 mdd)) {
716 /* keep webticket security token for potential further use */
717 mdd->wsse_security = g_strdup(wsse_security);
719 /* callback data passed down the line */
720 mdd = NULL;
722 g_free(query);
724 } else {
725 /* no ticket: this will show the minmum information */
726 SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
727 base_uri);
730 if (mdd)
731 mdd->failed_callback(sipe_private, mdd);
734 static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private,
735 struct ms_dlx_data *mdd)
737 if (!sipe_webticket_request(sipe_private,
738 mdd->session,
739 sipe_private->dlx_uri,
740 "AddressBookWebTicketBearer",
741 ms_dlx_webticket,
742 mdd)) {
743 SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
744 sipe_private->dlx_uri);
745 mdd->failed_callback(sipe_private, mdd);
749 static void search_contacts_finalize(struct sipe_core_private *sipe_private,
750 struct sipe_backend_search_results *results,
751 guint match_count,
752 gboolean more)
754 gchar *secondary = g_strdup_printf(
755 dngettext(PACKAGE_NAME,
756 "Found %d contact%s:",
757 "Found %d contacts%s:", match_count),
758 match_count, more ? _(" (more matched your query)") : "");
760 sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
761 results,
762 secondary,
763 more);
764 g_free(secondary);
767 static void search_ab_entry_response(struct sipe_core_private *sipe_private,
768 const gchar *uri,
769 SIPE_UNUSED_PARAMETER const gchar *raw,
770 sipe_xml *soap_body,
771 gpointer callback_data)
773 struct ms_dlx_data *mdd = callback_data;
775 if (soap_body) {
776 const sipe_xml *node;
777 struct sipe_backend_search_results *results;
778 GHashTable *found;
780 SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
781 uri);
783 /* any matches? */
784 node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
785 if (!node) {
786 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
787 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
788 mdd->token,
789 _("No contacts found"));
790 ms_dlx_free(mdd);
791 return;
794 /* OK, we found something - show the results to the user */
795 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
796 mdd->token);
797 if (!results) {
798 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
799 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
800 mdd->token,
801 _("Unable to display the search results"));
802 ms_dlx_free(mdd);
803 return;
806 /* SearchAbEntryResult can contain duplicates */
807 found = g_hash_table_new_full(g_str_hash, g_str_equal,
808 g_free, NULL);
810 for (/* initialized above */ ; node; node = sipe_xml_twin(node)) {
811 const sipe_xml *attrs;
812 gchar *sip_uri = NULL;
813 gchar *displayname = NULL;
814 gchar *company = NULL;
815 gchar *country = NULL;
816 gchar *email = NULL;
818 for (attrs = sipe_xml_child(node, "Attributes/Attribute");
819 attrs;
820 attrs = sipe_xml_twin(attrs)) {
821 gchar *name = sipe_xml_data(sipe_xml_child(attrs,
822 "Name"));
823 gchar *value = sipe_xml_data(sipe_xml_child(attrs,
824 "Value"));
826 if (!is_empty(value)) {
827 if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) {
828 g_free(sip_uri);
829 sip_uri = value;
830 value = NULL;
831 } else if (sipe_strcase_equal(name, "displayname")) {
832 g_free(displayname);
833 displayname = value;
834 value = NULL;
835 } else if (sipe_strcase_equal(name, "mail")) {
836 g_free(email);
837 email = value;
838 value = NULL;
839 } else if (sipe_strcase_equal(name, "company")) {
840 g_free(company);
841 company = value;
842 value = NULL;
843 } else if (sipe_strcase_equal(name, "country")) {
844 g_free(country);
845 country = value;
846 value = NULL;
850 g_free(value);
851 g_free(name);
854 if (sip_uri && !g_hash_table_lookup(found, sip_uri)) {
855 gchar **uri_parts = g_strsplit(sip_uri, ":", 2);
856 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
857 results,
858 uri_parts[1],
859 displayname,
860 company,
861 country,
862 email);
863 g_strfreev(uri_parts);
865 g_hash_table_insert(found, sip_uri, (gpointer) TRUE);
866 sip_uri = NULL;
869 g_free(email);
870 g_free(country);
871 g_free(company);
872 g_free(displayname);
873 g_free(sip_uri);
876 search_contacts_finalize(sipe_private, results,
877 g_hash_table_size(found),
878 FALSE);
879 g_hash_table_destroy(found);
880 ms_dlx_free(mdd);
882 } else {
883 mdd->failed_callback(sipe_private, mdd);
887 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
888 struct sipmsg *msg,
889 struct transaction *trans)
891 struct sipe_backend_search_token *token = trans->payload->data;
892 struct sipe_backend_search_results *results;
893 sipe_xml *searchResults;
894 const sipe_xml *mrow;
895 guint match_count = 0;
896 gboolean more = FALSE;
898 /* valid response? */
899 if (msg->response != 200) {
900 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
901 msg->response);
902 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
903 token,
904 _("Contact search failed"));
905 return(FALSE);
908 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
910 /* valid XML? */
911 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
912 if (!searchResults) {
913 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
914 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
915 token,
916 _("Contact search failed"));
917 return(FALSE);
920 /* any matches? */
921 mrow = sipe_xml_child(searchResults, "Body/Array/row");
922 if (!mrow) {
923 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
924 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
925 token,
926 _("No contacts found"));
928 sipe_xml_free(searchResults);
929 return(FALSE);
932 /* OK, we found something - show the results to the user */
933 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
934 trans->payload->data);
935 if (!results) {
936 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
937 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
938 token,
939 _("Unable to display the search results"));
941 sipe_xml_free(searchResults);
942 return FALSE;
945 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
946 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
947 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
948 results,
949 uri_parts[1],
950 sipe_xml_attribute(mrow, "displayName"),
951 sipe_xml_attribute(mrow, "company"),
952 sipe_xml_attribute(mrow, "country"),
953 sipe_xml_attribute(mrow, "email"));
954 g_strfreev(uri_parts);
955 match_count++;
958 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
959 char *data = sipe_xml_data(mrow);
960 more = (g_ascii_strcasecmp(data, "true") == 0);
961 g_free(data);
964 search_contacts_finalize(sipe_private, results, match_count, more);
965 sipe_xml_free(searchResults);
967 return(TRUE);
970 static void search_soap_request(struct sipe_core_private *sipe_private,
971 struct sipe_backend_search_token *token,
972 GSList *search_rows)
974 gchar *query = prepare_buddy_search_query(search_rows, FALSE);
975 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
977 payload->data = token;
979 sip_soap_directory_search(sipe_private,
980 100,
981 query,
982 process_search_contact_response,
983 payload);
984 g_free(query);
987 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
988 struct ms_dlx_data *mdd)
990 /* error using [MS-DLX] server, retry using Active Directory */
991 search_soap_request(sipe_private, mdd->token, mdd->search_rows);
992 ms_dlx_free(mdd);
995 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
996 struct sipe_backend_search_token *token,
997 const gchar *given_name,
998 const gchar *surname,
999 const gchar *email,
1000 const gchar *company,
1001 const gchar *country)
1003 GSList *query_rows = NULL;
1005 #define ADD_QUERY_ROW(attr, val) \
1006 if (val) { \
1007 query_rows = g_slist_append(query_rows, g_strdup(attr)); \
1008 query_rows = g_slist_append(query_rows, g_strdup(val)); \
1011 ADD_QUERY_ROW("givenName", given_name);
1012 ADD_QUERY_ROW("sn", surname);
1013 ADD_QUERY_ROW("mail", email);
1014 ADD_QUERY_ROW("company", company);
1015 ADD_QUERY_ROW("c", country);
1017 if (query_rows) {
1018 if (SIPE_CORE_PRIVATE->dlx_uri != NULL) {
1019 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1021 mdd->search_rows = query_rows;
1022 mdd->max_returns = 100;
1023 mdd->callback = search_ab_entry_response;
1024 mdd->failed_callback = search_ab_entry_failed;
1025 mdd->session = sipe_svc_session_start();
1026 mdd->token = token;
1028 ms_dlx_webticket_request(SIPE_CORE_PRIVATE, mdd);
1030 } else {
1031 /* no [MS-DLX] server, use Active Directory search instead */
1032 search_soap_request(SIPE_CORE_PRIVATE, token, query_rows);
1033 sipe_utils_slist_free_full(query_rows, g_free);
1035 } else
1036 sipe_backend_search_failed(sipe_public,
1037 token,
1038 _("Invalid contact search query"));
1041 static void get_info_finalize(struct sipe_core_private *sipe_private,
1042 struct sipe_backend_buddy_info *info,
1043 const gchar *uri,
1044 const gchar *server_alias,
1045 const gchar *email)
1047 sipe_backend_buddy bbuddy;
1048 struct sipe_buddy *sbuddy;
1049 gchar *alias;
1050 gchar *value;
1052 if (!info) {
1053 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1054 } else {
1055 sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info);
1057 if (!info)
1058 return;
1060 bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
1062 if (is_empty(server_alias)) {
1063 value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
1064 bbuddy);
1065 if (value) {
1066 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1067 info,
1068 SIPE_BUDDY_INFO_DISPLAY_NAME,
1069 value);
1071 } else {
1072 value = g_strdup(server_alias);
1075 /* present alias if it differs from server alias */
1076 alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy);
1077 if (alias && !sipe_strequal(alias, value))
1079 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1080 info,
1081 SIPE_BUDDY_INFO_ALIAS,
1082 alias);
1084 g_free(alias);
1085 g_free(value);
1087 if (is_empty(email)) {
1088 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1089 bbuddy,
1090 SIPE_BUDDY_INFO_EMAIL);
1091 if (value) {
1092 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1093 info,
1094 SIPE_BUDDY_INFO_EMAIL,
1095 value);
1096 g_free(value);
1100 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1101 bbuddy,
1102 SIPE_BUDDY_INFO_SITE);
1103 if (value) {
1104 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1105 info,
1106 SIPE_BUDDY_INFO_SITE,
1107 value);
1108 g_free(value);
1111 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
1112 if (sbuddy && sbuddy->device_name) {
1113 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1114 info,
1115 SIPE_BUDDY_INFO_DEVICE,
1116 sbuddy->device_name);
1119 sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri);
1123 static void get_info_ab_entry_response(struct sipe_core_private *sipe_private,
1124 const gchar *uri,
1125 SIPE_UNUSED_PARAMETER const gchar *raw,
1126 sipe_xml *soap_body,
1127 gpointer callback_data)
1129 struct ms_dlx_data *mdd = callback_data;
1130 struct sipe_backend_buddy_info *info = NULL;
1131 gchar *server_alias = NULL;
1132 gchar *email = NULL;
1134 if (soap_body) {
1135 const sipe_xml *node;
1137 SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s",
1138 uri);
1140 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1142 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
1143 node;
1144 node = sipe_xml_twin(node)) {
1145 gchar *name = sipe_xml_data(sipe_xml_child(node,
1146 "Name"));
1147 gchar *value = sipe_xml_data(sipe_xml_child(node,
1148 "Value"));
1149 const sipe_xml *values = sipe_xml_child(node,
1150 "Values");
1152 /* Single value entries */
1153 if (!is_empty(value)) {
1155 if (sipe_strcase_equal(name, "displayname")) {
1156 g_free(server_alias);
1157 server_alias = value;
1158 value = NULL;
1159 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1160 info,
1161 SIPE_BUDDY_INFO_DISPLAY_NAME,
1162 server_alias);
1163 } else if (sipe_strcase_equal(name, "mail")) {
1164 g_free(email);
1165 email = value;
1166 value = NULL;
1167 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1168 info,
1169 SIPE_BUDDY_INFO_EMAIL,
1170 email);
1171 } else if (sipe_strcase_equal(name, "title")) {
1172 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1173 info,
1174 SIPE_BUDDY_INFO_JOB_TITLE,
1175 value);
1176 } else if (sipe_strcase_equal(name, "company")) {
1177 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1178 info,
1179 SIPE_BUDDY_INFO_COMPANY,
1180 value);
1181 } else if (sipe_strcase_equal(name, "country")) {
1182 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1183 info,
1184 SIPE_BUDDY_INFO_COUNTRY,
1185 value);
1188 } else if (values) {
1189 gchar *first = sipe_xml_data(sipe_xml_child(values,
1190 "string"));
1192 if (sipe_strcase_equal(name, "telephonenumber")) {
1193 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1194 info,
1195 SIPE_BUDDY_INFO_WORK_PHONE,
1196 first);
1199 g_free(first);
1202 g_free(value);
1203 g_free(name);
1207 /* this will show the minmum information */
1208 get_info_finalize(sipe_private,
1209 info,
1210 mdd->other,
1211 server_alias,
1212 email);
1214 g_free(email);
1215 g_free(server_alias);
1216 ms_dlx_free(mdd);
1219 static gboolean process_get_info_response(struct sipe_core_private *sipe_private,
1220 struct sipmsg *msg,
1221 struct transaction *trans)
1223 const gchar *uri = trans->payload->data;
1224 struct sipe_backend_buddy_info *info = NULL;
1225 gchar *server_alias = NULL;
1226 gchar *email = NULL;
1228 SIPE_DEBUG_INFO("Fetching %s's user info for %s",
1229 uri, sipe_private->username);
1231 if (msg->response != 200) {
1232 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
1233 } else {
1234 sipe_xml *searchResults;
1235 const sipe_xml *mrow;
1237 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s",
1238 msg->body ? msg->body : "");
1240 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1241 if (!searchResults) {
1243 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
1245 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
1246 const gchar *value;
1247 gchar *phone_number;
1249 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1251 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
1252 email = g_strdup(sipe_xml_attribute(mrow, "email"));
1253 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
1256 * For 2007 system we will take this from ContactCard -
1257 * it has cleaner tel: URIs at least
1259 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1260 char *tel_uri = sip_to_tel_uri(phone_number);
1261 /* trims its parameters, so call first */
1262 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1263 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1264 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1265 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1266 g_free(tel_uri);
1268 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC,
1269 uri);
1272 if (!is_empty(server_alias)) {
1273 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1274 info,
1275 SIPE_BUDDY_INFO_DISPLAY_NAME,
1276 server_alias);
1278 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1279 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1280 info,
1281 SIPE_BUDDY_INFO_JOB_TITLE,
1282 value);
1284 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1285 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1286 info,
1287 SIPE_BUDDY_INFO_OFFICE,
1288 value);
1290 if (!is_empty(phone_number)) {
1291 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1292 info,
1293 SIPE_BUDDY_INFO_WORK_PHONE,
1294 phone_number);
1296 g_free(phone_number);
1297 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1298 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1299 info,
1300 SIPE_BUDDY_INFO_COMPANY,
1301 value);
1303 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1304 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1305 info,
1306 SIPE_BUDDY_INFO_CITY,
1307 value);
1309 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1310 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1311 info,
1312 SIPE_BUDDY_INFO_STATE,
1313 value);
1315 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1316 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1317 info,
1318 SIPE_BUDDY_INFO_COUNTRY,
1319 value);
1321 if (!is_empty(email)) {
1322 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1323 info,
1324 SIPE_BUDDY_INFO_EMAIL,
1325 email);
1328 sipe_xml_free(searchResults);
1331 /* this will show the minmum information */
1332 get_info_finalize(sipe_private,
1333 info,
1334 uri,
1335 server_alias,
1336 email);
1338 g_free(server_alias);
1339 g_free(email);
1341 return TRUE;
1344 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
1345 struct ms_dlx_data *mdd)
1347 /* error using [MS-DLX] server, retry using Active Directory */
1348 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
1349 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1351 payload->destroy = g_free;
1352 payload->data = mdd->other;
1353 mdd->other = NULL;
1355 sip_soap_directory_search(sipe_private,
1357 query,
1358 process_get_info_response,
1359 payload);
1361 ms_dlx_free(mdd);
1362 g_free(query);
1365 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
1366 const gchar *who)
1368 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1370 if (sipe_private->dlx_uri) {
1371 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1373 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1374 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(who));
1376 mdd->other = g_strdup(who);
1377 mdd->max_returns = 1;
1378 mdd->callback = get_info_ab_entry_response;
1379 mdd->failed_callback = get_info_ab_entry_failed;
1380 mdd->session = sipe_svc_session_start();
1382 ms_dlx_webticket_request(sipe_private, mdd);
1384 } else {
1385 /* no [MS-DLX] server, use Active Directory search instead */
1386 gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
1387 "msRTCSIP-PrimaryUserAddress",
1388 who);
1389 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1391 SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
1392 row ? row : "");
1394 payload->destroy = g_free;
1395 payload->data = g_strdup(who);
1397 sip_soap_directory_search(sipe_private,
1399 row,
1400 process_get_info_response,
1401 payload);
1402 g_free(row);
1406 static void photo_response_data_free(struct photo_response_data *data)
1408 g_free(data->who);
1409 g_free(data->photo_hash);
1410 if (data->request) {
1411 sipe_http_request_cancel(data->request);
1413 g_free(data);
1416 static void process_buddy_photo_response(struct sipe_core_private *sipe_private,
1417 guint status,
1418 GSList *headers,
1419 const char *body,
1420 gpointer data)
1422 struct photo_response_data *rdata = (struct photo_response_data *) data;
1424 rdata->request = NULL;
1426 if (status == SIPE_HTTP_STATUS_OK) {
1427 const gchar *len_str = sipe_utils_nameval_find(headers,
1428 "Content-Length");
1429 if (len_str) {
1430 gsize photo_size = atoi(len_str);
1431 gpointer photo = g_new(char, photo_size);
1433 if (photo) {
1434 memcpy(photo, body, photo_size);
1436 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC,
1437 rdata->who,
1438 photo,
1439 photo_size,
1440 rdata->photo_hash);
1445 sipe_private->buddies->pending_photo_requests =
1446 g_slist_remove(sipe_private->buddies->pending_photo_requests, rdata);
1448 photo_response_data_free(rdata);
1451 static gchar *create_x_ms_webticket_header(const gchar *wsse_security)
1453 gchar *assertion = sipe_xml_extract_raw(wsse_security, "saml:Assertion", TRUE);
1454 gchar *wsse_security_base64;
1455 gchar *x_ms_webticket_header;
1457 if (!assertion) {
1458 return NULL;
1461 wsse_security_base64 = g_base64_encode((const guchar *)assertion,
1462 strlen(assertion));
1463 x_ms_webticket_header = g_strdup_printf("X-MS-WebTicket: opaque=%s\r\n",
1464 wsse_security_base64);
1466 g_free(assertion);
1467 g_free(wsse_security_base64);
1469 return x_ms_webticket_header;
1472 static void get_photo_ab_entry_response(struct sipe_core_private *sipe_private,
1473 const gchar *uri,
1474 SIPE_UNUSED_PARAMETER const gchar *raw,
1475 sipe_xml *soap_body,
1476 gpointer callback_data)
1478 struct ms_dlx_data *mdd = callback_data;
1479 gchar *photo_rel_path = NULL;
1480 gchar *photo_hash = NULL;
1481 const gchar *photo_hash_old =
1482 sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, mdd->other);
1484 if (soap_body) {
1485 const sipe_xml *node;
1487 SIPE_DEBUG_INFO("get_photo_ab_entry_response: received valid SOAP message from service %s",
1488 uri);
1490 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
1491 node;
1492 node = sipe_xml_twin(node)) {
1493 gchar *name = sipe_xml_data(sipe_xml_child(node, "Name"));
1494 gchar *value = sipe_xml_data(sipe_xml_child(node, "Value"));
1496 if (!is_empty(value)) {
1497 if (sipe_strcase_equal(name, "PhotoRelPath")) {
1498 g_free(photo_rel_path);
1499 photo_rel_path = value;
1500 value = NULL;
1501 } else if (sipe_strcase_equal(name, "PhotoHash")) {
1502 g_free(photo_hash);
1503 photo_hash = value;
1504 value = NULL;
1508 g_free(value);
1509 g_free(name);
1513 if (sipe_private->addressbook_uri && photo_rel_path &&
1514 photo_hash && !sipe_strequal(photo_hash, photo_hash_old)) {
1515 gchar *photo_url = g_strdup_printf("%s/%s",
1516 sipe_private->addressbook_uri, photo_rel_path);
1517 gchar *x_ms_webticket_header = create_x_ms_webticket_header(mdd->wsse_security);
1519 struct photo_response_data *data = g_new(struct photo_response_data, 1);
1520 data->who = g_strdup(mdd->other);
1521 data->photo_hash = photo_hash;
1522 photo_hash = NULL;
1524 data->request = sipe_http_request_get(sipe_private,
1525 photo_url,
1526 x_ms_webticket_header,
1527 process_buddy_photo_response,
1528 data);
1530 if (data->request) {
1531 sipe_private->buddies->pending_photo_requests =
1532 g_slist_append(sipe_private->buddies->pending_photo_requests, data);
1533 sipe_http_request_ready(data->request);
1534 } else {
1535 photo_response_data_free(data);
1538 g_free(x_ms_webticket_header);
1539 g_free(photo_url);
1542 g_free(photo_rel_path);
1543 g_free(photo_hash);
1544 ms_dlx_free(mdd);
1547 static void get_photo_ab_entry_failed(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
1548 struct ms_dlx_data *mdd)
1550 ms_dlx_free(mdd);
1553 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
1554 const gchar *uri)
1556 if (sipe_backend_uses_photo()) {
1558 /* Lync 2013 or newer: use UCS */
1559 if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013)) {
1561 sipe_ucs_get_photo(sipe_private, uri);
1563 /* Lync 2010: use [MS-DLX] */
1564 } else if (sipe_private->dlx_uri &&
1565 sipe_private->addressbook_uri) {
1566 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1568 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1569 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(uri));
1571 mdd->other = g_strdup(uri);
1572 mdd->max_returns = 1;
1573 mdd->callback = get_photo_ab_entry_response;
1574 mdd->failed_callback = get_photo_ab_entry_failed;
1575 mdd->session = sipe_svc_session_start();
1577 ms_dlx_webticket_request(sipe_private, mdd);
1582 static void buddy_refresh_photos_cb(gpointer uri,
1583 SIPE_UNUSED_PARAMETER gpointer value,
1584 gpointer sipe_private)
1586 buddy_fetch_photo(sipe_private, uri);
1589 void sipe_buddy_refresh_photos(struct sipe_core_private *sipe_private)
1591 g_hash_table_foreach(sipe_private->buddies->uri,
1592 buddy_refresh_photos_cb,
1593 sipe_private);
1596 /* Buddy menu callbacks*/
1598 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
1599 const gchar *who)
1601 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1603 /* 2007+ conference */
1604 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1605 sipe_conf_add(sipe_private, who);
1607 /* 2005- multiparty chat */
1608 } else {
1609 gchar *self = sip_uri_self(sipe_private);
1610 struct sip_session *session;
1612 session = sipe_session_add_chat(sipe_private,
1613 NULL,
1614 TRUE,
1615 self);
1616 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1617 session->chat_session,
1618 session->chat_session->title,
1619 self);
1620 g_free(self);
1622 sipe_im_invite(sipe_private, session, who,
1623 NULL, NULL, NULL, FALSE);
1627 void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public,
1628 const gchar *who)
1630 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1631 who,
1632 NULL);
1633 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1634 buddy,
1635 SIPE_BUDDY_INFO_EMAIL);
1637 if (email) {
1638 gchar *command_line = g_strdup_printf(
1639 #ifdef _WIN32
1640 "cmd /c start"
1641 #else
1642 "xdg-email"
1643 #endif
1644 " mailto:%s", email);
1645 g_free(email);
1647 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s",
1648 command_line);
1649 g_spawn_command_line_async(command_line, NULL);
1650 g_free(command_line);
1652 } else {
1653 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s",
1654 who);
1658 /* Buddy menu */
1660 static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public,
1661 struct sipe_backend_buddy_menu *menu,
1662 sipe_backend_buddy buddy,
1663 sipe_buddy_info_fields id_phone,
1664 sipe_buddy_info_fields id_display,
1665 const gchar *type)
1667 gchar *phone = sipe_backend_buddy_get_string(sipe_public,
1668 buddy,
1669 id_phone);
1670 if (phone) {
1671 gchar *display = sipe_backend_buddy_get_string(sipe_public,
1672 buddy,
1673 id_display);
1674 gchar *tmp = NULL;
1675 gchar *label = g_strdup_printf("%s %s",
1676 type,
1677 display ? display :
1678 (tmp = sip_tel_uri_denormalize(phone)));
1679 menu = sipe_backend_buddy_menu_add(sipe_public,
1680 menu,
1681 label,
1682 SIPE_BUDDY_MENU_MAKE_CALL,
1683 phone);
1684 g_free(tmp);
1685 g_free(label);
1686 g_free(display);
1687 g_free(phone);
1690 return(menu);
1693 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
1694 const gchar *buddy_name,
1695 struct sipe_backend_buddy_menu *menu)
1697 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1698 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1699 buddy_name,
1700 NULL);
1701 gchar *self = sip_uri_self(sipe_private);
1703 SIPE_SESSION_FOREACH {
1704 if (!sipe_strcase_equal(self, buddy_name) && session->chat_session)
1706 struct sipe_chat_session *chat_session = session->chat_session;
1707 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1709 if (sipe_backend_chat_find(chat_session->backend, buddy_name))
1711 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1713 if (is_conf &&
1714 /* Not conf OP */
1715 !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) &&
1716 /* We are a conf OP */
1717 conf_op) {
1718 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1719 chat_session->title);
1720 menu = sipe_backend_buddy_menu_add(sipe_public,
1721 menu,
1722 label,
1723 SIPE_BUDDY_MENU_MAKE_CHAT_LEADER,
1724 chat_session);
1725 g_free(label);
1728 if (is_conf &&
1729 /* We are a conf OP */
1730 conf_op) {
1731 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1732 chat_session->title);
1733 menu = sipe_backend_buddy_menu_add(sipe_public,
1734 menu,
1735 label,
1736 SIPE_BUDDY_MENU_REMOVE_FROM_CHAT,
1737 chat_session);
1738 g_free(label);
1741 else
1743 if (!is_conf ||
1744 (is_conf && !session->locked)) {
1745 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1746 chat_session->title);
1747 menu = sipe_backend_buddy_menu_add(sipe_public,
1748 menu,
1749 label,
1750 SIPE_BUDDY_MENU_INVITE_TO_CHAT,
1751 chat_session);
1752 g_free(label);
1756 } SIPE_SESSION_FOREACH_END;
1757 g_free(self);
1759 menu = sipe_backend_buddy_menu_add(sipe_public,
1760 menu,
1761 _("New chat"),
1762 SIPE_BUDDY_MENU_NEW_CHAT,
1763 NULL);
1765 /* add buddy's phone numbers if we have call control */
1766 if (sip_csta_is_idle(sipe_private)) {
1768 /* work phone */
1769 menu = buddy_menu_phone(sipe_public,
1770 menu,
1771 buddy,
1772 SIPE_BUDDY_INFO_WORK_PHONE,
1773 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY,
1774 _("Work"));
1775 /* mobile phone */
1776 menu = buddy_menu_phone(sipe_public,
1777 menu,
1778 buddy,
1779 SIPE_BUDDY_INFO_MOBILE_PHONE,
1780 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY,
1781 _("Mobile"));
1783 /* home phone */
1784 menu = buddy_menu_phone(sipe_public,
1785 menu,
1786 buddy,
1787 SIPE_BUDDY_INFO_HOME_PHONE,
1788 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY,
1789 _("Home"));
1791 /* other phone */
1792 menu = buddy_menu_phone(sipe_public,
1793 menu,
1794 buddy,
1795 SIPE_BUDDY_INFO_OTHER_PHONE,
1796 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY,
1797 _("Other"));
1799 /* custom1 phone */
1800 menu = buddy_menu_phone(sipe_public,
1801 menu,
1802 buddy,
1803 SIPE_BUDDY_INFO_CUSTOM1_PHONE,
1804 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY,
1805 _("Custom1"));
1809 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1810 buddy,
1811 SIPE_BUDDY_INFO_EMAIL);
1812 if (email) {
1813 menu = sipe_backend_buddy_menu_add(sipe_public,
1814 menu,
1815 _("Send email..."),
1816 SIPE_BUDDY_MENU_SEND_EMAIL,
1817 NULL);
1818 g_free(email);
1822 /* access level control */
1823 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1824 menu = sipe_backend_buddy_sub_menu_add(sipe_public,
1825 menu,
1826 _("Access level"),
1827 sipe_ocs2007_access_control_menu(sipe_private,
1828 buddy_name));
1830 return(menu);
1833 guint sipe_buddy_count(struct sipe_core_private *sipe_private)
1835 return(g_hash_table_size(sipe_private->buddies->uri));
1838 static guint sipe_ht_hash_nick(const char *nick)
1840 char *lc = g_utf8_strdown(nick, -1);
1841 guint bucket = g_str_hash(lc);
1842 g_free(lc);
1844 return bucket;
1847 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
1849 char *nick1_norm = NULL;
1850 char *nick2_norm = NULL;
1851 gboolean equal;
1853 if (nick1 == NULL && nick2 == NULL) return TRUE;
1854 if (nick1 == NULL || nick2 == NULL ||
1855 !g_utf8_validate(nick1, -1, NULL) ||
1856 !g_utf8_validate(nick2, -1, NULL)) return FALSE;
1858 nick1_norm = g_utf8_casefold(nick1, -1);
1859 nick2_norm = g_utf8_casefold(nick2, -1);
1860 equal = g_utf8_collate(nick1_norm, nick2_norm) == 0;
1861 g_free(nick2_norm);
1862 g_free(nick1_norm);
1864 return equal;
1867 void sipe_buddy_init(struct sipe_core_private *sipe_private)
1869 struct sipe_buddies *buddies = g_new0(struct sipe_buddies, 1);
1870 buddies->uri = g_hash_table_new((GHashFunc) sipe_ht_hash_nick,
1871 (GEqualFunc) sipe_ht_equals_nick);
1872 buddies->exchange_key = g_hash_table_new(g_str_hash,
1873 g_str_equal);
1874 sipe_private->buddies = buddies;
1878 Local Variables:
1879 mode: c
1880 c-file-style: "bsd"
1881 indent-tabs-mode: t
1882 tab-width: 8
1883 End: