build: replace deprecated glib2 functions
[siplcs.git] / src / core / sipe-buddy.c
blob7d1f75d89c3ab815ee2ebdf865c0aee974c2edb8
1 /**
2 * @file sipe-buddy.c
4 * pidgin-sipe
6 * Copyright (C) 2010-11 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 gchar *search;
412 gchar *other;
413 guint entries;
414 guint max_returns;
415 sipe_svc_callback *callback;
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 g_free(mdd->search);
424 g_free(mdd->other);
425 g_free(mdd);
428 static void ms_dlx_webticket(struct sipe_core_private *sipe_private,
429 const gchar *base_uri,
430 const gchar *auth_uri,
431 const gchar *wsse_security,
432 gpointer callback_data)
434 struct ms_dlx_data *mdd = callback_data;
436 if (wsse_security) {
437 SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
438 base_uri);
440 if (sipe_svc_ab_entry_request(sipe_private,
441 auth_uri,
442 wsse_security,
443 mdd->search,
444 mdd->entries,
445 mdd->max_returns,
446 mdd->callback,
447 mdd)) {
448 /* callback data passed down the line */
449 mdd = NULL;
452 } else {
453 /* no ticket: this will show the minmum information */
454 SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
455 base_uri);
458 if (mdd)
459 mdd->failed_callback(sipe_private, mdd);
462 static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private,
463 struct ms_dlx_data *mdd)
465 if (!sipe_webticket_request(sipe_private,
466 sipe_private->dlx_uri,
467 "AddressBookWebTicketBearer",
468 ms_dlx_webticket,
469 mdd)) {
470 SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
471 sipe_private->dlx_uri);
472 mdd->failed_callback(sipe_private, mdd);
476 static void search_contacts_finalize(struct sipe_core_private *sipe_private,
477 struct sipe_backend_search_results *results,
478 guint match_count,
479 gboolean more)
481 gchar *secondary = g_strdup_printf(
482 dngettext(PACKAGE_NAME,
483 "Found %d contact%s:",
484 "Found %d contacts%s:", match_count),
485 match_count, more ? _(" (more matched your query)") : "");
487 sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
488 results,
489 secondary,
490 more);
491 g_free(secondary);
494 static void search_ab_entry_response(struct sipe_core_private *sipe_private,
495 const gchar *uri,
496 SIPE_UNUSED_PARAMETER const gchar *raw,
497 sipe_xml *soap_body,
498 gpointer callback_data)
500 struct ms_dlx_data *mdd = callback_data;
502 if (soap_body) {
503 const sipe_xml *node;
504 struct sipe_backend_search_results *results;
505 GHashTable *found;
507 SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
508 uri);
510 /* any matches? */
511 node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
512 if (!node) {
513 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
514 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
515 _("No contacts found"),
516 NULL);
517 ms_dlx_free(mdd);
518 return;
521 /* OK, we found something - show the results to the user */
522 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
523 if (!results) {
524 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
525 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
526 _("Unable to display the search results"),
527 NULL);
528 ms_dlx_free(mdd);
529 return;
532 /* SearchAbEntryResult can contain duplicates */
533 found = g_hash_table_new_full(g_str_hash, g_str_equal,
534 g_free, NULL);
536 for (/* initialized above */ ; node; node = sipe_xml_twin(node)) {
537 const sipe_xml *attrs;
538 gchar *sip_uri = NULL;
539 gchar *displayname = NULL;
540 gchar *company = NULL;
541 gchar *country = NULL;
542 gchar *email = NULL;
544 for (attrs = sipe_xml_child(node, "Attributes/Attribute");
545 attrs;
546 attrs = sipe_xml_twin(attrs)) {
547 gchar *name = sipe_xml_data(sipe_xml_child(attrs,
548 "Name"));
549 gchar *value = sipe_xml_data(sipe_xml_child(attrs,
550 "Value"));
552 if (!is_empty(value)) {
553 if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) {
554 g_free(sip_uri);
555 sip_uri = value;
556 value = NULL;
557 } else if (sipe_strcase_equal(name, "displayname")) {
558 g_free(displayname);
559 displayname = value;
560 value = NULL;
561 } else if (sipe_strcase_equal(name, "mail")) {
562 g_free(email);
563 email = value;
564 value = NULL;
565 } else if (sipe_strcase_equal(name, "company")) {
566 g_free(company);
567 company = value;
568 value = NULL;
569 } else if (sipe_strcase_equal(name, "country")) {
570 g_free(country);
571 country = value;
572 value = NULL;
576 g_free(value);
577 g_free(name);
580 if (sip_uri && !g_hash_table_lookup(found, sip_uri)) {
581 gchar **uri_parts = g_strsplit(sip_uri, ":", 2);
582 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
583 results,
584 uri_parts[1],
585 displayname,
586 company,
587 country,
588 email);
589 g_strfreev(uri_parts);
591 g_hash_table_insert(found, sip_uri, (gpointer) TRUE);
592 sip_uri = NULL;
595 g_free(email);
596 g_free(country);
597 g_free(company);
598 g_free(displayname);
599 g_free(sip_uri);
602 search_contacts_finalize(sipe_private, results,
603 g_hash_table_size(found),
604 FALSE);
605 g_hash_table_destroy(found);
606 ms_dlx_free(mdd);
608 } else {
609 mdd->failed_callback(sipe_private, mdd);
613 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
614 struct ms_dlx_data *mdd)
616 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
617 _("Contact search failed"),
618 NULL);
619 ms_dlx_free(mdd);
622 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
623 struct sipmsg *msg,
624 SIPE_UNUSED_PARAMETER struct transaction *trans)
626 struct sipe_backend_search_results *results;
627 sipe_xml *searchResults;
628 const sipe_xml *mrow;
629 guint match_count = 0;
630 gboolean more = FALSE;
632 /* valid response? */
633 if (msg->response != 200) {
634 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
635 msg->response);
636 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
637 _("Contact search failed"),
638 NULL);
639 return(FALSE);
642 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
644 /* valid XML? */
645 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
646 if (!searchResults) {
647 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
648 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
649 _("Contact search failed"),
650 NULL);
651 return(FALSE);
654 /* any matches? */
655 mrow = sipe_xml_child(searchResults, "Body/Array/row");
656 if (!mrow) {
657 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
658 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
659 _("No contacts found"),
660 NULL);
662 sipe_xml_free(searchResults);
663 return(FALSE);
666 /* OK, we found something - show the results to the user */
667 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
668 if (!results) {
669 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
670 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
671 _("Unable to display the search results"),
672 NULL);
674 sipe_xml_free(searchResults);
675 return FALSE;
678 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
679 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
680 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
681 results,
682 uri_parts[1],
683 sipe_xml_attribute(mrow, "displayName"),
684 sipe_xml_attribute(mrow, "company"),
685 sipe_xml_attribute(mrow, "country"),
686 sipe_xml_attribute(mrow, "email"));
687 g_strfreev(uri_parts);
688 match_count++;
691 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
692 char *data = sipe_xml_data(mrow);
693 more = (g_ascii_strcasecmp(data, "true") == 0);
694 g_free(data);
697 search_contacts_finalize(sipe_private, results, match_count, more);
698 sipe_xml_free(searchResults);
700 return(TRUE);
703 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
704 #define DLX_SEARCH_ITEM \
705 "<AbEntryRequest.ChangeSearchQuery>" \
706 " <SearchOn>%s</SearchOn>" \
707 " <Value>%s</Value>" \
708 "</AbEntryRequest.ChangeSearchQuery>"
710 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
711 const gchar *given_name,
712 const gchar *surname,
713 const gchar *email,
714 const gchar *company,
715 const gchar *country)
717 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
718 gchar **attrs = g_new(gchar *, 6);
719 guint i = 0;
720 gboolean dlx = (sipe_private->dlx_uri != NULL);
722 #define ADD_QUERY_ROW(a, v) \
723 if (v) attrs[i++] = g_markup_printf_escaped(dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW, \
724 a, \
727 ADD_QUERY_ROW("givenName", given_name);
728 ADD_QUERY_ROW("sn", surname);
729 ADD_QUERY_ROW("mail", email);
730 ADD_QUERY_ROW("company", company);
731 ADD_QUERY_ROW("c", country);
732 attrs[i] = NULL;
734 if (i) {
735 gchar *query;
737 query = g_strjoinv(NULL, attrs);
738 SIPE_DEBUG_INFO("sipe_core_buddy_search: rows:\n%s",
739 query ? query : "");
741 if (dlx) {
742 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
744 mdd->search = query;
745 mdd->entries = i;
746 mdd->max_returns = 100;
747 mdd->callback = search_ab_entry_response;
748 mdd->failed_callback = search_ab_entry_failed;
750 ms_dlx_webticket_request(sipe_private, mdd);
752 } else {
753 /* no [MS-DLX] server, use Active Directory search instead */
754 sip_soap_directory_search(SIPE_CORE_PRIVATE,
755 100,
756 query,
757 process_search_contact_response,
758 NULL);
759 g_free(query);
763 g_strfreev(attrs);
766 static void get_info_finalize(struct sipe_core_private *sipe_private,
767 struct sipe_backend_buddy_info *info,
768 const gchar *uri,
769 const gchar *server_alias,
770 const gchar *email)
772 sipe_backend_buddy bbuddy;
773 struct sipe_buddy *sbuddy;
774 gchar *alias;
775 gchar *value;
777 if (!info) {
778 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
779 } else {
780 sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info);
782 if (!info)
783 return;
785 bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
787 if (is_empty(server_alias)) {
788 value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
789 bbuddy);
790 if (value) {
791 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
792 info,
793 SIPE_BUDDY_INFO_DISPLAY_NAME,
794 value);
796 } else {
797 value = g_strdup(server_alias);
800 /* present alias if it differs from server alias */
801 alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy);
802 if (alias && !sipe_strequal(alias, value))
804 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
805 info,
806 SIPE_BUDDY_INFO_ALIAS,
807 alias);
809 g_free(alias);
810 g_free(value);
812 if (is_empty(email)) {
813 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
814 bbuddy,
815 SIPE_BUDDY_INFO_EMAIL);
816 if (value) {
817 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
818 info,
819 SIPE_BUDDY_INFO_EMAIL,
820 value);
821 g_free(value);
825 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
826 bbuddy,
827 SIPE_BUDDY_INFO_SITE);
828 if (value) {
829 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
830 info,
831 SIPE_BUDDY_INFO_SITE,
832 value);
833 g_free(value);
836 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
837 if (sbuddy && sbuddy->device_name) {
838 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
839 info,
840 SIPE_BUDDY_INFO_DEVICE,
841 sbuddy->device_name);
844 sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri);
848 static void get_info_ab_entry_response(struct sipe_core_private *sipe_private,
849 const gchar *uri,
850 SIPE_UNUSED_PARAMETER const gchar *raw,
851 sipe_xml *soap_body,
852 gpointer callback_data)
854 struct ms_dlx_data *mdd = callback_data;
855 struct sipe_backend_buddy_info *info = NULL;
856 gchar *server_alias = NULL;
857 gchar *email = NULL;
859 if (soap_body) {
860 const sipe_xml *node;
862 SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s",
863 uri);
865 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
867 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
868 node;
869 node = sipe_xml_twin(node)) {
870 gchar *name = sipe_xml_data(sipe_xml_child(node,
871 "Name"));
872 gchar *value = sipe_xml_data(sipe_xml_child(node,
873 "Value"));
874 const sipe_xml *values = sipe_xml_child(node,
875 "Values");
877 /* Single value entries */
878 if (!is_empty(value)) {
880 if (sipe_strcase_equal(name, "displayname")) {
881 g_free(server_alias);
882 server_alias = value;
883 value = NULL;
884 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
885 info,
886 SIPE_BUDDY_INFO_DISPLAY_NAME,
887 server_alias);
888 } else if (sipe_strcase_equal(name, "mail")) {
889 g_free(email);
890 email = value;
891 value = NULL;
892 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
893 info,
894 SIPE_BUDDY_INFO_EMAIL,
895 email);
896 } else if (sipe_strcase_equal(name, "title")) {
897 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
898 info,
899 SIPE_BUDDY_INFO_JOB_TITLE,
900 value);
901 } else if (sipe_strcase_equal(name, "company")) {
902 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
903 info,
904 SIPE_BUDDY_INFO_COMPANY,
905 value);
906 } else if (sipe_strcase_equal(name, "country")) {
907 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
908 info,
909 SIPE_BUDDY_INFO_COUNTRY,
910 value);
913 } else if (values) {
914 gchar *first = sipe_xml_data(sipe_xml_child(values,
915 "string"));
917 if (sipe_strcase_equal(name, "telephonenumber")) {
918 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
919 info,
920 SIPE_BUDDY_INFO_WORK_PHONE,
921 first);
924 g_free(first);
927 g_free(value);
928 g_free(name);
932 /* this will show the minmum information */
933 get_info_finalize(sipe_private,
934 info,
935 mdd->other,
936 server_alias,
937 email);
939 g_free(email);
940 g_free(server_alias);
941 ms_dlx_free(mdd);
944 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
945 struct ms_dlx_data *mdd)
947 /* request failed: this will show the minmum information */
948 get_info_finalize(sipe_private,
949 NULL,
950 mdd->other,
951 NULL,
952 NULL);
953 ms_dlx_free(mdd);
956 static gboolean process_get_info_response(struct sipe_core_private *sipe_private,
957 struct sipmsg *msg,
958 struct transaction *trans)
960 const gchar *uri = trans->payload->data;
961 struct sipe_backend_buddy_info *info = NULL;
962 gchar *server_alias = NULL;
963 gchar *email = NULL;
965 SIPE_DEBUG_INFO("Fetching %s's user info for %s",
966 uri, sipe_private->username);
968 if (msg->response != 200) {
969 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
970 } else {
971 sipe_xml *searchResults;
972 const sipe_xml *mrow;
974 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s",
975 msg->body ? msg->body : "");
977 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
978 if (!searchResults) {
980 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
982 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
983 const gchar *value;
984 gchar *phone_number;
986 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
988 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
989 email = g_strdup(sipe_xml_attribute(mrow, "email"));
990 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
993 * For 2007 system we will take this from ContactCard -
994 * it has cleaner tel: URIs at least
996 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
997 char *tel_uri = sip_to_tel_uri(phone_number);
998 /* trims its parameters, so call first */
999 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1000 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1001 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1002 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1003 g_free(tel_uri);
1006 if (!is_empty(server_alias)) {
1007 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1008 info,
1009 SIPE_BUDDY_INFO_DISPLAY_NAME,
1010 server_alias);
1012 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1013 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1014 info,
1015 SIPE_BUDDY_INFO_JOB_TITLE,
1016 value);
1018 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1019 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1020 info,
1021 SIPE_BUDDY_INFO_OFFICE,
1022 value);
1024 if (!is_empty(phone_number)) {
1025 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1026 info,
1027 SIPE_BUDDY_INFO_WORK_PHONE,
1028 phone_number);
1030 g_free(phone_number);
1031 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1032 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1033 info,
1034 SIPE_BUDDY_INFO_COMPANY,
1035 value);
1037 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1038 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1039 info,
1040 SIPE_BUDDY_INFO_CITY,
1041 value);
1043 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1044 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1045 info,
1046 SIPE_BUDDY_INFO_STATE,
1047 value);
1049 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1050 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1051 info,
1052 SIPE_BUDDY_INFO_COUNTRY,
1053 value);
1055 if (!is_empty(email)) {
1056 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1057 info,
1058 SIPE_BUDDY_INFO_EMAIL,
1059 email);
1062 sipe_xml_free(searchResults);
1065 /* this will show the minmum information */
1066 get_info_finalize(sipe_private,
1067 info,
1068 uri,
1069 server_alias,
1070 email);
1072 g_free(server_alias);
1073 g_free(email);
1075 return TRUE;
1078 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
1079 const gchar *who)
1081 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1083 if (sipe_private->dlx_uri) {
1084 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1086 mdd->search = g_markup_printf_escaped(DLX_SEARCH_ITEM,
1087 "msRTCSIP-PrimaryUserAddress",
1088 who);
1089 mdd->other = g_strdup(who);
1090 mdd->entries = 1;
1091 mdd->max_returns = 1;
1092 mdd->callback = get_info_ab_entry_response;
1093 mdd->failed_callback = get_info_ab_entry_failed;
1095 ms_dlx_webticket_request(sipe_private, mdd);
1097 } else {
1098 /* no [MS-DLX] server, use Active Directory search instead */
1099 gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
1100 "msRTCSIP-PrimaryUserAddress",
1101 who);
1102 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1104 SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
1105 row ? row : "");
1107 payload->destroy = g_free;
1108 payload->data = g_strdup(who);
1110 sip_soap_directory_search(sipe_private,
1112 row,
1113 process_get_info_response,
1114 payload);
1115 g_free(row);
1119 /* Buddy menu callbacks*/
1121 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
1122 const gchar *who)
1124 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1126 /* 2007+ conference */
1127 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1128 sipe_conf_add(sipe_private, who);
1130 /* 2005- multiparty chat */
1131 } else {
1132 gchar *self = sip_uri_self(sipe_private);
1133 struct sip_session *session;
1135 session = sipe_session_add_chat(sipe_private,
1136 NULL,
1137 TRUE,
1138 self);
1139 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1140 session->chat_session,
1141 session->chat_session->title,
1142 self);
1143 g_free(self);
1145 sipe_im_invite(sipe_private, session, who,
1146 NULL, NULL, NULL, FALSE);
1150 void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public,
1151 const gchar *who)
1153 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1154 who,
1155 NULL);
1156 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1157 buddy,
1158 SIPE_BUDDY_INFO_EMAIL);
1160 if (email) {
1161 gchar *command_line = g_strdup_printf(
1162 #ifdef _WIN32
1163 "cmd /c start"
1164 #else
1165 "xdg-email"
1166 #endif
1167 " mailto:%s", email);
1168 g_free(email);
1170 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s",
1171 command_line);
1172 g_spawn_command_line_async(command_line, NULL);
1173 g_free(command_line);
1175 } else {
1176 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s",
1177 who);
1181 /* Buddy menu */
1183 static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public,
1184 struct sipe_backend_buddy_menu *menu,
1185 sipe_backend_buddy buddy,
1186 sipe_buddy_info_fields id_phone,
1187 sipe_buddy_info_fields id_display,
1188 const gchar *type)
1190 gchar *phone = sipe_backend_buddy_get_string(sipe_public,
1191 buddy,
1192 id_phone);
1193 if (phone) {
1194 gchar *display = sipe_backend_buddy_get_string(sipe_public,
1195 buddy,
1196 id_display);
1197 gchar *tmp = NULL;
1198 gchar *label = g_strdup_printf("%s %s",
1199 type,
1200 display ? display :
1201 (tmp = sip_tel_uri_denormalize(phone)));
1202 menu = sipe_backend_buddy_menu_add(sipe_public,
1203 menu,
1204 label,
1205 SIPE_BUDDY_MENU_MAKE_CALL,
1206 phone);
1207 g_free(tmp);
1208 g_free(label);
1209 g_free(display);
1210 g_free(phone);
1213 return(menu);
1216 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
1217 const gchar *buddy_name,
1218 struct sipe_backend_buddy_menu *menu)
1220 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1221 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1222 buddy_name,
1223 NULL);
1224 gchar *self = sip_uri_self(sipe_private);
1226 SIPE_SESSION_FOREACH {
1227 if (!sipe_strcase_equal(self, buddy_name) && session->chat_session)
1229 struct sipe_chat_session *chat_session = session->chat_session;
1230 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1232 if (sipe_backend_chat_find(chat_session->backend, buddy_name))
1234 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1236 if (is_conf &&
1237 /* Not conf OP */
1238 !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) &&
1239 /* We are a conf OP */
1240 conf_op) {
1241 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1242 chat_session->title);
1243 menu = sipe_backend_buddy_menu_add(sipe_public,
1244 menu,
1245 label,
1246 SIPE_BUDDY_MENU_MAKE_CHAT_LEADER,
1247 chat_session);
1248 g_free(label);
1251 if (is_conf &&
1252 /* We are a conf OP */
1253 conf_op) {
1254 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1255 chat_session->title);
1256 menu = sipe_backend_buddy_menu_add(sipe_public,
1257 menu,
1258 label,
1259 SIPE_BUDDY_MENU_REMOVE_FROM_CHAT,
1260 chat_session);
1261 g_free(label);
1264 else
1266 if (!is_conf ||
1267 (is_conf && !session->locked)) {
1268 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1269 chat_session->title);
1270 menu = sipe_backend_buddy_menu_add(sipe_public,
1271 menu,
1272 label,
1273 SIPE_BUDDY_MENU_INVITE_TO_CHAT,
1274 chat_session);
1275 g_free(label);
1279 } SIPE_SESSION_FOREACH_END;
1280 g_free(self);
1282 menu = sipe_backend_buddy_menu_add(sipe_public,
1283 menu,
1284 _("New chat"),
1285 SIPE_BUDDY_MENU_NEW_CHAT,
1286 NULL);
1288 /* add buddy's phone numbers if we have call control */
1289 if (sip_csta_is_idle(sipe_private)) {
1291 /* work phone */
1292 menu = buddy_menu_phone(sipe_public,
1293 menu,
1294 buddy,
1295 SIPE_BUDDY_INFO_WORK_PHONE,
1296 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY,
1297 _("Work"));
1298 /* mobile phone */
1299 menu = buddy_menu_phone(sipe_public,
1300 menu,
1301 buddy,
1302 SIPE_BUDDY_INFO_MOBILE_PHONE,
1303 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY,
1304 _("Mobile"));
1306 /* home phone */
1307 menu = buddy_menu_phone(sipe_public,
1308 menu,
1309 buddy,
1310 SIPE_BUDDY_INFO_HOME_PHONE,
1311 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY,
1312 _("Home"));
1314 /* other phone */
1315 menu = buddy_menu_phone(sipe_public,
1316 menu,
1317 buddy,
1318 SIPE_BUDDY_INFO_OTHER_PHONE,
1319 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY,
1320 _("Other"));
1322 /* custom1 phone */
1323 menu = buddy_menu_phone(sipe_public,
1324 menu,
1325 buddy,
1326 SIPE_BUDDY_INFO_CUSTOM1_PHONE,
1327 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY,
1328 _("Custom1"));
1332 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1333 buddy,
1334 SIPE_BUDDY_INFO_EMAIL);
1335 if (email) {
1336 menu = sipe_backend_buddy_menu_add(sipe_public,
1337 menu,
1338 _("Send email..."),
1339 SIPE_BUDDY_MENU_SEND_EMAIL,
1340 NULL);
1341 g_free(email);
1345 /* access level control */
1346 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1347 menu = sipe_backend_buddy_sub_menu_add(sipe_public,
1348 menu,
1349 _("Access level"),
1350 sipe_ocs2007_access_control_menu(sipe_private,
1351 buddy_name));
1353 return(menu);
1357 Local Variables:
1358 mode: c
1359 c-file-style: "bsd"
1360 indent-tabs-mode: t
1361 tab-width: 8
1362 End: