buddy: add interface to iterate over buddies
[siplcs.git] / src / core / sipe-buddy.c
blob49477801d0fd0296d57b3b93af2a728fee077792
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 photo_response_data {
62 gchar *who;
63 gchar *photo_hash;
64 struct sipe_http_request *request;
67 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
68 const gchar *uri);
69 static void photo_response_data_free(struct photo_response_data *data);
71 struct sipe_buddy *sipe_buddy_add(struct sipe_core_private *sipe_private,
72 const gchar *uri,
73 const gchar *exchange_key)
75 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private, uri);
76 if (!buddy) {
77 buddy = g_new0(struct sipe_buddy, 1);
78 buddy->name = g_strdup(uri);
79 buddy->exchange_key = g_strdup(exchange_key);
80 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
82 SIPE_DEBUG_INFO("sipe_buddy_add: Added buddy %s", uri);
84 buddy_fetch_photo(sipe_private, uri);
85 } else {
86 SIPE_DEBUG_INFO("sipe_buddy_add: Buddy %s already exists", uri);
89 return buddy;
92 struct sipe_buddy *sipe_buddy_find_by_uri(struct sipe_core_private *sipe_private,
93 const gchar *uri)
95 return(g_hash_table_lookup(sipe_private->buddies, uri));
98 void sipe_buddy_foreach(struct sipe_core_private *sipe_private,
99 GHFunc callback,
100 gpointer callback_data)
102 g_hash_table_foreach(sipe_private->buddies, callback, callback_data);
105 static void buddy_free(struct sipe_buddy *buddy)
107 #ifndef _WIN32
109 * We are calling g_hash_table_foreach_steal(). That means that no
110 * key/value deallocation functions are called. Therefore the glib
111 * hash code does not touch the key (buddy->name) or value (buddy)
112 * of the to-be-deleted hash node at all. It follows that we
114 * - MUST free the memory for the key ourselves and
115 * - ARE allowed to do it in this function
117 * Conclusion: glib must be broken on the Windows platform if sipe
118 * crashes with SIGTRAP when closing. You'll have to live
119 * with the memory leak until this is fixed.
121 g_free(buddy->name);
122 #endif
123 g_free(buddy->exchange_key);
124 g_free(buddy->activity);
125 g_free(buddy->meeting_subject);
126 g_free(buddy->meeting_location);
127 g_free(buddy->note);
129 g_free(buddy->cal_start_time);
130 g_free(buddy->cal_free_busy_base64);
131 g_free(buddy->cal_free_busy);
132 g_free(buddy->last_non_cal_activity);
134 sipe_cal_free_working_hours(buddy->cal_working_hours);
136 g_free(buddy->device_name);
137 g_slist_free(buddy->groups);
138 g_free(buddy);
141 static gboolean buddy_free_cb(SIPE_UNUSED_PARAMETER gpointer key,
142 gpointer buddy,
143 SIPE_UNUSED_PARAMETER gpointer user_data)
145 buddy_free(buddy);
146 /* We must return TRUE as the key/value have already been deleted */
147 return(TRUE);
150 void sipe_buddy_free(struct sipe_core_private *sipe_private)
152 g_hash_table_foreach_steal(sipe_private->buddies,
153 buddy_free_cb,
154 NULL);
156 /* core is being deallocated, remove all its pending photo requests */
157 while (sipe_private->pending_photo_requests) {
158 struct photo_response_data *data =
159 sipe_private->pending_photo_requests->data;
160 sipe_private->pending_photo_requests =
161 g_slist_remove(sipe_private->pending_photo_requests, data);
162 photo_response_data_free(data);
165 g_hash_table_destroy(sipe_private->buddies);
166 sipe_private->buddies = NULL;
169 gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public,
170 const gchar *uri,
171 guint activity,
172 const gchar *status_text)
174 struct sipe_buddy *sbuddy;
175 const char *activity_str;
177 if (!sipe_public) return NULL; /* happens on pidgin exit */
179 sbuddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, uri);
180 if (!sbuddy) return NULL;
182 activity_str = sbuddy->activity ? sbuddy->activity :
183 (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
184 status_text : NULL;
186 if (activity_str && sbuddy->note) {
187 return g_strdup_printf("%s - <i>%s</i>", activity_str, sbuddy->note);
188 } else if (activity_str) {
189 return g_strdup(activity_str);
190 } else if (sbuddy->note) {
191 return g_strdup_printf("<i>%s</i>", sbuddy->note);
192 } else {
193 return NULL;
197 gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private,
198 const gchar *with)
200 sipe_backend_buddy pbuddy;
201 gchar *alias = NULL;
202 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
203 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
205 return alias;
208 void sipe_core_buddy_group(struct sipe_core_public *sipe_public,
209 const gchar *who,
210 const gchar *old_group_name,
211 const gchar *new_group_name)
213 struct sipe_buddy * buddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, who);
214 struct sipe_group * old_group = NULL;
215 struct sipe_group * new_group;
217 SIPE_DEBUG_INFO("sipe_core_buddy_group: who:%s old_group_name:%s new_group_name:%s",
218 who ? who : "", old_group_name ? old_group_name : "", new_group_name ? new_group_name : "");
220 if(!buddy) { // buddy not in roaming list
221 return;
224 if (old_group_name) {
225 old_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, old_group_name);
227 new_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, new_group_name);
229 if (old_group) {
230 buddy->groups = g_slist_remove(buddy->groups, old_group);
231 SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy %s removed from old group %s", who, old_group_name);
234 if (!new_group) {
235 sipe_group_create(SIPE_CORE_PRIVATE, new_group_name, who);
236 } else {
237 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
238 new_group,
239 (GCompareFunc)sipe_group_compare,
240 NULL);
241 sipe_group_update_buddy(SIPE_CORE_PRIVATE, buddy);
245 void sipe_core_buddy_add(struct sipe_core_public *sipe_public,
246 const gchar *uri,
247 const gchar *group_name)
249 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
251 if (!sipe_buddy_find_by_uri(sipe_private, uri)) {
252 struct sipe_buddy *b = sipe_buddy_add(sipe_private, uri, NULL);
253 b->just_added = TRUE;
255 sipe_subscribe_presence_single_cb(sipe_private, b->name);
257 } else {
258 SIPE_DEBUG_INFO("sipe_core_buddy_add: buddy %s already in internal list",
259 uri);
262 sipe_core_buddy_group(sipe_public,
263 uri,
264 NULL,
265 group_name);
268 void sipe_buddy_remove(struct sipe_core_private *sipe_private,
269 struct sipe_buddy *buddy)
271 gchar *action_name = sipe_utils_presence_key(buddy->name);
272 sipe_schedule_cancel(sipe_private, action_name);
273 g_free(action_name);
275 g_hash_table_remove(sipe_private->buddies, buddy->name);
277 buddy_free(buddy);
281 * Unassociates buddy from group first.
282 * Then see if no groups left, removes buddy completely.
283 * Otherwise updates buddy groups on server.
285 void sipe_core_buddy_remove(struct sipe_core_public *sipe_public,
286 const gchar *uri,
287 const gchar *group_name)
289 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
290 struct sipe_buddy *b = sipe_buddy_find_by_uri(sipe_private,
291 uri);
293 if (!b) return;
295 if (group_name) {
296 struct sipe_group *g = sipe_group_find_by_name(sipe_private,
297 group_name);
298 if (g) {
299 b->groups = g_slist_remove(b->groups, g);
300 SIPE_DEBUG_INFO("sipe_core_buddy_remove: buddy %s removed from group %s",
301 uri, g->name);
305 if (g_slist_length(b->groups) < 1) {
306 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
307 b->name);
308 sip_soap_request(sipe_private,
309 "deleteContact",
310 request);
311 g_free(request);
312 sipe_buddy_remove(sipe_private, b);
313 } else {
314 /* updates groups on server */
315 sipe_group_update_buddy(sipe_private, b);
320 void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
321 const gchar *uri,
322 guint activity)
324 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
325 struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private,
326 uri);
328 if (!sbuddy) return;
330 /* Check if on 2005 system contact's calendar,
331 * then set/preserve it.
333 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
334 sipe_backend_buddy_set_status(sipe_public, uri, activity);
335 } else {
336 sipe_ocs2005_apply_calendar_status(sipe_private,
337 sbuddy,
338 sipe_status_activity_to_token(activity));
342 void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public,
343 const gchar *uri,
344 const gchar *status_name,
345 gboolean is_online,
346 struct sipe_backend_buddy_tooltip *tooltip)
348 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
349 gchar *note = NULL;
350 gboolean is_oof_note = FALSE;
351 const gchar *activity = NULL;
352 gchar *calendar = NULL;
353 const gchar *meeting_subject = NULL;
354 const gchar *meeting_location = NULL;
355 gchar *access_text = NULL;
357 #define SIPE_ADD_BUDDY_INFO(l, t) \
359 gchar *tmp = g_markup_escape_text((t), -1); \
360 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), tmp); \
361 g_free(tmp); \
363 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) \
364 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), (t))
366 if (sipe_public) { /* happens on pidgin exit */
367 struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private,
368 uri);
369 if (sbuddy) {
370 note = sbuddy->note;
371 is_oof_note = sbuddy->is_oof_note;
372 activity = sbuddy->activity;
373 calendar = sipe_cal_get_description(sbuddy);
374 meeting_subject = sbuddy->meeting_subject;
375 meeting_location = sbuddy->meeting_location;
377 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
378 gboolean is_group_access = FALSE;
379 const int container_id = sipe_ocs2007_find_access_level(sipe_private,
380 "user",
381 sipe_get_no_sip_uri(uri),
382 &is_group_access);
383 const char *access_level = sipe_ocs2007_access_level_name(container_id);
384 access_text = is_group_access ?
385 g_strdup(access_level) :
386 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT,
387 access_level);
391 if (is_online) {
392 const gchar *status_str = activity ? activity : status_name;
394 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
396 if (is_online && !is_empty(calendar)) {
397 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
399 g_free(calendar);
400 if (!is_empty(meeting_location)) {
401 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", uri, meeting_location);
402 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
404 if (!is_empty(meeting_subject)) {
405 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", uri, meeting_subject);
406 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
408 if (note) {
409 gchar *note_italics = g_strdup_printf("<i>%s</i>", note);
410 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", uri, note);
411 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
412 note_italics);
413 g_free(note_italics);
415 if (access_text) {
416 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
417 g_free(access_text);
421 void sipe_buddy_update_property(struct sipe_core_private *sipe_private,
422 const char *uri,
423 sipe_buddy_info_fields propkey,
424 char *property_value)
426 GSList *buddies, *entry;
428 if (property_value)
429 property_value = g_strstrip(property_value);
431 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
432 while (entry) {
433 gchar *prop_str;
434 sipe_backend_buddy p_buddy = entry->data;
436 /* for Display Name */
437 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
438 gchar *alias;
439 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
440 if (property_value && sipe_is_bad_alias(uri, alias)) {
441 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
442 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
444 g_free(alias);
446 alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
447 if (!is_empty(property_value) &&
448 (!sipe_strequal(property_value, alias) || is_empty(alias)) )
450 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
451 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
453 g_free(alias);
455 /* for other properties */
456 else {
457 if (!is_empty(property_value)) {
458 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
459 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
460 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
462 g_free(prop_str);
466 entry = entry->next;
468 g_slist_free(buddies);
472 struct ms_dlx_data;
473 struct ms_dlx_data {
474 GSList *search_rows;
475 gchar *other;
476 guint max_returns;
477 sipe_svc_callback *callback;
478 struct sipe_svc_session *session;
479 gchar *wsse_security;
480 struct sipe_backend_search_token *token;
481 /* must call ms_dlx_free() */
482 void (*failed_callback)(struct sipe_core_private *sipe_private,
483 struct ms_dlx_data *mdd);
486 static void ms_dlx_free(struct ms_dlx_data *mdd)
488 sipe_utils_slist_free_full(mdd->search_rows, g_free);
489 sipe_svc_session_close(mdd->session);
490 g_free(mdd->other);
491 g_free(mdd->wsse_security);
492 g_free(mdd);
495 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
496 #define DLX_SEARCH_ITEM \
497 "<AbEntryRequest.ChangeSearchQuery>" \
498 " <SearchOn>%s</SearchOn>" \
499 " <Value>%s</Value>" \
500 "</AbEntryRequest.ChangeSearchQuery>"
502 static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) {
503 gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1);
504 guint i = 0;
505 gchar *query = NULL;
507 while (query_rows) {
508 gchar *attr;
509 gchar *value;
511 attr = query_rows->data;
512 query_rows = g_slist_next(query_rows);
513 value = query_rows->data;
514 query_rows = g_slist_next(query_rows);
516 if (!attr || !value)
517 break;
519 attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW,
520 attr, value);
522 attrs[i] = NULL;
524 if (i) {
525 query = g_strjoinv(NULL, attrs);
526 SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s",
527 query ? query : "");
530 g_strfreev(attrs);
532 return query;
535 static void ms_dlx_webticket(struct sipe_core_private *sipe_private,
536 const gchar *base_uri,
537 const gchar *auth_uri,
538 const gchar *wsse_security,
539 SIPE_UNUSED_PARAMETER const gchar *failure_msg,
540 gpointer callback_data)
542 struct ms_dlx_data *mdd = callback_data;
544 if (wsse_security) {
545 gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
547 SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
548 base_uri);
550 if (sipe_svc_ab_entry_request(sipe_private,
551 mdd->session,
552 auth_uri,
553 wsse_security,
554 query,
555 g_slist_length(mdd->search_rows) / 2,
556 mdd->max_returns,
557 mdd->callback,
558 mdd)) {
560 /* keep webticket security token for potential further use */
561 mdd->wsse_security = g_strdup(wsse_security);
563 /* callback data passed down the line */
564 mdd = NULL;
566 g_free(query);
568 } else {
569 /* no ticket: this will show the minmum information */
570 SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
571 base_uri);
574 if (mdd)
575 mdd->failed_callback(sipe_private, mdd);
578 static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private,
579 struct ms_dlx_data *mdd)
581 if (!sipe_webticket_request(sipe_private,
582 mdd->session,
583 sipe_private->dlx_uri,
584 "AddressBookWebTicketBearer",
585 ms_dlx_webticket,
586 mdd)) {
587 SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
588 sipe_private->dlx_uri);
589 mdd->failed_callback(sipe_private, mdd);
593 static void search_contacts_finalize(struct sipe_core_private *sipe_private,
594 struct sipe_backend_search_results *results,
595 guint match_count,
596 gboolean more)
598 gchar *secondary = g_strdup_printf(
599 dngettext(PACKAGE_NAME,
600 "Found %d contact%s:",
601 "Found %d contacts%s:", match_count),
602 match_count, more ? _(" (more matched your query)") : "");
604 sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
605 results,
606 secondary,
607 more);
608 g_free(secondary);
611 static void search_ab_entry_response(struct sipe_core_private *sipe_private,
612 const gchar *uri,
613 SIPE_UNUSED_PARAMETER const gchar *raw,
614 sipe_xml *soap_body,
615 gpointer callback_data)
617 struct ms_dlx_data *mdd = callback_data;
619 if (soap_body) {
620 const sipe_xml *node;
621 struct sipe_backend_search_results *results;
622 GHashTable *found;
624 SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
625 uri);
627 /* any matches? */
628 node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
629 if (!node) {
630 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
631 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
632 mdd->token,
633 _("No contacts found"));
634 ms_dlx_free(mdd);
635 return;
638 /* OK, we found something - show the results to the user */
639 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
640 mdd->token);
641 if (!results) {
642 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
643 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
644 mdd->token,
645 _("Unable to display the search results"));
646 ms_dlx_free(mdd);
647 return;
650 /* SearchAbEntryResult can contain duplicates */
651 found = g_hash_table_new_full(g_str_hash, g_str_equal,
652 g_free, NULL);
654 for (/* initialized above */ ; node; node = sipe_xml_twin(node)) {
655 const sipe_xml *attrs;
656 gchar *sip_uri = NULL;
657 gchar *displayname = NULL;
658 gchar *company = NULL;
659 gchar *country = NULL;
660 gchar *email = NULL;
662 for (attrs = sipe_xml_child(node, "Attributes/Attribute");
663 attrs;
664 attrs = sipe_xml_twin(attrs)) {
665 gchar *name = sipe_xml_data(sipe_xml_child(attrs,
666 "Name"));
667 gchar *value = sipe_xml_data(sipe_xml_child(attrs,
668 "Value"));
670 if (!is_empty(value)) {
671 if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) {
672 g_free(sip_uri);
673 sip_uri = value;
674 value = NULL;
675 } else if (sipe_strcase_equal(name, "displayname")) {
676 g_free(displayname);
677 displayname = value;
678 value = NULL;
679 } else if (sipe_strcase_equal(name, "mail")) {
680 g_free(email);
681 email = value;
682 value = NULL;
683 } else if (sipe_strcase_equal(name, "company")) {
684 g_free(company);
685 company = value;
686 value = NULL;
687 } else if (sipe_strcase_equal(name, "country")) {
688 g_free(country);
689 country = value;
690 value = NULL;
694 g_free(value);
695 g_free(name);
698 if (sip_uri && !g_hash_table_lookup(found, sip_uri)) {
699 gchar **uri_parts = g_strsplit(sip_uri, ":", 2);
700 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
701 results,
702 uri_parts[1],
703 displayname,
704 company,
705 country,
706 email);
707 g_strfreev(uri_parts);
709 g_hash_table_insert(found, sip_uri, (gpointer) TRUE);
710 sip_uri = NULL;
713 g_free(email);
714 g_free(country);
715 g_free(company);
716 g_free(displayname);
717 g_free(sip_uri);
720 search_contacts_finalize(sipe_private, results,
721 g_hash_table_size(found),
722 FALSE);
723 g_hash_table_destroy(found);
724 ms_dlx_free(mdd);
726 } else {
727 mdd->failed_callback(sipe_private, mdd);
731 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
732 struct sipmsg *msg,
733 struct transaction *trans)
735 struct sipe_backend_search_token *token = trans->payload->data;
736 struct sipe_backend_search_results *results;
737 sipe_xml *searchResults;
738 const sipe_xml *mrow;
739 guint match_count = 0;
740 gboolean more = FALSE;
742 /* valid response? */
743 if (msg->response != 200) {
744 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
745 msg->response);
746 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
747 token,
748 _("Contact search failed"));
749 return(FALSE);
752 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
754 /* valid XML? */
755 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
756 if (!searchResults) {
757 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
758 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
759 token,
760 _("Contact search failed"));
761 return(FALSE);
764 /* any matches? */
765 mrow = sipe_xml_child(searchResults, "Body/Array/row");
766 if (!mrow) {
767 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
768 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
769 token,
770 _("No contacts found"));
772 sipe_xml_free(searchResults);
773 return(FALSE);
776 /* OK, we found something - show the results to the user */
777 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
778 trans->payload->data);
779 if (!results) {
780 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
781 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
782 token,
783 _("Unable to display the search results"));
785 sipe_xml_free(searchResults);
786 return FALSE;
789 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
790 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
791 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
792 results,
793 uri_parts[1],
794 sipe_xml_attribute(mrow, "displayName"),
795 sipe_xml_attribute(mrow, "company"),
796 sipe_xml_attribute(mrow, "country"),
797 sipe_xml_attribute(mrow, "email"));
798 g_strfreev(uri_parts);
799 match_count++;
802 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
803 char *data = sipe_xml_data(mrow);
804 more = (g_ascii_strcasecmp(data, "true") == 0);
805 g_free(data);
808 search_contacts_finalize(sipe_private, results, match_count, more);
809 sipe_xml_free(searchResults);
811 return(TRUE);
814 static void search_soap_request(struct sipe_core_private *sipe_private,
815 struct sipe_backend_search_token *token,
816 GSList *search_rows)
818 gchar *query = prepare_buddy_search_query(search_rows, FALSE);
819 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
821 payload->data = token;
823 sip_soap_directory_search(sipe_private,
824 100,
825 query,
826 process_search_contact_response,
827 payload);
828 g_free(query);
831 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
832 struct ms_dlx_data *mdd)
834 /* error using [MS-DLX] server, retry using Active Directory */
835 search_soap_request(sipe_private, mdd->token, mdd->search_rows);
836 ms_dlx_free(mdd);
839 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
840 struct sipe_backend_search_token *token,
841 const gchar *given_name,
842 const gchar *surname,
843 const gchar *email,
844 const gchar *company,
845 const gchar *country)
847 GSList *query_rows = NULL;
849 #define ADD_QUERY_ROW(attr, val) \
850 if (val) { \
851 query_rows = g_slist_append(query_rows, g_strdup(attr)); \
852 query_rows = g_slist_append(query_rows, g_strdup(val)); \
855 ADD_QUERY_ROW("givenName", given_name);
856 ADD_QUERY_ROW("sn", surname);
857 ADD_QUERY_ROW("mail", email);
858 ADD_QUERY_ROW("company", company);
859 ADD_QUERY_ROW("c", country);
861 if (query_rows) {
862 if (SIPE_CORE_PRIVATE->dlx_uri != NULL) {
863 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
865 mdd->search_rows = query_rows;
866 mdd->max_returns = 100;
867 mdd->callback = search_ab_entry_response;
868 mdd->failed_callback = search_ab_entry_failed;
869 mdd->session = sipe_svc_session_start();
870 mdd->token = token;
872 ms_dlx_webticket_request(SIPE_CORE_PRIVATE, mdd);
874 } else {
875 /* no [MS-DLX] server, use Active Directory search instead */
876 search_soap_request(SIPE_CORE_PRIVATE, token, query_rows);
877 sipe_utils_slist_free_full(query_rows, g_free);
879 } else
880 sipe_backend_search_failed(sipe_public,
881 token,
882 _("Invalid contact search query"));
885 static void get_info_finalize(struct sipe_core_private *sipe_private,
886 struct sipe_backend_buddy_info *info,
887 const gchar *uri,
888 const gchar *server_alias,
889 const gchar *email)
891 sipe_backend_buddy bbuddy;
892 struct sipe_buddy *sbuddy;
893 gchar *alias;
894 gchar *value;
896 if (!info) {
897 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
898 } else {
899 sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info);
901 if (!info)
902 return;
904 bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
906 if (is_empty(server_alias)) {
907 value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
908 bbuddy);
909 if (value) {
910 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
911 info,
912 SIPE_BUDDY_INFO_DISPLAY_NAME,
913 value);
915 } else {
916 value = g_strdup(server_alias);
919 /* present alias if it differs from server alias */
920 alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy);
921 if (alias && !sipe_strequal(alias, value))
923 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
924 info,
925 SIPE_BUDDY_INFO_ALIAS,
926 alias);
928 g_free(alias);
929 g_free(value);
931 if (is_empty(email)) {
932 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
933 bbuddy,
934 SIPE_BUDDY_INFO_EMAIL);
935 if (value) {
936 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
937 info,
938 SIPE_BUDDY_INFO_EMAIL,
939 value);
940 g_free(value);
944 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
945 bbuddy,
946 SIPE_BUDDY_INFO_SITE);
947 if (value) {
948 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
949 info,
950 SIPE_BUDDY_INFO_SITE,
951 value);
952 g_free(value);
955 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
956 if (sbuddy && sbuddy->device_name) {
957 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
958 info,
959 SIPE_BUDDY_INFO_DEVICE,
960 sbuddy->device_name);
963 sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri);
967 static void get_info_ab_entry_response(struct sipe_core_private *sipe_private,
968 const gchar *uri,
969 SIPE_UNUSED_PARAMETER const gchar *raw,
970 sipe_xml *soap_body,
971 gpointer callback_data)
973 struct ms_dlx_data *mdd = callback_data;
974 struct sipe_backend_buddy_info *info = NULL;
975 gchar *server_alias = NULL;
976 gchar *email = NULL;
978 if (soap_body) {
979 const sipe_xml *node;
981 SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s",
982 uri);
984 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
986 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
987 node;
988 node = sipe_xml_twin(node)) {
989 gchar *name = sipe_xml_data(sipe_xml_child(node,
990 "Name"));
991 gchar *value = sipe_xml_data(sipe_xml_child(node,
992 "Value"));
993 const sipe_xml *values = sipe_xml_child(node,
994 "Values");
996 /* Single value entries */
997 if (!is_empty(value)) {
999 if (sipe_strcase_equal(name, "displayname")) {
1000 g_free(server_alias);
1001 server_alias = value;
1002 value = NULL;
1003 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1004 info,
1005 SIPE_BUDDY_INFO_DISPLAY_NAME,
1006 server_alias);
1007 } else if (sipe_strcase_equal(name, "mail")) {
1008 g_free(email);
1009 email = value;
1010 value = NULL;
1011 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1012 info,
1013 SIPE_BUDDY_INFO_EMAIL,
1014 email);
1015 } else if (sipe_strcase_equal(name, "title")) {
1016 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1017 info,
1018 SIPE_BUDDY_INFO_JOB_TITLE,
1019 value);
1020 } else if (sipe_strcase_equal(name, "company")) {
1021 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1022 info,
1023 SIPE_BUDDY_INFO_COMPANY,
1024 value);
1025 } else if (sipe_strcase_equal(name, "country")) {
1026 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1027 info,
1028 SIPE_BUDDY_INFO_COUNTRY,
1029 value);
1032 } else if (values) {
1033 gchar *first = sipe_xml_data(sipe_xml_child(values,
1034 "string"));
1036 if (sipe_strcase_equal(name, "telephonenumber")) {
1037 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1038 info,
1039 SIPE_BUDDY_INFO_WORK_PHONE,
1040 first);
1043 g_free(first);
1046 g_free(value);
1047 g_free(name);
1051 /* this will show the minmum information */
1052 get_info_finalize(sipe_private,
1053 info,
1054 mdd->other,
1055 server_alias,
1056 email);
1058 g_free(email);
1059 g_free(server_alias);
1060 ms_dlx_free(mdd);
1063 static gboolean process_get_info_response(struct sipe_core_private *sipe_private,
1064 struct sipmsg *msg,
1065 struct transaction *trans)
1067 const gchar *uri = trans->payload->data;
1068 struct sipe_backend_buddy_info *info = NULL;
1069 gchar *server_alias = NULL;
1070 gchar *email = NULL;
1072 SIPE_DEBUG_INFO("Fetching %s's user info for %s",
1073 uri, sipe_private->username);
1075 if (msg->response != 200) {
1076 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
1077 } else {
1078 sipe_xml *searchResults;
1079 const sipe_xml *mrow;
1081 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s",
1082 msg->body ? msg->body : "");
1084 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1085 if (!searchResults) {
1087 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
1089 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
1090 const gchar *value;
1091 gchar *phone_number;
1093 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1095 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
1096 email = g_strdup(sipe_xml_attribute(mrow, "email"));
1097 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
1100 * For 2007 system we will take this from ContactCard -
1101 * it has cleaner tel: URIs at least
1103 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1104 char *tel_uri = sip_to_tel_uri(phone_number);
1105 /* trims its parameters, so call first */
1106 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1107 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1108 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1109 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1110 g_free(tel_uri);
1112 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC,
1113 uri);
1116 if (!is_empty(server_alias)) {
1117 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1118 info,
1119 SIPE_BUDDY_INFO_DISPLAY_NAME,
1120 server_alias);
1122 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1123 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1124 info,
1125 SIPE_BUDDY_INFO_JOB_TITLE,
1126 value);
1128 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1129 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1130 info,
1131 SIPE_BUDDY_INFO_OFFICE,
1132 value);
1134 if (!is_empty(phone_number)) {
1135 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1136 info,
1137 SIPE_BUDDY_INFO_WORK_PHONE,
1138 phone_number);
1140 g_free(phone_number);
1141 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1142 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1143 info,
1144 SIPE_BUDDY_INFO_COMPANY,
1145 value);
1147 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1148 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1149 info,
1150 SIPE_BUDDY_INFO_CITY,
1151 value);
1153 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1154 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1155 info,
1156 SIPE_BUDDY_INFO_STATE,
1157 value);
1159 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1160 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1161 info,
1162 SIPE_BUDDY_INFO_COUNTRY,
1163 value);
1165 if (!is_empty(email)) {
1166 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1167 info,
1168 SIPE_BUDDY_INFO_EMAIL,
1169 email);
1172 sipe_xml_free(searchResults);
1175 /* this will show the minmum information */
1176 get_info_finalize(sipe_private,
1177 info,
1178 uri,
1179 server_alias,
1180 email);
1182 g_free(server_alias);
1183 g_free(email);
1185 return TRUE;
1188 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
1189 struct ms_dlx_data *mdd)
1191 /* error using [MS-DLX] server, retry using Active Directory */
1192 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
1193 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1195 payload->destroy = g_free;
1196 payload->data = mdd->other;
1197 mdd->other = NULL;
1199 sip_soap_directory_search(sipe_private,
1201 query,
1202 process_get_info_response,
1203 payload);
1205 ms_dlx_free(mdd);
1206 g_free(query);
1209 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
1210 const gchar *who)
1212 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1214 if (sipe_private->dlx_uri) {
1215 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1217 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1218 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(who));
1220 mdd->other = g_strdup(who);
1221 mdd->max_returns = 1;
1222 mdd->callback = get_info_ab_entry_response;
1223 mdd->failed_callback = get_info_ab_entry_failed;
1224 mdd->session = sipe_svc_session_start();
1226 ms_dlx_webticket_request(sipe_private, mdd);
1228 } else {
1229 /* no [MS-DLX] server, use Active Directory search instead */
1230 gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
1231 "msRTCSIP-PrimaryUserAddress",
1232 who);
1233 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1235 SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
1236 row ? row : "");
1238 payload->destroy = g_free;
1239 payload->data = g_strdup(who);
1241 sip_soap_directory_search(sipe_private,
1243 row,
1244 process_get_info_response,
1245 payload);
1246 g_free(row);
1250 static void photo_response_data_free(struct photo_response_data *data)
1252 g_free(data->who);
1253 g_free(data->photo_hash);
1254 if (data->request) {
1255 sipe_http_request_cancel(data->request);
1257 g_free(data);
1260 static void process_buddy_photo_response(struct sipe_core_private *sipe_private,
1261 guint status,
1262 GSList *headers,
1263 const char *body,
1264 gpointer data)
1266 struct photo_response_data *rdata = (struct photo_response_data *) data;
1268 rdata->request = NULL;
1270 if (status == SIPE_HTTP_STATUS_OK) {
1271 const gchar *len_str = sipe_utils_nameval_find(headers,
1272 "Content-Length");
1273 if (len_str) {
1274 gsize photo_size = atoi(len_str);
1275 gpointer photo = g_new(char, photo_size);
1277 if (photo) {
1278 memcpy(photo, body, photo_size);
1280 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC,
1281 rdata->who,
1282 photo,
1283 photo_size,
1284 rdata->photo_hash);
1289 sipe_private->pending_photo_requests =
1290 g_slist_remove(sipe_private->pending_photo_requests, rdata);
1292 photo_response_data_free(rdata);
1295 static gchar *create_x_ms_webticket_header(const gchar *wsse_security)
1297 gchar *assertion = sipe_xml_extract_raw(wsse_security, "saml:Assertion", TRUE);
1298 gchar *wsse_security_base64;
1299 gchar *x_ms_webticket_header;
1301 if (!assertion) {
1302 return NULL;
1305 wsse_security_base64 = g_base64_encode((const guchar *)assertion,
1306 strlen(assertion));
1307 x_ms_webticket_header = g_strdup_printf("X-MS-WebTicket: opaque=%s\r\n",
1308 wsse_security_base64);
1310 g_free(assertion);
1311 g_free(wsse_security_base64);
1313 return x_ms_webticket_header;
1316 static void get_photo_ab_entry_response(struct sipe_core_private *sipe_private,
1317 const gchar *uri,
1318 SIPE_UNUSED_PARAMETER const gchar *raw,
1319 sipe_xml *soap_body,
1320 gpointer callback_data)
1322 struct ms_dlx_data *mdd = callback_data;
1323 gchar *photo_rel_path = NULL;
1324 gchar *photo_hash = NULL;
1325 const gchar *photo_hash_old =
1326 sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, mdd->other);
1328 if (soap_body) {
1329 const sipe_xml *node;
1331 SIPE_DEBUG_INFO("get_photo_ab_entry_response: received valid SOAP message from service %s",
1332 uri);
1334 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
1335 node;
1336 node = sipe_xml_twin(node)) {
1337 gchar *name = sipe_xml_data(sipe_xml_child(node, "Name"));
1338 gchar *value = sipe_xml_data(sipe_xml_child(node, "Value"));
1340 if (!is_empty(value)) {
1341 if (sipe_strcase_equal(name, "PhotoRelPath")) {
1342 g_free(photo_rel_path);
1343 photo_rel_path = value;
1344 value = NULL;
1345 } else if (sipe_strcase_equal(name, "PhotoHash")) {
1346 g_free(photo_hash);
1347 photo_hash = value;
1348 value = NULL;
1352 g_free(value);
1353 g_free(name);
1357 if (sipe_private->addressbook_uri && photo_rel_path &&
1358 photo_hash && !sipe_strequal(photo_hash, photo_hash_old)) {
1359 gchar *photo_url = g_strdup_printf("%s/%s",
1360 sipe_private->addressbook_uri, photo_rel_path);
1361 gchar *x_ms_webticket_header = create_x_ms_webticket_header(mdd->wsse_security);
1363 struct photo_response_data *data = g_new(struct photo_response_data, 1);
1364 data->who = g_strdup(mdd->other);
1365 data->photo_hash = photo_hash;
1366 photo_hash = NULL;
1368 data->request = sipe_http_request_get(sipe_private,
1369 photo_url,
1370 x_ms_webticket_header,
1371 process_buddy_photo_response,
1372 data);
1374 if (data->request) {
1375 sipe_private->pending_photo_requests =
1376 g_slist_append(sipe_private->pending_photo_requests, data);
1377 sipe_http_request_ready(data->request);
1378 } else {
1379 photo_response_data_free(data);
1382 g_free(x_ms_webticket_header);
1383 g_free(photo_url);
1386 g_free(photo_rel_path);
1387 g_free(photo_hash);
1388 ms_dlx_free(mdd);
1391 static void get_photo_ab_entry_failed(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
1392 struct ms_dlx_data *mdd)
1394 ms_dlx_free(mdd);
1397 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
1398 const gchar *uri)
1400 if (sipe_backend_uses_photo()) {
1402 /* Lync 2013 or newer: use UCS */
1403 if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013)) {
1405 sipe_ucs_get_photo(sipe_private, uri);
1407 /* Lync 2010: use [MS-DLX] */
1408 } else if (sipe_private->dlx_uri &&
1409 sipe_private->addressbook_uri) {
1410 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1412 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1413 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(uri));
1415 mdd->other = g_strdup(uri);
1416 mdd->max_returns = 1;
1417 mdd->callback = get_photo_ab_entry_response;
1418 mdd->failed_callback = get_photo_ab_entry_failed;
1419 mdd->session = sipe_svc_session_start();
1421 ms_dlx_webticket_request(sipe_private, mdd);
1426 static void buddy_refresh_photos_cb(gpointer uri,
1427 SIPE_UNUSED_PARAMETER gpointer value,
1428 gpointer sipe_private)
1430 buddy_fetch_photo(sipe_private, uri);
1433 void sipe_buddy_refresh_photos(struct sipe_core_private *sipe_private)
1435 g_hash_table_foreach(sipe_private->buddies,
1436 buddy_refresh_photos_cb,
1437 sipe_private);
1440 /* Buddy menu callbacks*/
1442 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
1443 const gchar *who)
1445 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1447 /* 2007+ conference */
1448 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1449 sipe_conf_add(sipe_private, who);
1451 /* 2005- multiparty chat */
1452 } else {
1453 gchar *self = sip_uri_self(sipe_private);
1454 struct sip_session *session;
1456 session = sipe_session_add_chat(sipe_private,
1457 NULL,
1458 TRUE,
1459 self);
1460 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1461 session->chat_session,
1462 session->chat_session->title,
1463 self);
1464 g_free(self);
1466 sipe_im_invite(sipe_private, session, who,
1467 NULL, NULL, NULL, FALSE);
1471 void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public,
1472 const gchar *who)
1474 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1475 who,
1476 NULL);
1477 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1478 buddy,
1479 SIPE_BUDDY_INFO_EMAIL);
1481 if (email) {
1482 gchar *command_line = g_strdup_printf(
1483 #ifdef _WIN32
1484 "cmd /c start"
1485 #else
1486 "xdg-email"
1487 #endif
1488 " mailto:%s", email);
1489 g_free(email);
1491 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s",
1492 command_line);
1493 g_spawn_command_line_async(command_line, NULL);
1494 g_free(command_line);
1496 } else {
1497 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s",
1498 who);
1502 /* Buddy menu */
1504 static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public,
1505 struct sipe_backend_buddy_menu *menu,
1506 sipe_backend_buddy buddy,
1507 sipe_buddy_info_fields id_phone,
1508 sipe_buddy_info_fields id_display,
1509 const gchar *type)
1511 gchar *phone = sipe_backend_buddy_get_string(sipe_public,
1512 buddy,
1513 id_phone);
1514 if (phone) {
1515 gchar *display = sipe_backend_buddy_get_string(sipe_public,
1516 buddy,
1517 id_display);
1518 gchar *tmp = NULL;
1519 gchar *label = g_strdup_printf("%s %s",
1520 type,
1521 display ? display :
1522 (tmp = sip_tel_uri_denormalize(phone)));
1523 menu = sipe_backend_buddy_menu_add(sipe_public,
1524 menu,
1525 label,
1526 SIPE_BUDDY_MENU_MAKE_CALL,
1527 phone);
1528 g_free(tmp);
1529 g_free(label);
1530 g_free(display);
1531 g_free(phone);
1534 return(menu);
1537 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
1538 const gchar *buddy_name,
1539 struct sipe_backend_buddy_menu *menu)
1541 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1542 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1543 buddy_name,
1544 NULL);
1545 gchar *self = sip_uri_self(sipe_private);
1547 SIPE_SESSION_FOREACH {
1548 if (!sipe_strcase_equal(self, buddy_name) && session->chat_session)
1550 struct sipe_chat_session *chat_session = session->chat_session;
1551 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1553 if (sipe_backend_chat_find(chat_session->backend, buddy_name))
1555 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1557 if (is_conf &&
1558 /* Not conf OP */
1559 !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) &&
1560 /* We are a conf OP */
1561 conf_op) {
1562 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1563 chat_session->title);
1564 menu = sipe_backend_buddy_menu_add(sipe_public,
1565 menu,
1566 label,
1567 SIPE_BUDDY_MENU_MAKE_CHAT_LEADER,
1568 chat_session);
1569 g_free(label);
1572 if (is_conf &&
1573 /* We are a conf OP */
1574 conf_op) {
1575 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1576 chat_session->title);
1577 menu = sipe_backend_buddy_menu_add(sipe_public,
1578 menu,
1579 label,
1580 SIPE_BUDDY_MENU_REMOVE_FROM_CHAT,
1581 chat_session);
1582 g_free(label);
1585 else
1587 if (!is_conf ||
1588 (is_conf && !session->locked)) {
1589 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1590 chat_session->title);
1591 menu = sipe_backend_buddy_menu_add(sipe_public,
1592 menu,
1593 label,
1594 SIPE_BUDDY_MENU_INVITE_TO_CHAT,
1595 chat_session);
1596 g_free(label);
1600 } SIPE_SESSION_FOREACH_END;
1601 g_free(self);
1603 menu = sipe_backend_buddy_menu_add(sipe_public,
1604 menu,
1605 _("New chat"),
1606 SIPE_BUDDY_MENU_NEW_CHAT,
1607 NULL);
1609 /* add buddy's phone numbers if we have call control */
1610 if (sip_csta_is_idle(sipe_private)) {
1612 /* work phone */
1613 menu = buddy_menu_phone(sipe_public,
1614 menu,
1615 buddy,
1616 SIPE_BUDDY_INFO_WORK_PHONE,
1617 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY,
1618 _("Work"));
1619 /* mobile phone */
1620 menu = buddy_menu_phone(sipe_public,
1621 menu,
1622 buddy,
1623 SIPE_BUDDY_INFO_MOBILE_PHONE,
1624 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY,
1625 _("Mobile"));
1627 /* home phone */
1628 menu = buddy_menu_phone(sipe_public,
1629 menu,
1630 buddy,
1631 SIPE_BUDDY_INFO_HOME_PHONE,
1632 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY,
1633 _("Home"));
1635 /* other phone */
1636 menu = buddy_menu_phone(sipe_public,
1637 menu,
1638 buddy,
1639 SIPE_BUDDY_INFO_OTHER_PHONE,
1640 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY,
1641 _("Other"));
1643 /* custom1 phone */
1644 menu = buddy_menu_phone(sipe_public,
1645 menu,
1646 buddy,
1647 SIPE_BUDDY_INFO_CUSTOM1_PHONE,
1648 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY,
1649 _("Custom1"));
1653 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1654 buddy,
1655 SIPE_BUDDY_INFO_EMAIL);
1656 if (email) {
1657 menu = sipe_backend_buddy_menu_add(sipe_public,
1658 menu,
1659 _("Send email..."),
1660 SIPE_BUDDY_MENU_SEND_EMAIL,
1661 NULL);
1662 g_free(email);
1666 /* access level control */
1667 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1668 menu = sipe_backend_buddy_sub_menu_add(sipe_public,
1669 menu,
1670 _("Access level"),
1671 sipe_ocs2007_access_control_menu(sipe_private,
1672 buddy_name));
1674 return(menu);
1677 guint sipe_buddy_count(struct sipe_core_private *sipe_private)
1679 return(g_hash_table_size(sipe_private->buddies));
1682 static guint sipe_ht_hash_nick(const char *nick)
1684 char *lc = g_utf8_strdown(nick, -1);
1685 guint bucket = g_str_hash(lc);
1686 g_free(lc);
1688 return bucket;
1691 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
1693 char *nick1_norm = NULL;
1694 char *nick2_norm = NULL;
1695 gboolean equal;
1697 if (nick1 == NULL && nick2 == NULL) return TRUE;
1698 if (nick1 == NULL || nick2 == NULL ||
1699 !g_utf8_validate(nick1, -1, NULL) ||
1700 !g_utf8_validate(nick2, -1, NULL)) return FALSE;
1702 nick1_norm = g_utf8_casefold(nick1, -1);
1703 nick2_norm = g_utf8_casefold(nick2, -1);
1704 equal = g_utf8_collate(nick1_norm, nick2_norm) == 0;
1705 g_free(nick2_norm);
1706 g_free(nick1_norm);
1708 return equal;
1711 void sipe_buddy_init(struct sipe_core_private *sipe_private)
1713 sipe_private->buddies = g_hash_table_new((GHashFunc) sipe_ht_hash_nick,
1714 (GEqualFunc) sipe_ht_equals_nick);
1720 Local Variables:
1721 mode: c
1722 c-file-style: "bsd"
1723 indent-tabs-mode: t
1724 tab-width: 8
1725 End: