buddy: make pending_photo_requests static
[siplcs.git] / src / core / sipe-buddy.c
blob46dfe40eec32bc37991cc0fee4340b08ac162c1c
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;
68 * Intentionally not creating relation between photo request and sipe_buddy, in
69 * future we might want to download our acount's photo (not having a sipe_buddy)
70 * or get photos of people not in our buddy list (e.g. during contact search).
72 * Any pending photo requests to addressbook server are freed when account
73 * disconnects.
75 static GSList *pending_photo_requests = NULL;
77 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
78 const gchar *uri);
79 static void photo_response_data_free(struct photo_response_data *data);
81 struct sipe_buddy *sipe_buddy_add(struct sipe_core_private *sipe_private,
82 const gchar *uri)
84 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies, uri);
85 if (!buddy) {
86 buddy = g_new0(struct sipe_buddy, 1);
87 buddy->name = g_strdup(uri);
88 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
90 SIPE_DEBUG_INFO("sipe_buddy_add: Added buddy %s", uri);
92 buddy_fetch_photo(sipe_private, uri);
93 } else {
94 SIPE_DEBUG_INFO("sipe_buddy_add: Buddy %s already exists", uri);
97 return buddy;
100 static void buddy_free(struct sipe_buddy *buddy)
102 #ifndef _WIN32
104 * We are calling g_hash_table_foreach_steal(). That means that no
105 * key/value deallocation functions are called. Therefore the glib
106 * hash code does not touch the key (buddy->name) or value (buddy)
107 * of the to-be-deleted hash node at all. It follows that we
109 * - MUST free the memory for the key ourselves and
110 * - ARE allowed to do it in this function
112 * Conclusion: glib must be broken on the Windows platform if sipe
113 * crashes with SIGTRAP when closing. You'll have to live
114 * with the memory leak until this is fixed.
116 g_free(buddy->name);
117 #endif
118 g_free(buddy->activity);
119 g_free(buddy->meeting_subject);
120 g_free(buddy->meeting_location);
121 g_free(buddy->note);
123 g_free(buddy->cal_start_time);
124 g_free(buddy->cal_free_busy_base64);
125 g_free(buddy->cal_free_busy);
126 g_free(buddy->last_non_cal_activity);
128 sipe_cal_free_working_hours(buddy->cal_working_hours);
130 g_free(buddy->device_name);
131 g_slist_free(buddy->groups);
132 g_free(buddy);
135 static gboolean buddy_free_cb(SIPE_UNUSED_PARAMETER gpointer key,
136 gpointer buddy,
137 SIPE_UNUSED_PARAMETER gpointer user_data)
139 buddy_free(buddy);
140 /* We must return TRUE as the key/value have already been deleted */
141 return(TRUE);
144 void sipe_buddy_free_all(struct sipe_core_private *sipe_private)
146 GSList *list = pending_photo_requests;
148 g_hash_table_foreach_steal(sipe_private->buddies,
149 buddy_free_cb,
150 NULL);
152 /* core is being deallocated, remove all its pending photo requests */
153 while (list) {
154 struct photo_response_data *data = list->data;
155 list = g_slist_next(list);
156 if (data->sipe_private == sipe_private) {
157 pending_photo_requests =
158 g_slist_remove(pending_photo_requests, data);
159 photo_response_data_free(data);
164 gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public,
165 const gchar *uri,
166 guint activity,
167 const gchar *status_text)
169 struct sipe_buddy *sbuddy;
170 const char *activity_str;
172 if (!sipe_public) return NULL; /* happens on pidgin exit */
174 sbuddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, uri);
175 if (!sbuddy) return NULL;
177 activity_str = sbuddy->activity ? sbuddy->activity :
178 (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
179 status_text : NULL;
181 if (activity_str && sbuddy->note) {
182 return g_strdup_printf("%s - <i>%s</i>", activity_str, sbuddy->note);
183 } else if (activity_str) {
184 return g_strdup(activity_str);
185 } else if (sbuddy->note) {
186 return g_strdup_printf("<i>%s</i>", sbuddy->note);
187 } else {
188 return NULL;
192 gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private,
193 const gchar *with)
195 sipe_backend_buddy pbuddy;
196 gchar *alias = NULL;
197 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
198 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
200 return alias;
203 void sipe_core_buddy_group(struct sipe_core_public *sipe_public,
204 const gchar *who,
205 const gchar *old_group_name,
206 const gchar *new_group_name)
208 struct sipe_buddy * buddy = g_hash_table_lookup(SIPE_CORE_PRIVATE->buddies, who);
209 struct sipe_group * old_group = NULL;
210 struct sipe_group * new_group;
212 SIPE_DEBUG_INFO("sipe_core_buddy_group: who:%s old_group_name:%s new_group_name:%s",
213 who ? who : "", old_group_name ? old_group_name : "", new_group_name ? new_group_name : "");
215 if(!buddy) { // buddy not in roaming list
216 return;
219 if (old_group_name) {
220 old_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, old_group_name);
222 new_group = sipe_group_find_by_name(SIPE_CORE_PRIVATE, new_group_name);
224 if (old_group) {
225 buddy->groups = g_slist_remove(buddy->groups, old_group);
226 SIPE_DEBUG_INFO("sipe_core_buddy_group: buddy %s removed from old group %s", who, old_group_name);
229 if (!new_group) {
230 sipe_group_create(SIPE_CORE_PRIVATE, new_group_name, who);
231 } else {
232 buddy->groups = slist_insert_unique_sorted(buddy->groups, new_group, (GCompareFunc)sipe_group_compare);
233 sipe_core_group_set_user(sipe_public, who);
237 void sipe_core_buddy_add(struct sipe_core_public *sipe_public,
238 const gchar *uri,
239 const gchar *group_name)
241 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
243 if (!g_hash_table_lookup(sipe_private->buddies, uri)) {
244 struct sipe_buddy *b = sipe_buddy_add(sipe_private, uri);
245 b->just_added = TRUE;
247 /* @TODO should go to callback */
248 sipe_subscribe_presence_single(sipe_private, b->name);
250 } else {
251 SIPE_DEBUG_INFO("sipe_core_buddy_add: buddy %s already in internal list",
252 uri);
255 sipe_core_buddy_group(sipe_public,
256 uri,
257 NULL,
258 group_name);
262 * Unassociates buddy from group first.
263 * Then see if no groups left, removes buddy completely.
264 * Otherwise updates buddy groups on server.
266 void sipe_core_buddy_remove(struct sipe_core_public *sipe_public,
267 const gchar *uri,
268 const gchar *group_name)
270 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
271 struct sipe_buddy *b = g_hash_table_lookup(sipe_private->buddies,
272 uri);
274 if (!b) return;
276 if (group_name) {
277 struct sipe_group *g = sipe_group_find_by_name(sipe_private,
278 group_name);
279 if (g) {
280 b->groups = g_slist_remove(b->groups, g);
281 SIPE_DEBUG_INFO("sipe_core_buddy_remove: buddy %s removed from group %s",
282 uri, g->name);
286 if (g_slist_length(b->groups) < 1) {
287 gchar *action_name = sipe_utils_presence_key(uri);
288 sipe_schedule_cancel(sipe_private, action_name);
289 g_free(action_name);
291 g_hash_table_remove(sipe_private->buddies, uri);
293 if (b->name) {
294 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
295 b->name);
296 sip_soap_request(sipe_private,
297 "deleteContact",
298 request);
299 g_free(request);
302 buddy_free(b);
303 } else {
304 /* updates groups on server */
305 sipe_core_group_set_user(sipe_public, b->name);
310 void sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
311 const gchar *uri,
312 guint activity)
314 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
315 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies,
316 uri);
318 if (!sbuddy) return;
320 /* Check if on 2005 system contact's calendar,
321 * then set/preserve it.
323 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
324 sipe_backend_buddy_set_status(sipe_public, uri, activity);
325 } else {
326 sipe_ocs2005_apply_calendar_status(sipe_private,
327 sbuddy,
328 sipe_status_activity_to_token(activity));
332 void sipe_core_buddy_tooltip_info(struct sipe_core_public *sipe_public,
333 const gchar *uri,
334 const gchar *status_name,
335 gboolean is_online,
336 struct sipe_backend_buddy_tooltip *tooltip)
338 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
339 gchar *note = NULL;
340 gboolean is_oof_note = FALSE;
341 const gchar *activity = NULL;
342 gchar *calendar = NULL;
343 const gchar *meeting_subject = NULL;
344 const gchar *meeting_location = NULL;
345 gchar *access_text = NULL;
347 #define SIPE_ADD_BUDDY_INFO(l, t) \
349 gchar *tmp = g_markup_escape_text((t), -1); \
350 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), tmp); \
351 g_free(tmp); \
353 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) \
354 sipe_backend_buddy_tooltip_add(sipe_public, tooltip, (l), (t))
356 if (sipe_public) { /* happens on pidgin exit */
357 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
358 if (sbuddy) {
359 note = sbuddy->note;
360 is_oof_note = sbuddy->is_oof_note;
361 activity = sbuddy->activity;
362 calendar = sipe_cal_get_description(sbuddy);
363 meeting_subject = sbuddy->meeting_subject;
364 meeting_location = sbuddy->meeting_location;
366 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
367 gboolean is_group_access = FALSE;
368 const int container_id = sipe_ocs2007_find_access_level(sipe_private,
369 "user",
370 sipe_get_no_sip_uri(uri),
371 &is_group_access);
372 const char *access_level = sipe_ocs2007_access_level_name(container_id);
373 access_text = is_group_access ?
374 g_strdup(access_level) :
375 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT,
376 access_level);
380 if (is_online) {
381 const gchar *status_str = activity ? activity : status_name;
383 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
385 if (is_online && !is_empty(calendar)) {
386 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
388 g_free(calendar);
389 if (!is_empty(meeting_location)) {
390 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", uri, meeting_location);
391 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
393 if (!is_empty(meeting_subject)) {
394 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", uri, meeting_subject);
395 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
397 if (note) {
398 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", uri, note);
399 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
400 g_strdup_printf("<i>%s</i>", note));
402 if (access_text) {
403 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
404 g_free(access_text);
408 void sipe_buddy_update_property(struct sipe_core_private *sipe_private,
409 const char *uri,
410 sipe_buddy_info_fields propkey,
411 char *property_value)
413 GSList *buddies, *entry;
415 if (property_value)
416 property_value = g_strstrip(property_value);
418 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
419 while (entry) {
420 gchar *prop_str;
421 sipe_backend_buddy p_buddy = entry->data;
423 /* for Display Name */
424 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
425 gchar *alias;
426 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
427 if (property_value && sipe_is_bad_alias(uri, alias)) {
428 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
429 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
431 g_free(alias);
433 alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
434 if (!is_empty(property_value) &&
435 (!sipe_strequal(property_value, alias) || is_empty(alias)) )
437 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
438 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
440 g_free(alias);
442 /* for other properties */
443 else {
444 if (!is_empty(property_value)) {
445 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
446 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
447 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
449 g_free(prop_str);
453 entry = entry->next;
455 g_slist_free(buddies);
459 struct ms_dlx_data;
460 struct ms_dlx_data {
461 GSList *search_rows;
462 gchar *other;
463 guint max_returns;
464 sipe_svc_callback *callback;
465 struct sipe_svc_session *session;
466 gchar *wsse_security;
467 /* must call ms_dlx_free() */
468 void (*failed_callback)(struct sipe_core_private *sipe_private,
469 struct ms_dlx_data *mdd);
472 static void ms_dlx_free(struct ms_dlx_data *mdd)
474 GSList *entry = mdd->search_rows;
475 while (entry) {
476 g_free(entry->data);
477 entry = entry->next;
479 g_slist_free(mdd->search_rows);
480 sipe_svc_session_close(mdd->session);
481 g_free(mdd->other);
482 g_free(mdd->wsse_security);
483 g_free(mdd);
486 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
487 #define DLX_SEARCH_ITEM \
488 "<AbEntryRequest.ChangeSearchQuery>" \
489 " <SearchOn>%s</SearchOn>" \
490 " <Value>%s</Value>" \
491 "</AbEntryRequest.ChangeSearchQuery>"
493 static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) {
494 gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1);
495 guint i = 0;
496 gchar *query = NULL;
498 while (query_rows) {
499 gchar *attr;
500 gchar *value;
502 attr = query_rows->data;
503 query_rows = g_slist_next(query_rows);
504 value = query_rows->data;
505 query_rows = g_slist_next(query_rows);
507 if (!attr || !value)
508 break;
510 attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW,
511 attr, value);
513 attrs[i] = NULL;
515 if (i) {
516 query = g_strjoinv(NULL, attrs);
517 SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s",
518 query ? query : "");
521 g_strfreev(attrs);
523 return query;
526 static void ms_dlx_webticket(struct sipe_core_private *sipe_private,
527 const gchar *base_uri,
528 const gchar *auth_uri,
529 const gchar *wsse_security,
530 gpointer callback_data)
532 struct ms_dlx_data *mdd = callback_data;
534 if (wsse_security) {
535 gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
537 SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
538 base_uri);
540 if (sipe_svc_ab_entry_request(sipe_private,
541 mdd->session,
542 auth_uri,
543 wsse_security,
544 query,
545 g_slist_length(mdd->search_rows) / 2,
546 mdd->max_returns,
547 mdd->callback,
548 mdd)) {
550 /* keep webticket security token for potential further use */
551 mdd->wsse_security = g_strdup(wsse_security);
553 /* callback data passed down the line */
554 mdd = NULL;
556 g_free(query);
558 } else {
559 /* no ticket: this will show the minmum information */
560 SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
561 base_uri);
564 if (mdd)
565 mdd->failed_callback(sipe_private, mdd);
568 static void ms_dlx_webticket_request(struct sipe_core_private *sipe_private,
569 struct ms_dlx_data *mdd)
571 if (!sipe_webticket_request(sipe_private,
572 mdd->session,
573 sipe_private->dlx_uri,
574 "AddressBookWebTicketBearer",
575 ms_dlx_webticket,
576 mdd)) {
577 SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
578 sipe_private->dlx_uri);
579 mdd->failed_callback(sipe_private, mdd);
583 static void search_contacts_finalize(struct sipe_core_private *sipe_private,
584 struct sipe_backend_search_results *results,
585 guint match_count,
586 gboolean more)
588 gchar *secondary = g_strdup_printf(
589 dngettext(PACKAGE_NAME,
590 "Found %d contact%s:",
591 "Found %d contacts%s:", match_count),
592 match_count, more ? _(" (more matched your query)") : "");
594 sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
595 results,
596 secondary,
597 more);
598 g_free(secondary);
601 static void search_ab_entry_response(struct sipe_core_private *sipe_private,
602 const gchar *uri,
603 SIPE_UNUSED_PARAMETER const gchar *raw,
604 sipe_xml *soap_body,
605 gpointer callback_data)
607 struct ms_dlx_data *mdd = callback_data;
609 if (soap_body) {
610 const sipe_xml *node;
611 struct sipe_backend_search_results *results;
612 GHashTable *found;
614 SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
615 uri);
617 /* any matches? */
618 node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
619 if (!node) {
620 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
621 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
622 _("No contacts found"),
623 NULL);
624 ms_dlx_free(mdd);
625 return;
628 /* OK, we found something - show the results to the user */
629 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
630 if (!results) {
631 SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
632 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
633 _("Unable to display the search results"),
634 NULL);
635 ms_dlx_free(mdd);
636 return;
639 /* SearchAbEntryResult can contain duplicates */
640 found = g_hash_table_new_full(g_str_hash, g_str_equal,
641 g_free, NULL);
643 for (/* initialized above */ ; node; node = sipe_xml_twin(node)) {
644 const sipe_xml *attrs;
645 gchar *sip_uri = NULL;
646 gchar *displayname = NULL;
647 gchar *company = NULL;
648 gchar *country = NULL;
649 gchar *email = NULL;
651 for (attrs = sipe_xml_child(node, "Attributes/Attribute");
652 attrs;
653 attrs = sipe_xml_twin(attrs)) {
654 gchar *name = sipe_xml_data(sipe_xml_child(attrs,
655 "Name"));
656 gchar *value = sipe_xml_data(sipe_xml_child(attrs,
657 "Value"));
659 if (!is_empty(value)) {
660 if (sipe_strcase_equal(name, "msrtcsip-primaryuseraddress")) {
661 g_free(sip_uri);
662 sip_uri = value;
663 value = NULL;
664 } else if (sipe_strcase_equal(name, "displayname")) {
665 g_free(displayname);
666 displayname = value;
667 value = NULL;
668 } else if (sipe_strcase_equal(name, "mail")) {
669 g_free(email);
670 email = value;
671 value = NULL;
672 } else if (sipe_strcase_equal(name, "company")) {
673 g_free(company);
674 company = value;
675 value = NULL;
676 } else if (sipe_strcase_equal(name, "country")) {
677 g_free(country);
678 country = value;
679 value = NULL;
683 g_free(value);
684 g_free(name);
687 if (sip_uri && !g_hash_table_lookup(found, sip_uri)) {
688 gchar **uri_parts = g_strsplit(sip_uri, ":", 2);
689 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
690 results,
691 uri_parts[1],
692 displayname,
693 company,
694 country,
695 email);
696 g_strfreev(uri_parts);
698 g_hash_table_insert(found, sip_uri, (gpointer) TRUE);
699 sip_uri = NULL;
702 g_free(email);
703 g_free(country);
704 g_free(company);
705 g_free(displayname);
706 g_free(sip_uri);
709 search_contacts_finalize(sipe_private, results,
710 g_hash_table_size(found),
711 FALSE);
712 g_hash_table_destroy(found);
713 ms_dlx_free(mdd);
715 } else {
716 mdd->failed_callback(sipe_private, mdd);
720 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
721 struct sipmsg *msg,
722 SIPE_UNUSED_PARAMETER struct transaction *trans)
724 struct sipe_backend_search_results *results;
725 sipe_xml *searchResults;
726 const sipe_xml *mrow;
727 guint match_count = 0;
728 gboolean more = FALSE;
730 /* valid response? */
731 if (msg->response != 200) {
732 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
733 msg->response);
734 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
735 _("Contact search failed"),
736 NULL);
737 return(FALSE);
740 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
742 /* valid XML? */
743 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
744 if (!searchResults) {
745 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
746 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
747 _("Contact search failed"),
748 NULL);
749 return(FALSE);
752 /* any matches? */
753 mrow = sipe_xml_child(searchResults, "Body/Array/row");
754 if (!mrow) {
755 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
756 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
757 _("No contacts found"),
758 NULL);
760 sipe_xml_free(searchResults);
761 return(FALSE);
764 /* OK, we found something - show the results to the user */
765 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC);
766 if (!results) {
767 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
768 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
769 _("Unable to display the search results"),
770 NULL);
772 sipe_xml_free(searchResults);
773 return FALSE;
776 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
777 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
778 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
779 results,
780 uri_parts[1],
781 sipe_xml_attribute(mrow, "displayName"),
782 sipe_xml_attribute(mrow, "company"),
783 sipe_xml_attribute(mrow, "country"),
784 sipe_xml_attribute(mrow, "email"));
785 g_strfreev(uri_parts);
786 match_count++;
789 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
790 char *data = sipe_xml_data(mrow);
791 more = (g_ascii_strcasecmp(data, "true") == 0);
792 g_free(data);
795 search_contacts_finalize(sipe_private, results, match_count, more);
796 sipe_xml_free(searchResults);
798 return(TRUE);
801 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
802 struct ms_dlx_data *mdd)
804 /* error using [MS-DLX] server, retry using Active Directory */
805 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
807 sip_soap_directory_search(sipe_private,
808 100,
809 query,
810 process_search_contact_response,
811 NULL);
812 ms_dlx_free(mdd);
813 g_free(query);
816 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
817 const gchar *given_name,
818 const gchar *surname,
819 const gchar *email,
820 const gchar *company,
821 const gchar *country)
823 GSList *query_rows = NULL;
825 #define ADD_QUERY_ROW(attr, val) \
826 if (val) { \
827 query_rows = g_slist_append(query_rows, g_strdup(attr)); \
828 query_rows = g_slist_append(query_rows, g_strdup(val)); \
831 ADD_QUERY_ROW("givenName", given_name);
832 ADD_QUERY_ROW("sn", surname);
833 ADD_QUERY_ROW("mail", email);
834 ADD_QUERY_ROW("company", company);
835 ADD_QUERY_ROW("c", country);
837 if (query_rows) {
838 if (SIPE_CORE_PRIVATE->dlx_uri != NULL) {
839 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
841 mdd->search_rows = query_rows;
842 mdd->max_returns = 100;
843 mdd->callback = search_ab_entry_response;
844 mdd->failed_callback = search_ab_entry_failed;
845 mdd->session = sipe_svc_session_start();
847 ms_dlx_webticket_request(SIPE_CORE_PRIVATE, mdd);
849 } else {
850 gchar *query = prepare_buddy_search_query(query_rows, FALSE);
852 /* no [MS-DLX] server, use Active Directory search instead */
853 sip_soap_directory_search(SIPE_CORE_PRIVATE,
854 100,
855 query,
856 process_search_contact_response,
857 NULL);
858 g_free(query);
859 g_slist_free(query_rows);
864 static void get_info_finalize(struct sipe_core_private *sipe_private,
865 struct sipe_backend_buddy_info *info,
866 const gchar *uri,
867 const gchar *server_alias,
868 const gchar *email)
870 sipe_backend_buddy bbuddy;
871 struct sipe_buddy *sbuddy;
872 gchar *alias;
873 gchar *value;
875 if (!info) {
876 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
877 } else {
878 sipe_backend_buddy_info_break(SIPE_CORE_PUBLIC, info);
880 if (!info)
881 return;
883 bbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
885 if (is_empty(server_alias)) {
886 value = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
887 bbuddy);
888 if (value) {
889 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
890 info,
891 SIPE_BUDDY_INFO_DISPLAY_NAME,
892 value);
894 } else {
895 value = g_strdup(server_alias);
898 /* present alias if it differs from server alias */
899 alias = sipe_backend_buddy_get_local_alias(SIPE_CORE_PUBLIC, bbuddy);
900 if (alias && !sipe_strequal(alias, value))
902 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
903 info,
904 SIPE_BUDDY_INFO_ALIAS,
905 alias);
907 g_free(alias);
908 g_free(value);
910 if (is_empty(email)) {
911 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
912 bbuddy,
913 SIPE_BUDDY_INFO_EMAIL);
914 if (value) {
915 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
916 info,
917 SIPE_BUDDY_INFO_EMAIL,
918 value);
919 g_free(value);
923 value = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
924 bbuddy,
925 SIPE_BUDDY_INFO_SITE);
926 if (value) {
927 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
928 info,
929 SIPE_BUDDY_INFO_SITE,
930 value);
931 g_free(value);
934 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
935 if (sbuddy && sbuddy->device_name) {
936 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
937 info,
938 SIPE_BUDDY_INFO_DEVICE,
939 sbuddy->device_name);
942 sipe_backend_buddy_info_finalize(SIPE_CORE_PUBLIC, info, uri);
946 static void get_info_ab_entry_response(struct sipe_core_private *sipe_private,
947 const gchar *uri,
948 SIPE_UNUSED_PARAMETER const gchar *raw,
949 sipe_xml *soap_body,
950 gpointer callback_data)
952 struct ms_dlx_data *mdd = callback_data;
953 struct sipe_backend_buddy_info *info = NULL;
954 gchar *server_alias = NULL;
955 gchar *email = NULL;
957 if (soap_body) {
958 const sipe_xml *node;
960 SIPE_DEBUG_INFO("get_info_ab_entry_response: received valid SOAP message from service %s",
961 uri);
963 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
965 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
966 node;
967 node = sipe_xml_twin(node)) {
968 gchar *name = sipe_xml_data(sipe_xml_child(node,
969 "Name"));
970 gchar *value = sipe_xml_data(sipe_xml_child(node,
971 "Value"));
972 const sipe_xml *values = sipe_xml_child(node,
973 "Values");
975 /* Single value entries */
976 if (!is_empty(value)) {
978 if (sipe_strcase_equal(name, "displayname")) {
979 g_free(server_alias);
980 server_alias = value;
981 value = NULL;
982 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
983 info,
984 SIPE_BUDDY_INFO_DISPLAY_NAME,
985 server_alias);
986 } else if (sipe_strcase_equal(name, "mail")) {
987 g_free(email);
988 email = value;
989 value = NULL;
990 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
991 info,
992 SIPE_BUDDY_INFO_EMAIL,
993 email);
994 } else if (sipe_strcase_equal(name, "title")) {
995 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
996 info,
997 SIPE_BUDDY_INFO_JOB_TITLE,
998 value);
999 } else if (sipe_strcase_equal(name, "company")) {
1000 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1001 info,
1002 SIPE_BUDDY_INFO_COMPANY,
1003 value);
1004 } else if (sipe_strcase_equal(name, "country")) {
1005 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1006 info,
1007 SIPE_BUDDY_INFO_COUNTRY,
1008 value);
1011 } else if (values) {
1012 gchar *first = sipe_xml_data(sipe_xml_child(values,
1013 "string"));
1015 if (sipe_strcase_equal(name, "telephonenumber")) {
1016 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1017 info,
1018 SIPE_BUDDY_INFO_WORK_PHONE,
1019 first);
1022 g_free(first);
1025 g_free(value);
1026 g_free(name);
1030 /* this will show the minmum information */
1031 get_info_finalize(sipe_private,
1032 info,
1033 mdd->other,
1034 server_alias,
1035 email);
1037 g_free(email);
1038 g_free(server_alias);
1039 ms_dlx_free(mdd);
1042 static gboolean process_get_info_response(struct sipe_core_private *sipe_private,
1043 struct sipmsg *msg,
1044 struct transaction *trans)
1046 const gchar *uri = trans->payload->data;
1047 struct sipe_backend_buddy_info *info = NULL;
1048 gchar *server_alias = NULL;
1049 gchar *email = NULL;
1051 SIPE_DEBUG_INFO("Fetching %s's user info for %s",
1052 uri, sipe_private->username);
1054 if (msg->response != 200) {
1055 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
1056 } else {
1057 sipe_xml *searchResults;
1058 const sipe_xml *mrow;
1060 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s",
1061 msg->body ? msg->body : "");
1063 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1064 if (!searchResults) {
1066 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
1068 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
1069 const gchar *value;
1070 gchar *phone_number;
1072 info = sipe_backend_buddy_info_start(SIPE_CORE_PUBLIC);
1074 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
1075 email = g_strdup(sipe_xml_attribute(mrow, "email"));
1076 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
1079 * For 2007 system we will take this from ContactCard -
1080 * it has cleaner tel: URIs at least
1082 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1083 char *tel_uri = sip_to_tel_uri(phone_number);
1084 /* trims its parameters, so call first */
1085 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1086 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1087 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1088 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1089 g_free(tel_uri);
1092 if (!is_empty(server_alias)) {
1093 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1094 info,
1095 SIPE_BUDDY_INFO_DISPLAY_NAME,
1096 server_alias);
1098 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1099 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1100 info,
1101 SIPE_BUDDY_INFO_JOB_TITLE,
1102 value);
1104 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1105 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1106 info,
1107 SIPE_BUDDY_INFO_OFFICE,
1108 value);
1110 if (!is_empty(phone_number)) {
1111 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1112 info,
1113 SIPE_BUDDY_INFO_WORK_PHONE,
1114 phone_number);
1116 g_free(phone_number);
1117 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1118 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1119 info,
1120 SIPE_BUDDY_INFO_COMPANY,
1121 value);
1123 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1124 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1125 info,
1126 SIPE_BUDDY_INFO_CITY,
1127 value);
1129 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1130 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1131 info,
1132 SIPE_BUDDY_INFO_STATE,
1133 value);
1135 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1136 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1137 info,
1138 SIPE_BUDDY_INFO_COUNTRY,
1139 value);
1141 if (!is_empty(email)) {
1142 sipe_backend_buddy_info_add(SIPE_CORE_PUBLIC,
1143 info,
1144 SIPE_BUDDY_INFO_EMAIL,
1145 email);
1148 sipe_xml_free(searchResults);
1151 /* this will show the minmum information */
1152 get_info_finalize(sipe_private,
1153 info,
1154 uri,
1155 server_alias,
1156 email);
1158 g_free(server_alias);
1159 g_free(email);
1161 return TRUE;
1164 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
1165 struct ms_dlx_data *mdd)
1167 /* error using [MS-DLX] server, retry using Active Directory */
1168 gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
1169 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1171 payload->destroy = g_free;
1172 payload->data = mdd->other;
1173 mdd->other = NULL;
1175 sip_soap_directory_search(sipe_private,
1177 query,
1178 process_get_info_response,
1179 payload);
1181 ms_dlx_free(mdd);
1182 g_free(query);
1185 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
1186 const gchar *who)
1188 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1190 if (sipe_private->dlx_uri) {
1191 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1193 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1194 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(who));
1196 mdd->other = g_strdup(who);
1197 mdd->max_returns = 1;
1198 mdd->callback = get_info_ab_entry_response;
1199 mdd->failed_callback = get_info_ab_entry_failed;
1200 mdd->session = sipe_svc_session_start();
1202 ms_dlx_webticket_request(sipe_private, mdd);
1204 } else {
1205 /* no [MS-DLX] server, use Active Directory search instead */
1206 gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
1207 "msRTCSIP-PrimaryUserAddress",
1208 who);
1209 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1211 SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
1212 row ? row : "");
1214 payload->destroy = g_free;
1215 payload->data = g_strdup(who);
1217 sip_soap_directory_search(sipe_private,
1219 row,
1220 process_get_info_response,
1221 payload);
1222 g_free(row);
1226 static void photo_response_data_free(struct photo_response_data *data)
1228 g_free(data->who);
1229 g_free(data->photo_hash);
1230 if (data->conn) {
1231 http_conn_free(data->conn);
1233 g_free(data);
1236 static void process_buddy_photo_response(int return_code, const char *body,
1237 GSList *headers, SIPE_UNUSED_PARAMETER HttpConn *conn, void *data)
1239 struct photo_response_data *rdata = (struct photo_response_data *)data;
1241 if (return_code == 200) {
1242 const gchar *len_str = sipe_utils_nameval_find(headers, "Content-Length");
1243 if (len_str) {
1244 gsize photo_size = atoi(len_str);
1245 gpointer photo = g_new(char, photo_size);
1246 memcpy(photo, body, photo_size);
1248 sipe_backend_buddy_set_photo(&rdata->sipe_private->public,
1249 rdata->who,
1250 photo,
1251 photo_size,
1252 rdata->photo_hash);
1256 pending_photo_requests = g_slist_remove(pending_photo_requests, rdata);
1257 photo_response_data_free(rdata);
1260 static gchar *create_x_ms_webticket_header(const gchar *wsse_security)
1262 gchar *assertion = sipe_xml_extract_raw(wsse_security, "saml:Assertion", TRUE);
1263 gchar *wsse_security_base64;
1264 gchar *x_ms_webticket_header;
1266 if (!assertion) {
1267 return NULL;
1270 wsse_security_base64 = g_base64_encode((const guchar *)assertion,
1271 strlen(assertion));
1272 x_ms_webticket_header = g_strdup_printf("X-MS-WebTicket: opaque=%s\r\n",
1273 wsse_security_base64);
1275 g_free(assertion);
1276 g_free(wsse_security_base64);
1278 return x_ms_webticket_header;
1281 static void get_photo_ab_entry_response(struct sipe_core_private *sipe_private,
1282 const gchar *uri,
1283 SIPE_UNUSED_PARAMETER const gchar *raw,
1284 sipe_xml *soap_body,
1285 gpointer callback_data)
1287 struct ms_dlx_data *mdd = callback_data;
1288 gchar *photo_rel_path = NULL;
1289 gchar *photo_hash = NULL;
1290 const gchar *photo_hash_old =
1291 sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, mdd->other);
1293 if (soap_body) {
1294 const sipe_xml *node;
1296 SIPE_DEBUG_INFO("get_photo_ab_entry_response: received valid SOAP message from service %s",
1297 uri);
1299 for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
1300 node;
1301 node = sipe_xml_twin(node)) {
1302 gchar *name = sipe_xml_data(sipe_xml_child(node, "Name"));
1303 gchar *value = sipe_xml_data(sipe_xml_child(node, "Value"));
1305 if (!is_empty(value)) {
1306 if (sipe_strcase_equal(name, "PhotoRelPath")) {
1307 g_free(photo_rel_path);
1308 photo_rel_path = value;
1309 value = NULL;
1310 } else if (sipe_strcase_equal(name, "PhotoHash")) {
1311 g_free(photo_hash);
1312 photo_hash = value;
1313 value = NULL;
1317 g_free(value);
1318 g_free(name);
1322 if (sipe_private->addressbook_uri && photo_rel_path &&
1323 photo_hash && !sipe_strequal(photo_hash, photo_hash_old)) {
1324 gchar *photo_url = g_strdup_printf("%s/%s",
1325 sipe_private->addressbook_uri, photo_rel_path);
1326 gchar *x_ms_webticket_header = create_x_ms_webticket_header(mdd->wsse_security);
1328 struct photo_response_data *data = g_new(struct photo_response_data, 1);
1329 data->sipe_private = sipe_private;
1330 data->who = g_strdup(mdd->other);
1331 data->photo_hash = photo_hash;
1332 photo_hash = NULL;
1334 data->conn = http_conn_create(
1335 SIPE_CORE_PUBLIC,
1336 NULL, /* HttpSession */
1337 HTTP_CONN_GET,
1338 HTTP_CONN_SSL,
1339 HTTP_CONN_NO_REDIRECT,
1340 photo_url,
1341 NULL, /* body */
1342 NULL, /* content-type */
1343 x_ms_webticket_header,
1344 NULL, /* auth */
1345 process_buddy_photo_response,
1346 data);
1348 if (data->conn) {
1349 pending_photo_requests = g_slist_append(pending_photo_requests, data);
1350 } else {
1351 photo_response_data_free(data);
1354 g_free(x_ms_webticket_header);
1355 g_free(photo_url);
1358 g_free(photo_rel_path);
1359 g_free(photo_hash);
1360 ms_dlx_free(mdd);
1363 static void get_photo_ab_entry_failed(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
1364 struct ms_dlx_data *mdd)
1366 ms_dlx_free(mdd);
1369 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
1370 const gchar *uri)
1372 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
1373 struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
1375 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
1376 mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(uri));
1378 mdd->other = g_strdup(uri);
1379 mdd->max_returns = 1;
1380 mdd->callback = get_photo_ab_entry_response;
1381 mdd->failed_callback = get_photo_ab_entry_failed;
1382 mdd->session = sipe_svc_session_start();
1384 ms_dlx_webticket_request(sipe_private, mdd);
1388 /* Buddy menu callbacks*/
1390 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
1391 const gchar *who)
1393 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1395 /* 2007+ conference */
1396 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1397 sipe_conf_add(sipe_private, who);
1399 /* 2005- multiparty chat */
1400 } else {
1401 gchar *self = sip_uri_self(sipe_private);
1402 struct sip_session *session;
1404 session = sipe_session_add_chat(sipe_private,
1405 NULL,
1406 TRUE,
1407 self);
1408 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1409 session->chat_session,
1410 session->chat_session->title,
1411 self);
1412 g_free(self);
1414 sipe_im_invite(sipe_private, session, who,
1415 NULL, NULL, NULL, FALSE);
1419 void sipe_core_buddy_send_email(struct sipe_core_public *sipe_public,
1420 const gchar *who)
1422 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1423 who,
1424 NULL);
1425 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1426 buddy,
1427 SIPE_BUDDY_INFO_EMAIL);
1429 if (email) {
1430 gchar *command_line = g_strdup_printf(
1431 #ifdef _WIN32
1432 "cmd /c start"
1433 #else
1434 "xdg-email"
1435 #endif
1436 " mailto:%s", email);
1437 g_free(email);
1439 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: going to call email client: %s",
1440 command_line);
1441 g_spawn_command_line_async(command_line, NULL);
1442 g_free(command_line);
1444 } else {
1445 SIPE_DEBUG_INFO("sipe_core_buddy_send_email: no email address stored for buddy=%s",
1446 who);
1450 /* Buddy menu */
1452 static struct sipe_backend_buddy_menu *buddy_menu_phone(struct sipe_core_public *sipe_public,
1453 struct sipe_backend_buddy_menu *menu,
1454 sipe_backend_buddy buddy,
1455 sipe_buddy_info_fields id_phone,
1456 sipe_buddy_info_fields id_display,
1457 const gchar *type)
1459 gchar *phone = sipe_backend_buddy_get_string(sipe_public,
1460 buddy,
1461 id_phone);
1462 if (phone) {
1463 gchar *display = sipe_backend_buddy_get_string(sipe_public,
1464 buddy,
1465 id_display);
1466 gchar *tmp = NULL;
1467 gchar *label = g_strdup_printf("%s %s",
1468 type,
1469 display ? display :
1470 (tmp = sip_tel_uri_denormalize(phone)));
1471 menu = sipe_backend_buddy_menu_add(sipe_public,
1472 menu,
1473 label,
1474 SIPE_BUDDY_MENU_MAKE_CALL,
1475 phone);
1476 g_free(tmp);
1477 g_free(label);
1478 g_free(display);
1479 g_free(phone);
1482 return(menu);
1485 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
1486 const gchar *buddy_name,
1487 struct sipe_backend_buddy_menu *menu)
1489 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1490 sipe_backend_buddy buddy = sipe_backend_buddy_find(sipe_public,
1491 buddy_name,
1492 NULL);
1493 gchar *self = sip_uri_self(sipe_private);
1495 SIPE_SESSION_FOREACH {
1496 if (!sipe_strcase_equal(self, buddy_name) && session->chat_session)
1498 struct sipe_chat_session *chat_session = session->chat_session;
1499 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1501 if (sipe_backend_chat_find(chat_session->backend, buddy_name))
1503 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1505 if (is_conf &&
1506 /* Not conf OP */
1507 !sipe_backend_chat_is_operator(chat_session->backend, buddy_name) &&
1508 /* We are a conf OP */
1509 conf_op) {
1510 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1511 chat_session->title);
1512 menu = sipe_backend_buddy_menu_add(sipe_public,
1513 menu,
1514 label,
1515 SIPE_BUDDY_MENU_MAKE_CHAT_LEADER,
1516 chat_session);
1517 g_free(label);
1520 if (is_conf &&
1521 /* We are a conf OP */
1522 conf_op) {
1523 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1524 chat_session->title);
1525 menu = sipe_backend_buddy_menu_add(sipe_public,
1526 menu,
1527 label,
1528 SIPE_BUDDY_MENU_REMOVE_FROM_CHAT,
1529 chat_session);
1530 g_free(label);
1533 else
1535 if (!is_conf ||
1536 (is_conf && !session->locked)) {
1537 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1538 chat_session->title);
1539 menu = sipe_backend_buddy_menu_add(sipe_public,
1540 menu,
1541 label,
1542 SIPE_BUDDY_MENU_INVITE_TO_CHAT,
1543 chat_session);
1544 g_free(label);
1548 } SIPE_SESSION_FOREACH_END;
1549 g_free(self);
1551 menu = sipe_backend_buddy_menu_add(sipe_public,
1552 menu,
1553 _("New chat"),
1554 SIPE_BUDDY_MENU_NEW_CHAT,
1555 NULL);
1557 /* add buddy's phone numbers if we have call control */
1558 if (sip_csta_is_idle(sipe_private)) {
1560 /* work phone */
1561 menu = buddy_menu_phone(sipe_public,
1562 menu,
1563 buddy,
1564 SIPE_BUDDY_INFO_WORK_PHONE,
1565 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY,
1566 _("Work"));
1567 /* mobile phone */
1568 menu = buddy_menu_phone(sipe_public,
1569 menu,
1570 buddy,
1571 SIPE_BUDDY_INFO_MOBILE_PHONE,
1572 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY,
1573 _("Mobile"));
1575 /* home phone */
1576 menu = buddy_menu_phone(sipe_public,
1577 menu,
1578 buddy,
1579 SIPE_BUDDY_INFO_HOME_PHONE,
1580 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY,
1581 _("Home"));
1583 /* other phone */
1584 menu = buddy_menu_phone(sipe_public,
1585 menu,
1586 buddy,
1587 SIPE_BUDDY_INFO_OTHER_PHONE,
1588 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY,
1589 _("Other"));
1591 /* custom1 phone */
1592 menu = buddy_menu_phone(sipe_public,
1593 menu,
1594 buddy,
1595 SIPE_BUDDY_INFO_CUSTOM1_PHONE,
1596 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY,
1597 _("Custom1"));
1601 gchar *email = sipe_backend_buddy_get_string(sipe_public,
1602 buddy,
1603 SIPE_BUDDY_INFO_EMAIL);
1604 if (email) {
1605 menu = sipe_backend_buddy_menu_add(sipe_public,
1606 menu,
1607 _("Send email..."),
1608 SIPE_BUDDY_MENU_SEND_EMAIL,
1609 NULL);
1610 g_free(email);
1614 /* access level control */
1615 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1616 menu = sipe_backend_buddy_sub_menu_add(sipe_public,
1617 menu,
1618 _("Access level"),
1619 sipe_ocs2007_access_control_menu(sipe_private,
1620 buddy_name));
1622 return(menu);
1626 Local Variables:
1627 mode: c
1628 c-file-style: "bsd"
1629 indent-tabs-mode: t
1630 tab-width: 8
1631 End: