svc: add HTTP session handling
[siplcs.git] / src / core / sipe-buddy.c
blobc5b4ca329eb702b1be539a07b426a69c84ae0756
1 /**
2 * @file sipe-buddy.c
4 * pidgin-sipe
6 * Copyright (C) 2010-12 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <string.h>
28 #include <time.h>
30 #include <glib.h>
32 #include "sipe-common.h"
33 #include "sipmsg.h"
34 #include "sip-csta.h"
35 #include "sip-soap.h"
36 #include "sip-transport.h"
37 #include "sipe-backend.h"
38 #include "sipe-buddy.h"
39 #include "sipe-cal.h"
40 #include "sipe-chat.h"
41 #include "sipe-conf.h"
42 #include "sipe-core.h"
43 #include "sipe-core-private.h"
44 #include "sipe-group.h"
45 #include "sipe-im.h"
46 #include "sipe-nls.h"
47 #include "sipe-ocs2005.h"
48 #include "sipe-ocs2007.h"
49 #include "sipe-schedule.h"
50 #include "sipe-session.h"
51 #include "sipe-status.h"
52 #include "sipe-subscriptions.h"
53 #include "sipe-svc.h"
54 #include "sipe-utils.h"
55 #include "sipe-webticket.h"
56 #include "sipe-xml.h"
58 static void buddy_free(struct sipe_buddy *buddy)
60 #ifndef _WIN32
62 * We are calling g_hash_table_foreach_steal(). That means that no
63 * key/value deallocation functions are called. Therefore the glib
64 * hash code does not touch the key (buddy->name) or value (buddy)
65 * of the to-be-deleted hash node at all. It follows that we
67 * - MUST free the memory for the key ourselves and
68 * - ARE allowed to do it in this function
70 * Conclusion: glib must be broken on the Windows platform if sipe
71 * crashes with SIGTRAP when closing. You'll have to live
72 * with the memory leak until this is fixed.
74 g_free(buddy->name);
75 #endif
76 g_free(buddy->activity);
77 g_free(buddy->meeting_subject);
78 g_free(buddy->meeting_location);
79 g_free(buddy->note);
81 g_free(buddy->cal_start_time);
82 g_free(buddy->cal_free_busy_base64);
83 g_free(buddy->cal_free_busy);
84 g_free(buddy->last_non_cal_activity);
86 sipe_cal_free_working_hours(buddy->cal_working_hours);
88 g_free(buddy->device_name);
89 g_slist_free(buddy->groups);
90 g_free(buddy);
93 static gboolean buddy_free_cb(SIPE_UNUSED_PARAMETER gpointer key,
94 gpointer buddy,
95 SIPE_UNUSED_PARAMETER gpointer user_data)
97 buddy_free(buddy);
98 /* We must return TRUE as the key/value have already been deleted */
99 return(TRUE);
102 void sipe_buddy_free_all(struct sipe_core_private *sipe_private)
104 g_hash_table_foreach_steal(sipe_private->buddies,
105 buddy_free_cb,
106 NULL);
109 gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public,
110 const gchar *uri,
111 guint activity,
112 const gchar *status_text)
114 struct sipe_buddy *sbuddy;
115 const char *activity_str;
117 if (!sipe_public) return NULL; /* happens on pidgin exit */
119 sbuddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, uri);
120 if (!sbuddy) return NULL;
122 activity_str = sbuddy->activity ? sbuddy->activity :
123 (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
124 status_text : NULL;
126 if (activity_str && sbuddy->note) {
127 return g_strdup_printf("%s - <i>%s</i>", activity_str, sbuddy->note);
128 } else if (activity_str) {
129 return g_strdup(activity_str);
130 } else if (sbuddy->note) {
131 return g_strdup_printf("<i>%s</i>", sbuddy->note);
132 } else {
133 return NULL;
137 gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private,
138 const gchar *with)
140 sipe_backend_buddy pbuddy;
141 gchar *alias = NULL;
142 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
143 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
145 return alias;
148 void sipe_core_buddy_group(struct sipe_core_public *sipe_public,
149 const gchar *who,
150 const gchar *old_group_name,
151 const gchar *new_group_name)
153 struct sipe_buddy * buddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, who);
154 struct sipe_group * old_group = NULL;
155 struct sipe_group * new_group;
157 SIPE_DEBUG_INFO("sipe_core_buddy_group: who:%s old_group_name:%s new_group_name:%s",
158 who ? who : "", old_group_name ? old_group_name : "", new_group_name ? new_group_name : "");
160 if(!buddy) { // buddy not in roaming list
161 return;
164 if (old_group_name) {
165 old_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, old_group_name);
167 new_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, new_group_name);
169 if (old_group) {
170 buddy->groups = g_slist_remove(buddy->groups, old_group);
171 SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy %s removed from old group %s", who, old_group_name);
174 if (!new_group) {
175 sipe_group_create(SIPE_CORE_PRIVATE, new_group_name, who);
176 } else {
177 buddy->groups = slist_insert_unique_sorted(buddy->groups, new_group, (GCompareFunc)sipe_group_compare);
178 sipe_core_group_set_user(sipe_public, who);
182 void sipe_core_buddy_add(struct sipe_core_public *sipe_public,
183 const gchar *uri,
184 const gchar *group_name)
186 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
188 if (!g_hash_table_lookup(sipe_private->buddies, uri)) {
189 struct sipe_buddy *b = g_new0(struct sipe_buddy, 1);
191 SIPE_DEBUG_INFO("sipe_core_buddy_add: %s", uri);
193 b->name = g_strdup(uri);
194 b->just_added = TRUE;
195 g_hash_table_insert(sipe_private->buddies, b->name, b);
197 /* @TODO should go to callback */
198 sipe_subscribe_presence_single(sipe_private, b->name);
200 } else {
201 SIPE_DEBUG_INFO("sipe_core_buddy_add: buddy %s already in internal list",
202 uri);
205 sipe_core_buddy_group(sipe_public,
206 uri,
207 NULL,
208 group_name);
212 * Unassociates buddy from group first.
213 * Then see if no groups left, removes buddy completely.
214 * Otherwise updates buddy groups on server.
216 void sipe_core_buddy_remove(struct sipe_core_public *sipe_public,
217 const gchar *uri,
218 const gchar *group_name)
220 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
221 struct sipe_buddy *b = g_hash_table_lookup(sipe_private->buddies,
222 uri);
224 if (!b) return;
226 if (group_name) {
227 struct sipe_group *g = sipe_group_find_by_name(sipe_private,
228 group_name);
229 if (g) {
230 b->groups = g_slist_remove(b->groups, g);
231 SIPE_DEBUG_INFO("sipe_core_buddy_remove: buddy %s removed from group %s",
232 uri, g->name);
236 if (g_slist_length(b->groups) < 1) {
237 gchar *action_name = sipe_utils_presence_key(uri);
238 sipe_schedule_cancel(sipe_private, action_name);
239 g_free(action_name);
241 g_hash_table_remove(sipe_private->buddies, uri);
243 if (b->name) {
244 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
245 b->name);
246 sip_soap_request(sipe_private,
247 "deleteContact",
248 request);
249 g_free(request);
252 buddy_free(b);
253 } else {
254 /* updates groups on server */
255 sipe_core_group_set_user(sipe_public, b->name);
260 void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
261 const gchar *uri,
262 guint activity)
264 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
265 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies,
266 uri);
268 if (!sbuddy) return;
270 /* Check if on 2005 system contact's calendar,
271 * then set/preserve it.
273 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
274 sipe_backend_buddy_set_status(sipe_public, uri, activity);
275 } else {
276 sipe_ocs2005_apply_calendar_status(sipe_private,
277 sbuddy,
278 sipe_status_activity_to_token(activity));
282 void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public,
283 const gchar *uri,
284 const gchar *status_name,
285 gboolean is_online,
286 struct sipe_backend_buddy_tooltip *tooltip)
288 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
289 gchar *note = NULL;
290 gboolean is_oof_note = FALSE;
291 const gchar *activity = NULL;
292 gchar *calendar = NULL;
293 const gchar *meeting_subject = NULL;
294 const gchar *meeting_location = NULL;
295 gchar *access_text = NULL;
297 #define SIPE_ADD_BUDDY_INFO(l, t) \
299 gchar *tmp = g_markup_escape_text((t), -1); \
300 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), tmp); \
301 g_free(tmp); \
303 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) \
304 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), (t))
306 if (sipe_public) { /* happens on pidgin exit */
307 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
308 if (sbuddy) {
309 note = sbuddy->note;
310 is_oof_note = sbuddy->is_oof_note;
311 activity = sbuddy->activity;
312 calendar = sipe_cal_get_description(sbuddy);
313 meeting_subject = sbuddy->meeting_subject;
314 meeting_location = sbuddy->meeting_location;
316 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
317 gboolean is_group_access = FALSE;
318 const int container_id = sipe_ocs2007_find_access_level(sipe_private,
319 "user",
320 sipe_get_no_sip_uri(uri),
321 &is_group_access);
322 const char *access_level = sipe_ocs2007_access_level_name(container_id);
323 access_text = is_group_access ?
324 g_strdup(access_level) :
325 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT,
326 access_level);
330 if (is_online) {
331 const gchar *status_str = activity ? activity : status_name;
333 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
335 if (is_online && !is_empty(calendar)) {
336 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
338 g_free(calendar);
339 if (!is_empty(meeting_location)) {
340 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", uri, meeting_location);
341 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
343 if (!is_empty(meeting_subject)) {
344 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", uri, meeting_subject);
345 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
347 if (note) {
348 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", uri, note);
349 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
350 g_strdup_printf("<i>%s</i>", note));
352 if (access_text) {
353 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
354 g_free(access_text);
358 void sipe_buddy_update_property(struct sipe_core_private *sipe_private,
359 const char *uri,
360 sipe_buddy_info_fields propkey,
361 char *property_value)
363 GSList *buddies, *entry;
365 if (property_value)
366 property_value = g_strstrip(property_value);
368 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
369 while (entry) {
370 gchar *prop_str;
371 sipe_backend_buddy p_buddy = entry->data;
373 /* for Display Name */
374 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
375 gchar *alias;
376 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
377 if (property_value && sipe_is_bad_alias(uri, alias)) {
378 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
379 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
381 g_free(alias);
383 alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
384 if (!is_empty(property_value) &&
385 (!sipe_strequal(property_value, alias) || is_empty(alias)) )
387 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
388 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
390 g_free(alias);
392 /* for other properties */
393 else {
394 if (!is_empty(property_value)) {
395 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
396 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
397 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
399 g_free(prop_str);
403 entry = entry->next;
405 g_slist_free(buddies);
409 struct ms_dlx_data;
410 struct ms_dlx_data {
411 GSList *search_rows;
412 gchar *other;
413 guint max_returns;
414 sipe_svc_callback *callback;
415 struct sipe_svc_session *session;
416 /* must call ms_dlx_free() */
417 void (*failed_callback)(struct sipe_core_private *sipe_private,
418 struct ms_dlx_data *mdd);
421 static void ms_dlx_free(struct ms_dlx_data *mdd)
423 GSList *entry = mdd->search_rows;
424 while (entry) {
425 g_free(entry->data);
426 entry = entry->next;
428 g_slist_free(mdd->search_rows);
429 sipe_svc_session_close(mdd->session);
430 g_free(mdd->other);
431 g_free(mdd);
434 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
435 #define DLX_SEARCH_ITEM \
436 "<AbEntryRequest.ChangeSearchQuery>" \
437 " <SearchOn>%s</SearchOn>" \
438 " <Value>%s</Value>" \
439 "</AbEntryRequest.ChangeSearchQuery>"
441 static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) {
442 gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1);
443 guint i = 0;
444 gchar *query = NULL;
446 while (query_rows) {
447 gchar *attr;
448 gchar *value;
450 attr = query_rows->data;
451 query_rows = g_slist_next(query_rows);
452 value = query_rows->data;
453 query_rows = g_slist_next(query_rows);
455 if (!attr || !value)
456 break;
458 attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW,
459 attr, value);
461 attrs[i] = NULL;
463 if (i) {
464 query = g_strjoinv(NULL, attrs);
465 SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s",
466 query ? query : "");
469 g_strfreev(attrs);
471 return query;
474 static void ms_dlx_webticket(struct sipe_core_private *sipe_private,
475 const gchar *base_uri,
476 const gchar *auth_uri,
477 const gchar *wsse_security,
478 gpointer callback_data)
480 struct ms_dlx_data *mdd = callback_data;
482 if (wsse_security) {
483 gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
485 SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
486 base_uri);
488 if (sipe_svc_ab_entry_request(sipe_private,
489 mdd->session,
490 auth_uri,
491 wsse_security,
492 query,
493 g_slist_length(mdd->search_rows) / 2,
494 mdd->max_returns,
495 mdd->callback,
496 mdd)) {
497 /* callback data passed down the line */
498 mdd = NULL;
500 g_free(query);
502 } else {
503 /* no ticket: this will show the minmum information */
504 SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
505 base_uri);
508 if (mdd)
509 mdd->failed_callback(sipe_private, mdd);
512 static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private,
513 struct ms_dlx_data *mdd)
515 if (!sipe_webticket_request(sipe_private,
516 mdd->session,
517 sipe_private->dlx_uri,
518 "AddressBookWebTicketBearer",
519 ms_dlx_webticket,
520 mdd)) {
521 SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
522 sipe_private->dlx_uri);
523 mdd->failed_callback(sipe_private, mdd);
527 static void search_contacts_finalize(struct sipe_core_private *sipe_private,
528 struct sipe_backend_search_results *results,
529 guint match_count,
530 gboolean more)
532 gchar *secondary = g_strdup_printf(
533 dngettext(PACKAGE_NAME,
534 "Found %d contact%s:",
535 "Found %d contacts%s:", match_count),
536 match_count, more ? _(" (more matched your query)") : "");
538 sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
539 results,
540 secondary,
541 more);
542 g_free(secondary);
545 static void search_ab_entry_response(struct sipe_core_private *sipe_private,
546 const gchar *uri,
547 SIPE_UNUSED_PARAMETER const gchar *raw,
548 sipe_xml *soap_body,
549 gpointer callback_data)
551 struct ms_dlx_data *mdd = callback_data;
553 if (soap_body) {
554 const sipe_xml *node;
555 struct sipe_backend_search_results *results;
556 GHashTable *found;
558 SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
559 uri);
561 /* any matches? */
562 node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
563 if (!node) {
564 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
565 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
566 _("No contacts found"),
567 NULL);
568 ms_dlx_free(mdd);
569 return;
572 /* OK, we found something - show the results to the user */
573 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
574 if (!results) {
575 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
576 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
577 _("Unable to display the search results"),
578 NULL);
579 ms_dlx_free(mdd);
580 return;
583 /* SearchAbEntryResult can contain duplicates */
584 found = g_hash_table_new_full(g_str_hash, g_str_equal,
585 g_free, NULL);
587 for (/* initialized above */ ; node; node = sipe_xml_twin(node)) {
588 const sipe_xml *attrs;
589 gchar *sip_uri = NULL;
590 gchar *displayname = NULL;
591 gchar *company = NULL;
592 gchar *country = NULL;
593 gchar *email = NULL;
595 for (attrs = sipe_xml_child(node, "Attributes/Attribute");
596 attrs;
597 attrs = sipe_xml_twin(attrs)) {
598 gchar *name = sipe_xml_data(sipe_xml_child(attrs,
599 "Name"));
600 gchar *value = sipe_xml_data(sipe_xml_child(attrs,
601 "Value"));
603 if (!is_empty(value)) {
604 if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) {
605 g_free(sip_uri);
606 sip_uri = value;
607 value = NULL;
608 } else if (sipe_strcase_equal(name, "displayname")) {
609 g_free(displayname);
610 displayname = value;
611 value = NULL;
612 } else if (sipe_strcase_equal(name, "mail")) {
613 g_free(email);
614 email = value;
615 value = NULL;
616 } else if (sipe_strcase_equal(name, "company")) {
617 g_free(company);
618 company = value;
619 value = NULL;
620 } else if (sipe_strcase_equal(name, "country")) {
621 g_free(country);
622 country = value;
623 value = NULL;
627 g_free(value);
628 g_free(name);
631 if (sip_uri && !g_hash_table_lookup(found, sip_uri)) {
632 gchar **uri_parts = g_strsplit(sip_uri, ":", 2);
633 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
634 results,
635 uri_parts[1],
636 displayname,
637 company,
638 country,
639 email);
640 g_strfreev(uri_parts);
642 g_hash_table_insert(found, sip_uri, (gpointer) TRUE);
643 sip_uri = NULL;
646 g_free(email);
647 g_free(country);
648 g_free(company);
649 g_free(displayname);
650 g_free(sip_uri);
653 search_contacts_finalize(sipe_private, results,
654 g_hash_table_size(found),
655 FALSE);
656 g_hash_table_destroy(found);
657 ms_dlx_free(mdd);
659 } else {
660 mdd->failed_callback(sipe_private, mdd);
664 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
665 struct sipmsg *msg,
666 SIPE_UNUSED_PARAMETER struct transaction *trans)
668 struct sipe_backend_search_results *results;
669 sipe_xml *searchResults;
670 const sipe_xml *mrow;
671 guint match_count = 0;
672 gboolean more = FALSE;
674 /* valid response? */
675 if (msg->response != 200) {
676 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
677 msg->response);
678 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
679 _("Contact search failed"),
680 NULL);
681 return(FALSE);
684 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
686 /* valid XML? */
687 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
688 if (!searchResults) {
689 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
690 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
691 _("Contact search failed"),
692 NULL);
693 return(FALSE);
696 /* any matches? */
697 mrow = sipe_xml_child(searchResults, "Body/Array/row");
698 if (!mrow) {
699 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
700 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
701 _("No contacts found"),
702 NULL);
704 sipe_xml_free(searchResults);
705 return(FALSE);
708 /* OK, we found something - show the results to the user */
709 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
710 if (!results) {
711 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
712 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
713 _("Unable to display the search results"),
714 NULL);
716 sipe_xml_free(searchResults);
717 return FALSE;
720 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
721 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
722 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
723 results,
724 uri_parts[1],
725 sipe_xml_attribute(mrow, "displayName"),
726 sipe_xml_attribute(mrow, "company"),
727 sipe_xml_attribute(mrow, "country"),
728 sipe_xml_attribute(mrow, "email"));
729 g_strfreev(uri_parts);
730 match_count++;
733 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
734 char *data = sipe_xml_data(mrow);
735 more = (g_ascii_strcasecmp(data, "true") == 0);
736 g_free(data);
739 search_contacts_finalize(sipe_private, results, match_count, more);
740 sipe_xml_free(searchResults);
742 return(TRUE);
745 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
746 struct ms_dlx_data *mdd)
748 /* error using [MS-DLX] server, retry using Active Directory */
749 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
751 sip_soap_directory_search(sipe_private,
752 100,
753 query,
754 process_search_contact_response,
755 NULL);
756 ms_dlx_free(mdd);
757 g_free(query);
760 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
761 const gchar *given_name,
762 const gchar *surname,
763 const gchar *email,
764 const gchar *company,
765 const gchar *country)
767 GSList *query_rows = NULL;
769 #define ADD_QUERY_ROW(attr, val) \
770 if (val) { \
771 query_rows = g_slist_append(query_rows, g_strdup(attr)); \
772 query_rows = g_slist_append(query_rows, g_strdup(val)); \
775 ADD_QUERY_ROW("givenName", given_name);
776 ADD_QUERY_ROW("sn", surname);
777 ADD_QUERY_ROW("mail", email);
778 ADD_QUERY_ROW("company", company);
779 ADD_QUERY_ROW("c", country);
781 if (query_rows) {
782 if (SIPE_CORE_PRIVATE->dlx_uri != NULL) {
783 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
785 mdd->search_rows = query_rows;
786 mdd->max_returns = 100;
787 mdd->callback = search_ab_entry_response;
788 mdd->failed_callback = search_ab_entry_failed;
789 mdd->session = sipe_svc_session_start();
791 ms_dlx_webticket_request(SIPE_CORE_PRIVATE, mdd);
793 } else {
794 gchar *query = prepare_buddy_search_query(query_rows, FALSE);
796 /* no [MS-DLX] server, use Active Directory search instead */
797 sip_soap_directory_search(SIPE_CORE_PRIVATE,
798 100,
799 query,
800 process_search_contact_response,
801 NULL);
802 g_free(query);
803 g_slist_free(query_rows);
808 static void get_info_finalize(struct sipe_core_private *sipe_private,
809 struct sipe_backend_buddy_info *info,
810 const gchar *uri,
811 const gchar *server_alias,
812 const gchar *email)
814 sipe_backend_buddy bbuddy;
815 struct sipe_buddy *sbuddy;
816 gchar *alias;
817 gchar *value;
819 if (!info) {
820 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
821 } else {
822 sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info);
824 if (!info)
825 return;
827 bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
829 if (is_empty(server_alias)) {
830 value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
831 bbuddy);
832 if (value) {
833 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
834 info,
835 SIPE_BUDDY_INFO_DISPLAY_NAME,
836 value);
838 } else {
839 value = g_strdup(server_alias);
842 /* present alias if it differs from server alias */
843 alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy);
844 if (alias && !sipe_strequal(alias, value))
846 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
847 info,
848 SIPE_BUDDY_INFO_ALIAS,
849 alias);
851 g_free(alias);
852 g_free(value);
854 if (is_empty(email)) {
855 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
856 bbuddy,
857 SIPE_BUDDY_INFO_EMAIL);
858 if (value) {
859 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
860 info,
861 SIPE_BUDDY_INFO_EMAIL,
862 value);
863 g_free(value);
867 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
868 bbuddy,
869 SIPE_BUDDY_INFO_SITE);
870 if (value) {
871 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
872 info,
873 SIPE_BUDDY_INFO_SITE,
874 value);
875 g_free(value);
878 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
879 if (sbuddy && sbuddy->device_name) {
880 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
881 info,
882 SIPE_BUDDY_INFO_DEVICE,
883 sbuddy->device_name);
886 sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri);
890 static void get_info_ab_entry_response(struct sipe_core_private *sipe_private,
891 const gchar *uri,
892 SIPE_UNUSED_PARAMETER const gchar *raw,
893 sipe_xml *soap_body,
894 gpointer callback_data)
896 struct ms_dlx_data *mdd = callback_data;
897 struct sipe_backend_buddy_info *info = NULL;
898 gchar *server_alias = NULL;
899 gchar *email = NULL;
901 if (soap_body) {
902 const sipe_xml *node;
904 SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s",
905 uri);
907 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
909 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
910 node;
911 node = sipe_xml_twin(node)) {
912 gchar *name = sipe_xml_data(sipe_xml_child(node,
913 "Name"));
914 gchar *value = sipe_xml_data(sipe_xml_child(node,
915 "Value"));
916 const sipe_xml *values = sipe_xml_child(node,
917 "Values");
919 /* Single value entries */
920 if (!is_empty(value)) {
922 if (sipe_strcase_equal(name, "displayname")) {
923 g_free(server_alias);
924 server_alias = value;
925 value = NULL;
926 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
927 info,
928 SIPE_BUDDY_INFO_DISPLAY_NAME,
929 server_alias);
930 } else if (sipe_strcase_equal(name, "mail")) {
931 g_free(email);
932 email = value;
933 value = NULL;
934 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
935 info,
936 SIPE_BUDDY_INFO_EMAIL,
937 email);
938 } else if (sipe_strcase_equal(name, "title")) {
939 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
940 info,
941 SIPE_BUDDY_INFO_JOB_TITLE,
942 value);
943 } else if (sipe_strcase_equal(name, "company")) {
944 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
945 info,
946 SIPE_BUDDY_INFO_COMPANY,
947 value);
948 } else if (sipe_strcase_equal(name, "country")) {
949 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
950 info,
951 SIPE_BUDDY_INFO_COUNTRY,
952 value);
955 } else if (values) {
956 gchar *first = sipe_xml_data(sipe_xml_child(values,
957 "string"));
959 if (sipe_strcase_equal(name, "telephonenumber")) {
960 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
961 info,
962 SIPE_BUDDY_INFO_WORK_PHONE,
963 first);
966 g_free(first);
969 g_free(value);
970 g_free(name);
974 /* this will show the minmum information */
975 get_info_finalize(sipe_private,
976 info,
977 mdd->other,
978 server_alias,
979 email);
981 g_free(email);
982 g_free(server_alias);
983 ms_dlx_free(mdd);
986 static gboolean process_get_info_response(struct sipe_core_private *sipe_private,
987 struct sipmsg *msg,
988 struct transaction *trans)
990 const gchar *uri = trans->payload->data;
991 struct sipe_backend_buddy_info *info = NULL;
992 gchar *server_alias = NULL;
993 gchar *email = NULL;
995 SIPE_DEBUG_INFO("Fetching %s's user info for %s",
996 uri, sipe_private->username);
998 if (msg->response != 200) {
999 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
1000 } else {
1001 sipe_xml *searchResults;
1002 const sipe_xml *mrow;
1004 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s",
1005 msg->body ? msg->body : "");
1007 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1008 if (!searchResults) {
1010 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
1012 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
1013 const gchar *value;
1014 gchar *phone_number;
1016 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1018 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
1019 email = g_strdup(sipe_xml_attribute(mrow, "email"));
1020 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
1023 * For 2007 system we will take this from ContactCard -
1024 * it has cleaner tel: URIs at least
1026 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1027 char *tel_uri = sip_to_tel_uri(phone_number);
1028 /* trims its parameters, so call first */
1029 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1030 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1031 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1032 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1033 g_free(tel_uri);
1036 if (!is_empty(server_alias)) {
1037 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1038 info,
1039 SIPE_BUDDY_INFO_DISPLAY_NAME,
1040 server_alias);
1042 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1043 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1044 info,
1045 SIPE_BUDDY_INFO_JOB_TITLE,
1046 value);
1048 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1049 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1050 info,
1051 SIPE_BUDDY_INFO_OFFICE,
1052 value);
1054 if (!is_empty(phone_number)) {
1055 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1056 info,
1057 SIPE_BUDDY_INFO_WORK_PHONE,
1058 phone_number);
1060 g_free(phone_number);
1061 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1062 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1063 info,
1064 SIPE_BUDDY_INFO_COMPANY,
1065 value);
1067 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1068 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1069 info,
1070 SIPE_BUDDY_INFO_CITY,
1071 value);
1073 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1074 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1075 info,
1076 SIPE_BUDDY_INFO_STATE,
1077 value);
1079 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1080 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1081 info,
1082 SIPE_BUDDY_INFO_COUNTRY,
1083 value);
1085 if (!is_empty(email)) {
1086 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1087 info,
1088 SIPE_BUDDY_INFO_EMAIL,
1089 email);
1092 sipe_xml_free(searchResults);
1095 /* this will show the minmum information */
1096 get_info_finalize(sipe_private,
1097 info,
1098 uri,
1099 server_alias,
1100 email);
1102 g_free(server_alias);
1103 g_free(email);
1105 return TRUE;
1108 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
1109 struct ms_dlx_data *mdd)
1111 /* error using [MS-DLX] server, retry using Active Directory */
1112 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
1113 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1115 payload->destroy = g_free;
1116 payload->data = mdd->other;
1117 mdd->other = NULL;
1119 sip_soap_directory_search(sipe_private,
1121 query,
1122 process_get_info_response,
1123 payload);
1125 ms_dlx_free(mdd);
1126 g_free(query);
1129 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
1130 const gchar *who)
1132 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1134 if (sipe_private->dlx_uri) {
1135 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1137 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1138 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(who));
1140 mdd->other = g_strdup(who);
1141 mdd->max_returns = 1;
1142 mdd->callback = get_info_ab_entry_response;
1143 mdd->failed_callback = get_info_ab_entry_failed;
1144 mdd->session = sipe_svc_session_start();
1146 ms_dlx_webticket_request(sipe_private, mdd);
1148 } else {
1149 /* no [MS-DLX] server, use Active Directory search instead */
1150 gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
1151 "msRTCSIP-PrimaryUserAddress",
1152 who);
1153 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1155 SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
1156 row ? row : "");
1158 payload->destroy = g_free;
1159 payload->data = g_strdup(who);
1161 sip_soap_directory_search(sipe_private,
1163 row,
1164 process_get_info_response,
1165 payload);
1166 g_free(row);
1170 /* Buddy menu callbacks*/
1172 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
1173 const gchar *who)
1175 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1177 /* 2007+ conference */
1178 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1179 sipe_conf_add(sipe_private, who);
1181 /* 2005- multiparty chat */
1182 } else {
1183 gchar *self = sip_uri_self(sipe_private);
1184 struct sip_session *session;
1186 session = sipe_session_add_chat(sipe_private,
1187 NULL,
1188 TRUE,
1189 self);
1190 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1191 session->chat_session,
1192 session->chat_session->title,
1193 self);
1194 g_free(self);
1196 sipe_im_invite(sipe_private, session, who,
1197 NULL, NULL, NULL, FALSE);
1201 void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public,
1202 const gchar *who)
1204 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1205 who,
1206 NULL);
1207 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1208 buddy,
1209 SIPE_BUDDY_INFO_EMAIL);
1211 if (email) {
1212 gchar *command_line = g_strdup_printf(
1213 #ifdef _WIN32
1214 "cmd /c start"
1215 #else
1216 "xdg-email"
1217 #endif
1218 " mailto:%s", email);
1219 g_free(email);
1221 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s",
1222 command_line);
1223 g_spawn_command_line_async(command_line, NULL);
1224 g_free(command_line);
1226 } else {
1227 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s",
1228 who);
1232 /* Buddy menu */
1234 static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public,
1235 struct sipe_backend_buddy_menu *menu,
1236 sipe_backend_buddy buddy,
1237 sipe_buddy_info_fields id_phone,
1238 sipe_buddy_info_fields id_display,
1239 const gchar *type)
1241 gchar *phone = sipe_backend_buddy_get_string(sipe_public,
1242 buddy,
1243 id_phone);
1244 if (phone) {
1245 gchar *display = sipe_backend_buddy_get_string(sipe_public,
1246 buddy,
1247 id_display);
1248 gchar *tmp = NULL;
1249 gchar *label = g_strdup_printf("%s %s",
1250 type,
1251 display ? display :
1252 (tmp = sip_tel_uri_denormalize(phone)));
1253 menu = sipe_backend_buddy_menu_add(sipe_public,
1254 menu,
1255 label,
1256 SIPE_BUDDY_MENU_MAKE_CALL,
1257 phone);
1258 g_free(tmp);
1259 g_free(label);
1260 g_free(display);
1261 g_free(phone);
1264 return(menu);
1267 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
1268 const gchar *buddy_name,
1269 struct sipe_backend_buddy_menu *menu)
1271 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1272 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1273 buddy_name,
1274 NULL);
1275 gchar *self = sip_uri_self(sipe_private);
1277 SIPE_SESSION_FOREACH {
1278 if (!sipe_strcase_equal(self, buddy_name) && session->chat_session)
1280 struct sipe_chat_session *chat_session = session->chat_session;
1281 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1283 if (sipe_backend_chat_find(chat_session->backend, buddy_name))
1285 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1287 if (is_conf &&
1288 /* Not conf OP */
1289 !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) &&
1290 /* We are a conf OP */
1291 conf_op) {
1292 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1293 chat_session->title);
1294 menu = sipe_backend_buddy_menu_add(sipe_public,
1295 menu,
1296 label,
1297 SIPE_BUDDY_MENU_MAKE_CHAT_LEADER,
1298 chat_session);
1299 g_free(label);
1302 if (is_conf &&
1303 /* We are a conf OP */
1304 conf_op) {
1305 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1306 chat_session->title);
1307 menu = sipe_backend_buddy_menu_add(sipe_public,
1308 menu,
1309 label,
1310 SIPE_BUDDY_MENU_REMOVE_FROM_CHAT,
1311 chat_session);
1312 g_free(label);
1315 else
1317 if (!is_conf ||
1318 (is_conf && !session->locked)) {
1319 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1320 chat_session->title);
1321 menu = sipe_backend_buddy_menu_add(sipe_public,
1322 menu,
1323 label,
1324 SIPE_BUDDY_MENU_INVITE_TO_CHAT,
1325 chat_session);
1326 g_free(label);
1330 } SIPE_SESSION_FOREACH_END;
1331 g_free(self);
1333 menu = sipe_backend_buddy_menu_add(sipe_public,
1334 menu,
1335 _("New chat"),
1336 SIPE_BUDDY_MENU_NEW_CHAT,
1337 NULL);
1339 /* add buddy's phone numbers if we have call control */
1340 if (sip_csta_is_idle(sipe_private)) {
1342 /* work phone */
1343 menu = buddy_menu_phone(sipe_public,
1344 menu,
1345 buddy,
1346 SIPE_BUDDY_INFO_WORK_PHONE,
1347 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY,
1348 _("Work"));
1349 /* mobile phone */
1350 menu = buddy_menu_phone(sipe_public,
1351 menu,
1352 buddy,
1353 SIPE_BUDDY_INFO_MOBILE_PHONE,
1354 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY,
1355 _("Mobile"));
1357 /* home phone */
1358 menu = buddy_menu_phone(sipe_public,
1359 menu,
1360 buddy,
1361 SIPE_BUDDY_INFO_HOME_PHONE,
1362 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY,
1363 _("Home"));
1365 /* other phone */
1366 menu = buddy_menu_phone(sipe_public,
1367 menu,
1368 buddy,
1369 SIPE_BUDDY_INFO_OTHER_PHONE,
1370 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY,
1371 _("Other"));
1373 /* custom1 phone */
1374 menu = buddy_menu_phone(sipe_public,
1375 menu,
1376 buddy,
1377 SIPE_BUDDY_INFO_CUSTOM1_PHONE,
1378 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY,
1379 _("Custom1"));
1383 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1384 buddy,
1385 SIPE_BUDDY_INFO_EMAIL);
1386 if (email) {
1387 menu = sipe_backend_buddy_menu_add(sipe_public,
1388 menu,
1389 _("Send email..."),
1390 SIPE_BUDDY_MENU_SEND_EMAIL,
1391 NULL);
1392 g_free(email);
1396 /* access level control */
1397 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1398 menu = sipe_backend_buddy_sub_menu_add(sipe_public,
1399 menu,
1400 _("Access level"),
1401 sipe_ocs2007_access_control_menu(sipe_private,
1402 buddy_name));
1404 return(menu);
1408 Local Variables:
1409 mode: c
1410 c-file-style: "bsd"
1411 indent-tabs-mode: t
1412 tab-width: 8
1413 End: