buddy: refresh photos on provisioning v2 notification
[siplcs.git] / src / core / sipe-buddy.c
blob22d02c2bfebdf1933fbb45c35c36af943cf06a0c
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 <stdlib.h>
28 #include <string.h>
29 #include <time.h>
31 #include <glib.h>
33 #include "http-conn.h"
34 #include "sipe-common.h"
35 #include "sipmsg.h"
36 #include "sip-csta.h"
37 #include "sip-soap.h"
38 #include "sip-transport.h"
39 #include "sipe-backend.h"
40 #include "sipe-buddy.h"
41 #include "sipe-cal.h"
42 #include "sipe-chat.h"
43 #include "sipe-conf.h"
44 #include "sipe-core.h"
45 #include "sipe-core-private.h"
46 #include "sipe-group.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-utils.h"
57 #include "sipe-webticket.h"
58 #include "sipe-xml.h"
60 struct photo_response_data {
61 struct sipe_core_private *sipe_private;
62 gchar *who;
63 gchar *photo_hash;
64 HttpConn *conn;
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)
74 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies, uri);
75 if (!buddy) {
76 buddy = g_new0(struct sipe_buddy, 1);
77 buddy->name = g_strdup(uri);
78 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
80 SIPE_DEBUG_INFO("sipe_buddy_add: Added buddy %s", uri);
82 buddy_fetch_photo(sipe_private, uri);
83 } else {
84 SIPE_DEBUG_INFO("sipe_buddy_add: Buddy %s already exists", uri);
87 return buddy;
90 static void buddy_free(struct sipe_buddy *buddy)
92 #ifndef _WIN32
94 * We are calling g_hash_table_foreach_steal(). That means that no
95 * key/value deallocation functions are called. Therefore the glib
96 * hash code does not touch the key (buddy->name) or value (buddy)
97 * of the to-be-deleted hash node at all. It follows that we
99 * - MUST free the memory for the key ourselves and
100 * - ARE allowed to do it in this function
102 * Conclusion: glib must be broken on the Windows platform if sipe
103 * crashes with SIGTRAP when closing. You'll have to live
104 * with the memory leak until this is fixed.
106 g_free(buddy->name);
107 #endif
108 g_free(buddy->activity);
109 g_free(buddy->meeting_subject);
110 g_free(buddy->meeting_location);
111 g_free(buddy->note);
113 g_free(buddy->cal_start_time);
114 g_free(buddy->cal_free_busy_base64);
115 g_free(buddy->cal_free_busy);
116 g_free(buddy->last_non_cal_activity);
118 sipe_cal_free_working_hours(buddy->cal_working_hours);
120 g_free(buddy->device_name);
121 g_slist_free(buddy->groups);
122 g_free(buddy);
125 static gboolean buddy_free_cb(SIPE_UNUSED_PARAMETER gpointer key,
126 gpointer buddy,
127 SIPE_UNUSED_PARAMETER gpointer user_data)
129 buddy_free(buddy);
130 /* We must return TRUE as the key/value have already been deleted */
131 return(TRUE);
134 void sipe_buddy_free_all(struct sipe_core_private *sipe_private)
136 g_hash_table_foreach_steal(sipe_private->buddies,
137 buddy_free_cb,
138 NULL);
140 /* core is being deallocated, remove all its pending photo requests */
141 while (sipe_private->pending_photo_requests) {
142 struct photo_response_data *data =
143 sipe_private->pending_photo_requests->data;
144 sipe_private->pending_photo_requests =
145 g_slist_remove(sipe_private->pending_photo_requests, data);
146 photo_response_data_free(data);
150 gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public,
151 const gchar *uri,
152 guint activity,
153 const gchar *status_text)
155 struct sipe_buddy *sbuddy;
156 const char *activity_str;
158 if (!sipe_public) return NULL; /* happens on pidgin exit */
160 sbuddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, uri);
161 if (!sbuddy) return NULL;
163 activity_str = sbuddy->activity ? sbuddy->activity :
164 (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
165 status_text : NULL;
167 if (activity_str && sbuddy->note) {
168 return g_strdup_printf("%s - <i>%s</i>", activity_str, sbuddy->note);
169 } else if (activity_str) {
170 return g_strdup(activity_str);
171 } else if (sbuddy->note) {
172 return g_strdup_printf("<i>%s</i>", sbuddy->note);
173 } else {
174 return NULL;
178 gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private,
179 const gchar *with)
181 sipe_backend_buddy pbuddy;
182 gchar *alias = NULL;
183 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
184 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
186 return alias;
189 void sipe_core_buddy_group(struct sipe_core_public *sipe_public,
190 const gchar *who,
191 const gchar *old_group_name,
192 const gchar *new_group_name)
194 struct sipe_buddy * buddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, who);
195 struct sipe_group * old_group = NULL;
196 struct sipe_group * new_group;
198 SIPE_DEBUG_INFO("sipe_core_buddy_group: who:%s old_group_name:%s new_group_name:%s",
199 who ? who : "", old_group_name ? old_group_name : "", new_group_name ? new_group_name : "");
201 if(!buddy) { // buddy not in roaming list
202 return;
205 if (old_group_name) {
206 old_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, old_group_name);
208 new_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, new_group_name);
210 if (old_group) {
211 buddy->groups = g_slist_remove(buddy->groups, old_group);
212 SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy %s removed from old group %s", who, old_group_name);
215 if (!new_group) {
216 sipe_group_create(SIPE_CORE_PRIVATE, new_group_name, who);
217 } else {
218 buddy->groups = slist_insert_unique_sorted(buddy->groups, new_group, (GCompareFunc)sipe_group_compare);
219 sipe_core_group_set_user(sipe_public, who);
223 void sipe_core_buddy_add(struct sipe_core_public *sipe_public,
224 const gchar *uri,
225 const gchar *group_name)
227 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
229 if (!g_hash_table_lookup(sipe_private->buddies, uri)) {
230 struct sipe_buddy *b = sipe_buddy_add(sipe_private, uri);
231 b->just_added = TRUE;
233 /* @TODO should go to callback */
234 sipe_subscribe_presence_single(sipe_private, b->name);
236 } else {
237 SIPE_DEBUG_INFO("sipe_core_buddy_add: buddy %s already in internal list",
238 uri);
241 sipe_core_buddy_group(sipe_public,
242 uri,
243 NULL,
244 group_name);
248 * Unassociates buddy from group first.
249 * Then see if no groups left, removes buddy completely.
250 * Otherwise updates buddy groups on server.
252 void sipe_core_buddy_remove(struct sipe_core_public *sipe_public,
253 const gchar *uri,
254 const gchar *group_name)
256 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
257 struct sipe_buddy *b = g_hash_table_lookup(sipe_private->buddies,
258 uri);
260 if (!b) return;
262 if (group_name) {
263 struct sipe_group *g = sipe_group_find_by_name(sipe_private,
264 group_name);
265 if (g) {
266 b->groups = g_slist_remove(b->groups, g);
267 SIPE_DEBUG_INFO("sipe_core_buddy_remove: buddy %s removed from group %s",
268 uri, g->name);
272 if (g_slist_length(b->groups) < 1) {
273 gchar *action_name = sipe_utils_presence_key(uri);
274 sipe_schedule_cancel(sipe_private, action_name);
275 g_free(action_name);
277 g_hash_table_remove(sipe_private->buddies, uri);
279 if (b->name) {
280 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
281 b->name);
282 sip_soap_request(sipe_private,
283 "deleteContact",
284 request);
285 g_free(request);
288 buddy_free(b);
289 } else {
290 /* updates groups on server */
291 sipe_core_group_set_user(sipe_public, b->name);
296 void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
297 const gchar *uri,
298 guint activity)
300 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
301 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies,
302 uri);
304 if (!sbuddy) return;
306 /* Check if on 2005 system contact's calendar,
307 * then set/preserve it.
309 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
310 sipe_backend_buddy_set_status(sipe_public, uri, activity);
311 } else {
312 sipe_ocs2005_apply_calendar_status(sipe_private,
313 sbuddy,
314 sipe_status_activity_to_token(activity));
318 void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public,
319 const gchar *uri,
320 const gchar *status_name,
321 gboolean is_online,
322 struct sipe_backend_buddy_tooltip *tooltip)
324 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
325 gchar *note = NULL;
326 gboolean is_oof_note = FALSE;
327 const gchar *activity = NULL;
328 gchar *calendar = NULL;
329 const gchar *meeting_subject = NULL;
330 const gchar *meeting_location = NULL;
331 gchar *access_text = NULL;
333 #define SIPE_ADD_BUDDY_INFO(l, t) \
335 gchar *tmp = g_markup_escape_text((t), -1); \
336 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), tmp); \
337 g_free(tmp); \
339 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) \
340 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), (t))
342 if (sipe_public) { /* happens on pidgin exit */
343 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
344 if (sbuddy) {
345 note = sbuddy->note;
346 is_oof_note = sbuddy->is_oof_note;
347 activity = sbuddy->activity;
348 calendar = sipe_cal_get_description(sbuddy);
349 meeting_subject = sbuddy->meeting_subject;
350 meeting_location = sbuddy->meeting_location;
352 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
353 gboolean is_group_access = FALSE;
354 const int container_id = sipe_ocs2007_find_access_level(sipe_private,
355 "user",
356 sipe_get_no_sip_uri(uri),
357 &is_group_access);
358 const char *access_level = sipe_ocs2007_access_level_name(container_id);
359 access_text = is_group_access ?
360 g_strdup(access_level) :
361 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT,
362 access_level);
366 if (is_online) {
367 const gchar *status_str = activity ? activity : status_name;
369 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
371 if (is_online && !is_empty(calendar)) {
372 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
374 g_free(calendar);
375 if (!is_empty(meeting_location)) {
376 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", uri, meeting_location);
377 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
379 if (!is_empty(meeting_subject)) {
380 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", uri, meeting_subject);
381 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
383 if (note) {
384 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", uri, note);
385 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
386 g_strdup_printf("<i>%s</i>", note));
388 if (access_text) {
389 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
390 g_free(access_text);
394 void sipe_buddy_update_property(struct sipe_core_private *sipe_private,
395 const char *uri,
396 sipe_buddy_info_fields propkey,
397 char *property_value)
399 GSList *buddies, *entry;
401 if (property_value)
402 property_value = g_strstrip(property_value);
404 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
405 while (entry) {
406 gchar *prop_str;
407 sipe_backend_buddy p_buddy = entry->data;
409 /* for Display Name */
410 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
411 gchar *alias;
412 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
413 if (property_value && sipe_is_bad_alias(uri, alias)) {
414 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
415 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
417 g_free(alias);
419 alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
420 if (!is_empty(property_value) &&
421 (!sipe_strequal(property_value, alias) || is_empty(alias)) )
423 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
424 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
426 g_free(alias);
428 /* for other properties */
429 else {
430 if (!is_empty(property_value)) {
431 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
432 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
433 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
435 g_free(prop_str);
439 entry = entry->next;
441 g_slist_free(buddies);
445 struct ms_dlx_data;
446 struct ms_dlx_data {
447 GSList *search_rows;
448 gchar *other;
449 guint max_returns;
450 sipe_svc_callback *callback;
451 struct sipe_svc_session *session;
452 gchar *wsse_security;
453 /* must call ms_dlx_free() */
454 void (*failed_callback)(struct sipe_core_private *sipe_private,
455 struct ms_dlx_data *mdd);
458 static void ms_dlx_free(struct ms_dlx_data *mdd)
460 GSList *entry = mdd->search_rows;
461 while (entry) {
462 g_free(entry->data);
463 entry = entry->next;
465 g_slist_free(mdd->search_rows);
466 sipe_svc_session_close(mdd->session);
467 g_free(mdd->other);
468 g_free(mdd->wsse_security);
469 g_free(mdd);
472 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
473 #define DLX_SEARCH_ITEM \
474 "<AbEntryRequest.ChangeSearchQuery>" \
475 " <SearchOn>%s</SearchOn>" \
476 " <Value>%s</Value>" \
477 "</AbEntryRequest.ChangeSearchQuery>"
479 static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) {
480 gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1);
481 guint i = 0;
482 gchar *query = NULL;
484 while (query_rows) {
485 gchar *attr;
486 gchar *value;
488 attr = query_rows->data;
489 query_rows = g_slist_next(query_rows);
490 value = query_rows->data;
491 query_rows = g_slist_next(query_rows);
493 if (!attr || !value)
494 break;
496 attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW,
497 attr, value);
499 attrs[i] = NULL;
501 if (i) {
502 query = g_strjoinv(NULL, attrs);
503 SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s",
504 query ? query : "");
507 g_strfreev(attrs);
509 return query;
512 static void ms_dlx_webticket(struct sipe_core_private *sipe_private,
513 const gchar *base_uri,
514 const gchar *auth_uri,
515 const gchar *wsse_security,
516 gpointer callback_data)
518 struct ms_dlx_data *mdd = callback_data;
520 if (wsse_security) {
521 gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
523 SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
524 base_uri);
526 if (sipe_svc_ab_entry_request(sipe_private,
527 mdd->session,
528 auth_uri,
529 wsse_security,
530 query,
531 g_slist_length(mdd->search_rows) / 2,
532 mdd->max_returns,
533 mdd->callback,
534 mdd)) {
536 /* keep webticket security token for potential further use */
537 mdd->wsse_security = g_strdup(wsse_security);
539 /* callback data passed down the line */
540 mdd = NULL;
542 g_free(query);
544 } else {
545 /* no ticket: this will show the minmum information */
546 SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
547 base_uri);
550 if (mdd)
551 mdd->failed_callback(sipe_private, mdd);
554 static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private,
555 struct ms_dlx_data *mdd)
557 if (!sipe_webticket_request(sipe_private,
558 mdd->session,
559 sipe_private->dlx_uri,
560 "AddressBookWebTicketBearer",
561 ms_dlx_webticket,
562 mdd)) {
563 SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
564 sipe_private->dlx_uri);
565 mdd->failed_callback(sipe_private, mdd);
569 static void search_contacts_finalize(struct sipe_core_private *sipe_private,
570 struct sipe_backend_search_results *results,
571 guint match_count,
572 gboolean more)
574 gchar *secondary = g_strdup_printf(
575 dngettext(PACKAGE_NAME,
576 "Found %d contact%s:",
577 "Found %d contacts%s:", match_count),
578 match_count, more ? _(" (more matched your query)") : "");
580 sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
581 results,
582 secondary,
583 more);
584 g_free(secondary);
587 static void search_ab_entry_response(struct sipe_core_private *sipe_private,
588 const gchar *uri,
589 SIPE_UNUSED_PARAMETER const gchar *raw,
590 sipe_xml *soap_body,
591 gpointer callback_data)
593 struct ms_dlx_data *mdd = callback_data;
595 if (soap_body) {
596 const sipe_xml *node;
597 struct sipe_backend_search_results *results;
598 GHashTable *found;
600 SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
601 uri);
603 /* any matches? */
604 node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
605 if (!node) {
606 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
607 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
608 _("No contacts found"),
609 NULL);
610 ms_dlx_free(mdd);
611 return;
614 /* OK, we found something - show the results to the user */
615 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
616 if (!results) {
617 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
618 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
619 _("Unable to display the search results"),
620 NULL);
621 ms_dlx_free(mdd);
622 return;
625 /* SearchAbEntryResult can contain duplicates */
626 found = g_hash_table_new_full(g_str_hash, g_str_equal,
627 g_free, NULL);
629 for (/* initialized above */ ; node; node = sipe_xml_twin(node)) {
630 const sipe_xml *attrs;
631 gchar *sip_uri = NULL;
632 gchar *displayname = NULL;
633 gchar *company = NULL;
634 gchar *country = NULL;
635 gchar *email = NULL;
637 for (attrs = sipe_xml_child(node, "Attributes/Attribute");
638 attrs;
639 attrs = sipe_xml_twin(attrs)) {
640 gchar *name = sipe_xml_data(sipe_xml_child(attrs,
641 "Name"));
642 gchar *value = sipe_xml_data(sipe_xml_child(attrs,
643 "Value"));
645 if (!is_empty(value)) {
646 if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) {
647 g_free(sip_uri);
648 sip_uri = value;
649 value = NULL;
650 } else if (sipe_strcase_equal(name, "displayname")) {
651 g_free(displayname);
652 displayname = value;
653 value = NULL;
654 } else if (sipe_strcase_equal(name, "mail")) {
655 g_free(email);
656 email = value;
657 value = NULL;
658 } else if (sipe_strcase_equal(name, "company")) {
659 g_free(company);
660 company = value;
661 value = NULL;
662 } else if (sipe_strcase_equal(name, "country")) {
663 g_free(country);
664 country = value;
665 value = NULL;
669 g_free(value);
670 g_free(name);
673 if (sip_uri && !g_hash_table_lookup(found, sip_uri)) {
674 gchar **uri_parts = g_strsplit(sip_uri, ":", 2);
675 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
676 results,
677 uri_parts[1],
678 displayname,
679 company,
680 country,
681 email);
682 g_strfreev(uri_parts);
684 g_hash_table_insert(found, sip_uri, (gpointer) TRUE);
685 sip_uri = NULL;
688 g_free(email);
689 g_free(country);
690 g_free(company);
691 g_free(displayname);
692 g_free(sip_uri);
695 search_contacts_finalize(sipe_private, results,
696 g_hash_table_size(found),
697 FALSE);
698 g_hash_table_destroy(found);
699 ms_dlx_free(mdd);
701 } else {
702 mdd->failed_callback(sipe_private, mdd);
706 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
707 struct sipmsg *msg,
708 SIPE_UNUSED_PARAMETER struct transaction *trans)
710 struct sipe_backend_search_results *results;
711 sipe_xml *searchResults;
712 const sipe_xml *mrow;
713 guint match_count = 0;
714 gboolean more = FALSE;
716 /* valid response? */
717 if (msg->response != 200) {
718 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
719 msg->response);
720 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
721 _("Contact search failed"),
722 NULL);
723 return(FALSE);
726 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
728 /* valid XML? */
729 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
730 if (!searchResults) {
731 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
732 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
733 _("Contact search failed"),
734 NULL);
735 return(FALSE);
738 /* any matches? */
739 mrow = sipe_xml_child(searchResults, "Body/Array/row");
740 if (!mrow) {
741 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
742 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
743 _("No contacts found"),
744 NULL);
746 sipe_xml_free(searchResults);
747 return(FALSE);
750 /* OK, we found something - show the results to the user */
751 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
752 if (!results) {
753 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
754 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
755 _("Unable to display the search results"),
756 NULL);
758 sipe_xml_free(searchResults);
759 return FALSE;
762 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
763 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
764 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
765 results,
766 uri_parts[1],
767 sipe_xml_attribute(mrow, "displayName"),
768 sipe_xml_attribute(mrow, "company"),
769 sipe_xml_attribute(mrow, "country"),
770 sipe_xml_attribute(mrow, "email"));
771 g_strfreev(uri_parts);
772 match_count++;
775 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
776 char *data = sipe_xml_data(mrow);
777 more = (g_ascii_strcasecmp(data, "true") == 0);
778 g_free(data);
781 search_contacts_finalize(sipe_private, results, match_count, more);
782 sipe_xml_free(searchResults);
784 return(TRUE);
787 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
788 struct ms_dlx_data *mdd)
790 /* error using [MS-DLX] server, retry using Active Directory */
791 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
793 sip_soap_directory_search(sipe_private,
794 100,
795 query,
796 process_search_contact_response,
797 NULL);
798 ms_dlx_free(mdd);
799 g_free(query);
802 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
803 const gchar *given_name,
804 const gchar *surname,
805 const gchar *email,
806 const gchar *company,
807 const gchar *country)
809 GSList *query_rows = NULL;
811 #define ADD_QUERY_ROW(attr, val) \
812 if (val) { \
813 query_rows = g_slist_append(query_rows, g_strdup(attr)); \
814 query_rows = g_slist_append(query_rows, g_strdup(val)); \
817 ADD_QUERY_ROW("givenName", given_name);
818 ADD_QUERY_ROW("sn", surname);
819 ADD_QUERY_ROW("mail", email);
820 ADD_QUERY_ROW("company", company);
821 ADD_QUERY_ROW("c", country);
823 if (query_rows) {
824 if (SIPE_CORE_PRIVATE->dlx_uri != NULL) {
825 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
827 mdd->search_rows = query_rows;
828 mdd->max_returns = 100;
829 mdd->callback = search_ab_entry_response;
830 mdd->failed_callback = search_ab_entry_failed;
831 mdd->session = sipe_svc_session_start();
833 ms_dlx_webticket_request(SIPE_CORE_PRIVATE, mdd);
835 } else {
836 gchar *query = prepare_buddy_search_query(query_rows, FALSE);
838 /* no [MS-DLX] server, use Active Directory search instead */
839 sip_soap_directory_search(SIPE_CORE_PRIVATE,
840 100,
841 query,
842 process_search_contact_response,
843 NULL);
844 g_free(query);
845 g_slist_free(query_rows);
850 static void get_info_finalize(struct sipe_core_private *sipe_private,
851 struct sipe_backend_buddy_info *info,
852 const gchar *uri,
853 const gchar *server_alias,
854 const gchar *email)
856 sipe_backend_buddy bbuddy;
857 struct sipe_buddy *sbuddy;
858 gchar *alias;
859 gchar *value;
861 if (!info) {
862 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
863 } else {
864 sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info);
866 if (!info)
867 return;
869 bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
871 if (is_empty(server_alias)) {
872 value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
873 bbuddy);
874 if (value) {
875 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
876 info,
877 SIPE_BUDDY_INFO_DISPLAY_NAME,
878 value);
880 } else {
881 value = g_strdup(server_alias);
884 /* present alias if it differs from server alias */
885 alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy);
886 if (alias && !sipe_strequal(alias, value))
888 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
889 info,
890 SIPE_BUDDY_INFO_ALIAS,
891 alias);
893 g_free(alias);
894 g_free(value);
896 if (is_empty(email)) {
897 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
898 bbuddy,
899 SIPE_BUDDY_INFO_EMAIL);
900 if (value) {
901 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
902 info,
903 SIPE_BUDDY_INFO_EMAIL,
904 value);
905 g_free(value);
909 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
910 bbuddy,
911 SIPE_BUDDY_INFO_SITE);
912 if (value) {
913 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
914 info,
915 SIPE_BUDDY_INFO_SITE,
916 value);
917 g_free(value);
920 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
921 if (sbuddy && sbuddy->device_name) {
922 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
923 info,
924 SIPE_BUDDY_INFO_DEVICE,
925 sbuddy->device_name);
928 sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri);
932 static void get_info_ab_entry_response(struct sipe_core_private *sipe_private,
933 const gchar *uri,
934 SIPE_UNUSED_PARAMETER const gchar *raw,
935 sipe_xml *soap_body,
936 gpointer callback_data)
938 struct ms_dlx_data *mdd = callback_data;
939 struct sipe_backend_buddy_info *info = NULL;
940 gchar *server_alias = NULL;
941 gchar *email = NULL;
943 if (soap_body) {
944 const sipe_xml *node;
946 SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s",
947 uri);
949 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
951 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
952 node;
953 node = sipe_xml_twin(node)) {
954 gchar *name = sipe_xml_data(sipe_xml_child(node,
955 "Name"));
956 gchar *value = sipe_xml_data(sipe_xml_child(node,
957 "Value"));
958 const sipe_xml *values = sipe_xml_child(node,
959 "Values");
961 /* Single value entries */
962 if (!is_empty(value)) {
964 if (sipe_strcase_equal(name, "displayname")) {
965 g_free(server_alias);
966 server_alias = value;
967 value = NULL;
968 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
969 info,
970 SIPE_BUDDY_INFO_DISPLAY_NAME,
971 server_alias);
972 } else if (sipe_strcase_equal(name, "mail")) {
973 g_free(email);
974 email = value;
975 value = NULL;
976 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
977 info,
978 SIPE_BUDDY_INFO_EMAIL,
979 email);
980 } else if (sipe_strcase_equal(name, "title")) {
981 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
982 info,
983 SIPE_BUDDY_INFO_JOB_TITLE,
984 value);
985 } else if (sipe_strcase_equal(name, "company")) {
986 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
987 info,
988 SIPE_BUDDY_INFO_COMPANY,
989 value);
990 } else if (sipe_strcase_equal(name, "country")) {
991 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
992 info,
993 SIPE_BUDDY_INFO_COUNTRY,
994 value);
997 } else if (values) {
998 gchar *first = sipe_xml_data(sipe_xml_child(values,
999 "string"));
1001 if (sipe_strcase_equal(name, "telephonenumber")) {
1002 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1003 info,
1004 SIPE_BUDDY_INFO_WORK_PHONE,
1005 first);
1008 g_free(first);
1011 g_free(value);
1012 g_free(name);
1016 /* this will show the minmum information */
1017 get_info_finalize(sipe_private,
1018 info,
1019 mdd->other,
1020 server_alias,
1021 email);
1023 g_free(email);
1024 g_free(server_alias);
1025 ms_dlx_free(mdd);
1028 static gboolean process_get_info_response(struct sipe_core_private *sipe_private,
1029 struct sipmsg *msg,
1030 struct transaction *trans)
1032 const gchar *uri = trans->payload->data;
1033 struct sipe_backend_buddy_info *info = NULL;
1034 gchar *server_alias = NULL;
1035 gchar *email = NULL;
1037 SIPE_DEBUG_INFO("Fetching %s's user info for %s",
1038 uri, sipe_private->username);
1040 if (msg->response != 200) {
1041 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
1042 } else {
1043 sipe_xml *searchResults;
1044 const sipe_xml *mrow;
1046 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s",
1047 msg->body ? msg->body : "");
1049 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1050 if (!searchResults) {
1052 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
1054 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
1055 const gchar *value;
1056 gchar *phone_number;
1058 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1060 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
1061 email = g_strdup(sipe_xml_attribute(mrow, "email"));
1062 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
1065 * For 2007 system we will take this from ContactCard -
1066 * it has cleaner tel: URIs at least
1068 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1069 char *tel_uri = sip_to_tel_uri(phone_number);
1070 /* trims its parameters, so call first */
1071 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1072 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1073 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1074 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1075 g_free(tel_uri);
1078 if (!is_empty(server_alias)) {
1079 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1080 info,
1081 SIPE_BUDDY_INFO_DISPLAY_NAME,
1082 server_alias);
1084 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1085 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1086 info,
1087 SIPE_BUDDY_INFO_JOB_TITLE,
1088 value);
1090 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1091 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1092 info,
1093 SIPE_BUDDY_INFO_OFFICE,
1094 value);
1096 if (!is_empty(phone_number)) {
1097 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1098 info,
1099 SIPE_BUDDY_INFO_WORK_PHONE,
1100 phone_number);
1102 g_free(phone_number);
1103 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1104 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1105 info,
1106 SIPE_BUDDY_INFO_COMPANY,
1107 value);
1109 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1110 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1111 info,
1112 SIPE_BUDDY_INFO_CITY,
1113 value);
1115 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1116 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1117 info,
1118 SIPE_BUDDY_INFO_STATE,
1119 value);
1121 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1122 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1123 info,
1124 SIPE_BUDDY_INFO_COUNTRY,
1125 value);
1127 if (!is_empty(email)) {
1128 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1129 info,
1130 SIPE_BUDDY_INFO_EMAIL,
1131 email);
1134 sipe_xml_free(searchResults);
1137 /* this will show the minmum information */
1138 get_info_finalize(sipe_private,
1139 info,
1140 uri,
1141 server_alias,
1142 email);
1144 g_free(server_alias);
1145 g_free(email);
1147 return TRUE;
1150 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
1151 struct ms_dlx_data *mdd)
1153 /* error using [MS-DLX] server, retry using Active Directory */
1154 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
1155 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1157 payload->destroy = g_free;
1158 payload->data = mdd->other;
1159 mdd->other = NULL;
1161 sip_soap_directory_search(sipe_private,
1163 query,
1164 process_get_info_response,
1165 payload);
1167 ms_dlx_free(mdd);
1168 g_free(query);
1171 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
1172 const gchar *who)
1174 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1176 if (sipe_private->dlx_uri) {
1177 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1179 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1180 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(who));
1182 mdd->other = g_strdup(who);
1183 mdd->max_returns = 1;
1184 mdd->callback = get_info_ab_entry_response;
1185 mdd->failed_callback = get_info_ab_entry_failed;
1186 mdd->session = sipe_svc_session_start();
1188 ms_dlx_webticket_request(sipe_private, mdd);
1190 } else {
1191 /* no [MS-DLX] server, use Active Directory search instead */
1192 gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
1193 "msRTCSIP-PrimaryUserAddress",
1194 who);
1195 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1197 SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
1198 row ? row : "");
1200 payload->destroy = g_free;
1201 payload->data = g_strdup(who);
1203 sip_soap_directory_search(sipe_private,
1205 row,
1206 process_get_info_response,
1207 payload);
1208 g_free(row);
1212 static void photo_response_data_free(struct photo_response_data *data)
1214 g_free(data->who);
1215 g_free(data->photo_hash);
1216 if (data->conn) {
1217 http_conn_free(data->conn);
1219 g_free(data);
1222 static void process_buddy_photo_response(int return_code, const char *body,
1223 GSList *headers, SIPE_UNUSED_PARAMETER HttpConn *conn, void *data)
1225 struct photo_response_data *rdata = (struct photo_response_data *)data;
1226 struct sipe_core_private *sipe_private = rdata->sipe_private;
1228 if (return_code == 200) {
1229 const gchar *len_str = sipe_utils_nameval_find(headers, "Content-Length");
1230 if (len_str) {
1231 gsize photo_size = atoi(len_str);
1232 gpointer photo = g_new(char, photo_size);
1234 if (photo) {
1235 memcpy(photo, body, photo_size);
1237 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC,
1238 rdata->who,
1239 photo,
1240 photo_size,
1241 rdata->photo_hash);
1246 sipe_private->pending_photo_requests =
1247 g_slist_remove(sipe_private->pending_photo_requests, rdata);
1248 photo_response_data_free(rdata);
1251 static gchar *create_x_ms_webticket_header(const gchar *wsse_security)
1253 gchar *assertion = sipe_xml_extract_raw(wsse_security, "saml:Assertion", TRUE);
1254 gchar *wsse_security_base64;
1255 gchar *x_ms_webticket_header;
1257 if (!assertion) {
1258 return NULL;
1261 wsse_security_base64 = g_base64_encode((const guchar *)assertion,
1262 strlen(assertion));
1263 x_ms_webticket_header = g_strdup_printf("X-MS-WebTicket: opaque=%s\r\n",
1264 wsse_security_base64);
1266 g_free(assertion);
1267 g_free(wsse_security_base64);
1269 return x_ms_webticket_header;
1272 static void get_photo_ab_entry_response(struct sipe_core_private *sipe_private,
1273 const gchar *uri,
1274 SIPE_UNUSED_PARAMETER const gchar *raw,
1275 sipe_xml *soap_body,
1276 gpointer callback_data)
1278 struct ms_dlx_data *mdd = callback_data;
1279 gchar *photo_rel_path = NULL;
1280 gchar *photo_hash = NULL;
1281 const gchar *photo_hash_old =
1282 sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, mdd->other);
1284 if (soap_body) {
1285 const sipe_xml *node;
1287 SIPE_DEBUG_INFO("get_photo_ab_entry_response: received valid SOAP message from service %s",
1288 uri);
1290 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
1291 node;
1292 node = sipe_xml_twin(node)) {
1293 gchar *name = sipe_xml_data(sipe_xml_child(node, "Name"));
1294 gchar *value = sipe_xml_data(sipe_xml_child(node, "Value"));
1296 if (!is_empty(value)) {
1297 if (sipe_strcase_equal(name, "PhotoRelPath")) {
1298 g_free(photo_rel_path);
1299 photo_rel_path = value;
1300 value = NULL;
1301 } else if (sipe_strcase_equal(name, "PhotoHash")) {
1302 g_free(photo_hash);
1303 photo_hash = value;
1304 value = NULL;
1308 g_free(value);
1309 g_free(name);
1313 if (sipe_private->addressbook_uri && photo_rel_path &&
1314 photo_hash && !sipe_strequal(photo_hash, photo_hash_old)) {
1315 gchar *photo_url = g_strdup_printf("%s/%s",
1316 sipe_private->addressbook_uri, photo_rel_path);
1317 gchar *x_ms_webticket_header = create_x_ms_webticket_header(mdd->wsse_security);
1319 struct photo_response_data *data = g_new(struct photo_response_data, 1);
1320 data->sipe_private = sipe_private;
1321 data->who = g_strdup(mdd->other);
1322 data->photo_hash = photo_hash;
1323 photo_hash = NULL;
1325 data->conn = http_conn_create(
1326 SIPE_CORE_PUBLIC,
1327 NULL, /* HttpSession */
1328 HTTP_CONN_GET,
1329 HTTP_CONN_SSL,
1330 HTTP_CONN_NO_REDIRECT,
1331 photo_url,
1332 NULL, /* body */
1333 NULL, /* content-type */
1334 x_ms_webticket_header,
1335 NULL, /* auth */
1336 process_buddy_photo_response,
1337 data);
1339 if (data->conn) {
1340 sipe_private->pending_photo_requests =
1341 g_slist_append(sipe_private->pending_photo_requests, data);
1342 } else {
1343 photo_response_data_free(data);
1346 g_free(x_ms_webticket_header);
1347 g_free(photo_url);
1350 g_free(photo_rel_path);
1351 g_free(photo_hash);
1352 ms_dlx_free(mdd);
1355 static void get_photo_ab_entry_failed(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
1356 struct ms_dlx_data *mdd)
1358 ms_dlx_free(mdd);
1361 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
1362 const gchar *uri)
1364 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
1365 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1367 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1368 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(uri));
1370 mdd->other = g_strdup(uri);
1371 mdd->max_returns = 1;
1372 mdd->callback = get_photo_ab_entry_response;
1373 mdd->failed_callback = get_photo_ab_entry_failed;
1374 mdd->session = sipe_svc_session_start();
1376 ms_dlx_webticket_request(sipe_private, mdd);
1380 static void buddy_refresh_photos_cb(gpointer uri,
1381 SIPE_UNUSED_PARAMETER gpointer value,
1382 gpointer sipe_private)
1384 buddy_fetch_photo(sipe_private, uri);
1387 void sipe_buddy_refresh_photos(struct sipe_core_private *sipe_private)
1389 g_hash_table_foreach(sipe_private->buddies,
1390 buddy_refresh_photos_cb,
1391 sipe_private);
1394 /* Buddy menu callbacks*/
1396 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
1397 const gchar *who)
1399 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1401 /* 2007+ conference */
1402 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1403 sipe_conf_add(sipe_private, who);
1405 /* 2005- multiparty chat */
1406 } else {
1407 gchar *self = sip_uri_self(sipe_private);
1408 struct sip_session *session;
1410 session = sipe_session_add_chat(sipe_private,
1411 NULL,
1412 TRUE,
1413 self);
1414 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1415 session->chat_session,
1416 session->chat_session->title,
1417 self);
1418 g_free(self);
1420 sipe_im_invite(sipe_private, session, who,
1421 NULL, NULL, NULL, FALSE);
1425 void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public,
1426 const gchar *who)
1428 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1429 who,
1430 NULL);
1431 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1432 buddy,
1433 SIPE_BUDDY_INFO_EMAIL);
1435 if (email) {
1436 gchar *command_line = g_strdup_printf(
1437 #ifdef _WIN32
1438 "cmd /c start"
1439 #else
1440 "xdg-email"
1441 #endif
1442 " mailto:%s", email);
1443 g_free(email);
1445 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s",
1446 command_line);
1447 g_spawn_command_line_async(command_line, NULL);
1448 g_free(command_line);
1450 } else {
1451 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s",
1452 who);
1456 /* Buddy menu */
1458 static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public,
1459 struct sipe_backend_buddy_menu *menu,
1460 sipe_backend_buddy buddy,
1461 sipe_buddy_info_fields id_phone,
1462 sipe_buddy_info_fields id_display,
1463 const gchar *type)
1465 gchar *phone = sipe_backend_buddy_get_string(sipe_public,
1466 buddy,
1467 id_phone);
1468 if (phone) {
1469 gchar *display = sipe_backend_buddy_get_string(sipe_public,
1470 buddy,
1471 id_display);
1472 gchar *tmp = NULL;
1473 gchar *label = g_strdup_printf("%s %s",
1474 type,
1475 display ? display :
1476 (tmp = sip_tel_uri_denormalize(phone)));
1477 menu = sipe_backend_buddy_menu_add(sipe_public,
1478 menu,
1479 label,
1480 SIPE_BUDDY_MENU_MAKE_CALL,
1481 phone);
1482 g_free(tmp);
1483 g_free(label);
1484 g_free(display);
1485 g_free(phone);
1488 return(menu);
1491 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
1492 const gchar *buddy_name,
1493 struct sipe_backend_buddy_menu *menu)
1495 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1496 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1497 buddy_name,
1498 NULL);
1499 gchar *self = sip_uri_self(sipe_private);
1501 SIPE_SESSION_FOREACH {
1502 if (!sipe_strcase_equal(self, buddy_name) && session->chat_session)
1504 struct sipe_chat_session *chat_session = session->chat_session;
1505 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1507 if (sipe_backend_chat_find(chat_session->backend, buddy_name))
1509 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1511 if (is_conf &&
1512 /* Not conf OP */
1513 !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) &&
1514 /* We are a conf OP */
1515 conf_op) {
1516 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1517 chat_session->title);
1518 menu = sipe_backend_buddy_menu_add(sipe_public,
1519 menu,
1520 label,
1521 SIPE_BUDDY_MENU_MAKE_CHAT_LEADER,
1522 chat_session);
1523 g_free(label);
1526 if (is_conf &&
1527 /* We are a conf OP */
1528 conf_op) {
1529 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1530 chat_session->title);
1531 menu = sipe_backend_buddy_menu_add(sipe_public,
1532 menu,
1533 label,
1534 SIPE_BUDDY_MENU_REMOVE_FROM_CHAT,
1535 chat_session);
1536 g_free(label);
1539 else
1541 if (!is_conf ||
1542 (is_conf && !session->locked)) {
1543 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1544 chat_session->title);
1545 menu = sipe_backend_buddy_menu_add(sipe_public,
1546 menu,
1547 label,
1548 SIPE_BUDDY_MENU_INVITE_TO_CHAT,
1549 chat_session);
1550 g_free(label);
1554 } SIPE_SESSION_FOREACH_END;
1555 g_free(self);
1557 menu = sipe_backend_buddy_menu_add(sipe_public,
1558 menu,
1559 _("New chat"),
1560 SIPE_BUDDY_MENU_NEW_CHAT,
1561 NULL);
1563 /* add buddy's phone numbers if we have call control */
1564 if (sip_csta_is_idle(sipe_private)) {
1566 /* work phone */
1567 menu = buddy_menu_phone(sipe_public,
1568 menu,
1569 buddy,
1570 SIPE_BUDDY_INFO_WORK_PHONE,
1571 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY,
1572 _("Work"));
1573 /* mobile phone */
1574 menu = buddy_menu_phone(sipe_public,
1575 menu,
1576 buddy,
1577 SIPE_BUDDY_INFO_MOBILE_PHONE,
1578 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY,
1579 _("Mobile"));
1581 /* home phone */
1582 menu = buddy_menu_phone(sipe_public,
1583 menu,
1584 buddy,
1585 SIPE_BUDDY_INFO_HOME_PHONE,
1586 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY,
1587 _("Home"));
1589 /* other phone */
1590 menu = buddy_menu_phone(sipe_public,
1591 menu,
1592 buddy,
1593 SIPE_BUDDY_INFO_OTHER_PHONE,
1594 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY,
1595 _("Other"));
1597 /* custom1 phone */
1598 menu = buddy_menu_phone(sipe_public,
1599 menu,
1600 buddy,
1601 SIPE_BUDDY_INFO_CUSTOM1_PHONE,
1602 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY,
1603 _("Custom1"));
1607 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1608 buddy,
1609 SIPE_BUDDY_INFO_EMAIL);
1610 if (email) {
1611 menu = sipe_backend_buddy_menu_add(sipe_public,
1612 menu,
1613 _("Send email..."),
1614 SIPE_BUDDY_MENU_SEND_EMAIL,
1615 NULL);
1616 g_free(email);
1620 /* access level control */
1621 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1622 menu = sipe_backend_buddy_sub_menu_add(sipe_public,
1623 menu,
1624 _("Access level"),
1625 sipe_ocs2007_access_control_menu(sipe_private,
1626 buddy_name));
1628 return(menu);
1632 Local Variables:
1633 mode: c
1634 c-file-style: "bsd"
1635 indent-tabs-mode: t
1636 tab-width: 8
1637 End: