notify: store ucPC2PCAVEncryption value form server
[siplcs.git] / src / core / sipe-notify.c
blob2d0630aea59f71174bfa55098ebb227ac3965f6d
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Process incoming SIP NOTIFY/BENOTIFY messages
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include "sipmsg.h"
38 #include "sip-csta.h"
39 #include "sip-soap.h"
40 #include "sipe-backend.h"
41 #include "sipe-buddy.h"
42 #include "sipe-cal.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-groupchat.h"
48 #include "sipe-media.h"
49 #include "sipe-mime.h"
50 #include "sipe-nls.h"
51 #include "sipe-notify.h"
52 #include "sipe-ocs2005.h"
53 #include "sipe-ocs2007.h"
54 #include "sipe-status.h"
55 #include "sipe-subscriptions.h"
56 #include "sipe-ucs.h"
57 #include "sipe-utils.h"
58 #include "sipe-xml.h"
60 /* OCS2005 */
61 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
62 struct sipmsg *msg)
64 sipe_xml *xn_provision;
65 const sipe_xml *node;
67 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
68 if ((node = sipe_xml_child(xn_provision, "user"))) {
69 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
70 if ((node = sipe_xml_child(node, "line"))) {
71 const gchar *line_uri = sipe_xml_attribute(node, "uri");
72 const gchar *server = sipe_xml_attribute(node, "server");
73 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
74 sip_csta_open(sipe_private, line_uri, server);
77 sipe_xml_free(xn_provision);
80 /* OCS2007+ */
81 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
82 struct sipmsg *msg)
84 sipe_xml *xn_provision_group_list;
85 const sipe_xml *node;
87 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
89 /* provisionGroup */
90 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup");
91 node;
92 node = sipe_xml_twin(node)) {
93 const gchar *node_name = sipe_xml_attribute(node, "name");
95 /* ServerConfiguration */
96 if (sipe_strequal("ServerConfiguration", node_name)) {
97 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
98 "dlxExternalUrl" : "dlxInternalUrl";
99 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
100 "absExternalServerUrl" : "absInternalServerUrl";
101 gchar *ucPC2PCAVEncryption = NULL;
103 g_free(sipe_private->focus_factory_uri);
104 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
105 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
106 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
108 g_free(sipe_private->dlx_uri);
109 sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
110 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s",
111 sipe_private->dlx_uri ? sipe_private->dlx_uri : "");
113 g_free(sipe_private->addressbook_uri);
114 sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str));
115 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s",
116 sipe_private->addressbook_uri ? sipe_private->addressbook_uri : "");
118 #ifdef HAVE_VV
119 g_free(sipe_private->test_call_bot_uri);
120 sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall"));
121 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s",
122 sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : "");
124 g_free(sipe_private->mras_uri);
125 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
126 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
127 sipe_private->mras_uri ? sipe_private->mras_uri : "");
129 if (sipe_private->mras_uri)
130 sipe_media_get_av_edge_credentials(sipe_private);
131 #endif
133 ucPC2PCAVEncryption = g_strstrip(sipe_xml_data(sipe_xml_child(node, "ucPC2PCAVEncryption")));
134 if (sipe_strequal(ucPC2PCAVEncryption, "SupportEncryption")) {
135 sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_OPTIONAL;
136 } else if (sipe_strequal(ucPC2PCAVEncryption, "DoNotSupportEncryption")) {
137 sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REJECTED;
138 } else {
139 // "RequireEncryption" or any unknown value.
140 sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REQUIRED;
142 g_free(ucPC2PCAVEncryption);
144 /* persistentChatConfiguration */
145 } else if (sipe_strequal("persistentChatConfiguration", node_name)) {
146 const sipe_xml *property;
147 gboolean enabled = FALSE;
148 gchar *uri = NULL;
150 for (property = sipe_xml_child(node, "propertyEntryList/property");
151 property;
152 property = sipe_xml_twin(property)) {
153 const gchar *name = sipe_xml_attribute(property, "name");
154 gchar *value = sipe_xml_data(property);
156 if (sipe_strequal(name, "EnablePersistentChat")) {
157 enabled = sipe_strequal(value, "true");
159 } else if (sipe_strequal(name, "DefaultPersistentChatPoolUri")) {
160 g_free(uri);
161 uri = value;
162 value = NULL;
164 g_free(value);
167 if (enabled) {
168 g_free(sipe_private->persistentChatPool_uri);
169 sipe_private->persistentChatPool_uri = g_strdup(sipe_get_no_sip_uri(uri));
170 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->persistentChatPool_uri=%s",
171 sipe_private->persistentChatPool_uri ? sipe_private->persistentChatPool_uri : "");
173 g_free(uri);
177 sipe_xml_free(xn_provision_group_list);
179 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
180 /* Some buddies might have been added before we received this
181 * provisioning notify with DLX and addressbook URIs. Now we can
182 * trigger an update of their photos. */
183 sipe_buddy_refresh_photos(sipe_private);
186 if (sipe_private->focus_factory_uri) {
187 /* Fill the list of conferencing capabilities enabled on
188 * the server. */
189 sipe_conf_get_capabilities(sipe_private);
192 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
193 /* persistentChatPool_uri has been set at this point */
194 sipe_groupchat_init(sipe_private);
197 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
198 const gchar *data, unsigned len)
200 sipe_xml *xn_list;
201 const sipe_xml *xn_resource;
202 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
203 g_free, NULL);
205 xn_list = sipe_xml_parse(data, len);
207 for (xn_resource = sipe_xml_child(xn_list, "resource");
208 xn_resource;
209 xn_resource = sipe_xml_twin(xn_resource) )
211 const char *uri, *state;
212 const sipe_xml *xn_instance;
214 xn_instance = sipe_xml_child(xn_resource, "instance");
215 if (!xn_instance) continue;
217 uri = sipe_xml_attribute(xn_resource, "uri");
218 state = sipe_xml_attribute(xn_instance, "state");
219 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
221 if (strstr(state, "resubscribe")) {
222 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
224 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
225 gchar *user = g_strdup(uri);
226 gchar *host = g_strdup(poolFqdn);
227 GSList *server = g_hash_table_lookup(servers,
228 host);
229 server = g_slist_append(server, user);
230 g_hash_table_insert(servers, host, server);
231 } else {
232 sipe_subscribe_presence_single(sipe_private,
233 uri,
234 uri);
239 /* Send out any deferred poolFqdn subscriptions */
240 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
241 g_hash_table_destroy(servers);
243 sipe_xml_free(xn_list);
247 * Update user phone
248 * Suitable for both 2005 and 2007 systems.
250 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
251 * @param phone_type
252 * @param phone may be modified to strip white space
253 * @param phone_display_string may be modified to strip white space
255 static void
256 sipe_update_user_phone(struct sipe_core_private *sipe_private,
257 const gchar *uri,
258 const gchar *phone_type,
259 gchar *phone,
260 gchar *phone_display_string)
262 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
263 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
265 if(!phone || strlen(phone) == 0) return;
267 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
268 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
269 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
270 } else if (sipe_strequal(phone_type, "home")) {
271 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
272 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
273 } else if (sipe_strequal(phone_type, "other")) {
274 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
275 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
276 } else if (sipe_strequal(phone_type, "custom1")) {
277 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
278 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
281 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
282 if (phone_display_string) {
283 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
287 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
288 const gchar *data,
289 unsigned len)
291 char *activity = NULL;
292 const char *epid;
293 const char *status_id = NULL;
294 const char *name;
295 char *uri;
296 char *self_uri = sip_uri_self(sipe_private);
297 int avl;
298 int act;
299 const char *device_name = NULL;
300 const char *cal_start_time = NULL;
301 const char *cal_granularity = NULL;
302 char *cal_free_busy_base64 = NULL;
303 struct sipe_buddy *sbuddy;
304 const sipe_xml *node;
305 sipe_xml *xn_presentity;
306 const sipe_xml *xn_availability;
307 const sipe_xml *xn_activity;
308 const sipe_xml *xn_display_name;
309 const sipe_xml *xn_email;
310 const sipe_xml *xn_phone_number;
311 const sipe_xml *xn_userinfo;
312 const sipe_xml *xn_note;
313 const sipe_xml *xn_oof;
314 const sipe_xml *xn_state;
315 const sipe_xml *xn_contact;
316 char *note;
317 int user_avail;
318 const char *user_avail_nil;
319 int res_avail;
320 time_t user_avail_since = 0;
321 time_t activity_since = 0;
323 /* fix for Reuters environment on Linux */
324 if (data && strstr(data, "encoding=\"utf-16\"")) {
325 char *tmp_data;
326 tmp_data = sipe_utils_str_replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
327 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
328 g_free(tmp_data);
329 } else {
330 xn_presentity = sipe_xml_parse(data, len);
333 xn_availability = sipe_xml_child(xn_presentity, "availability");
334 xn_activity = sipe_xml_child(xn_presentity, "activity");
335 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
336 xn_email = sipe_xml_child(xn_presentity, "email");
337 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
338 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
339 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
340 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
341 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
342 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
343 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
344 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
345 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
346 note = xn_note ? sipe_xml_data(xn_note) : NULL;
348 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
349 user_avail = 0;
350 user_avail_since = 0;
353 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
354 uri = sip_uri_from_name(name);
355 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
356 epid = sipe_xml_attribute(xn_availability, "epid");
357 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
359 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
360 activity = g_strdup(sipe_ocs2005_activity_description(act));
361 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
362 if (user_avail > res_avail) {
363 res_avail = user_avail;
364 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
367 if (xn_display_name) {
368 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
369 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
370 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
371 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
372 char *tel_uri = sip_to_tel_uri(phone_number);
374 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
375 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
376 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
377 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
379 g_free(tel_uri);
380 g_free(phone_label);
381 g_free(phone_number);
382 g_free(email);
383 g_free(display_name);
386 if (xn_contact) {
387 /* tel */
388 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
390 /* Ex.: <tel type="work">tel:+3222220000</tel> */
391 const char *phone_type = sipe_xml_attribute(node, "type");
392 char* phone = sipe_xml_data(node);
394 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
396 g_free(phone);
400 if (xn_display_name || xn_contact)
401 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
403 /* devicePresence */
404 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
405 const sipe_xml *xn_device_name;
406 const sipe_xml *xn_calendar_info;
407 const sipe_xml *xn_state;
408 char *state;
410 /* deviceName */
411 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
412 xn_device_name = sipe_xml_child(node, "deviceName");
413 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
416 /* calendarInfo */
417 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
418 if (xn_calendar_info) {
419 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
421 if (cal_start_time) {
422 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
423 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
425 if (cal_start_time_t_tmp > cal_start_time_t) {
426 cal_start_time = cal_start_time_tmp;
427 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
428 g_free(cal_free_busy_base64);
429 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
431 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time, cal_granularity, cal_free_busy_base64);
433 } else {
434 cal_start_time = cal_start_time_tmp;
435 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
436 g_free(cal_free_busy_base64);
437 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
439 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time, cal_granularity, cal_free_busy_base64);
443 /* state */
444 xn_state = sipe_xml_child(node, "states/state");
445 if (xn_state) {
446 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
447 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
449 state = sipe_xml_data(xn_state);
450 if (dev_avail_since > user_avail_since &&
451 dev_avail >= res_avail)
453 const gchar *new_desc;
454 res_avail = dev_avail;
455 if (!is_empty(state)) {
456 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
457 g_free(activity);
458 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
459 } else if (sipe_strequal(state, "presenting")) {
460 g_free(activity);
461 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
462 } else {
463 activity = state;
464 state = NULL;
466 activity_since = dev_avail_since;
468 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
469 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
470 if (new_desc) {
471 g_free(activity);
472 activity = g_strdup(new_desc);
475 g_free(state);
479 /* oof */
480 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
481 g_free(activity);
482 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
483 activity_since = 0;
486 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
487 if (sbuddy)
489 g_free(sbuddy->activity);
490 sbuddy->activity = activity;
491 activity = NULL;
493 sbuddy->activity_since = activity_since;
495 sbuddy->user_avail = user_avail;
496 sbuddy->user_avail_since = user_avail_since;
498 g_free(sbuddy->note);
499 sbuddy->note = NULL;
500 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
502 sbuddy->is_oof_note = (xn_oof != NULL);
504 g_free(sbuddy->device_name);
505 sbuddy->device_name = NULL;
506 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
508 if (!is_empty(cal_free_busy_base64)) {
509 g_free(sbuddy->cal_start_time);
510 sbuddy->cal_start_time = g_strdup(cal_start_time);
512 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
514 g_free(sbuddy->cal_free_busy_base64);
515 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
516 cal_free_busy_base64 = NULL;
518 g_free(sbuddy->cal_free_busy);
519 sbuddy->cal_free_busy = NULL;
522 sbuddy->last_non_cal_status_id = status_id;
523 g_free(sbuddy->last_non_cal_activity);
524 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
526 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
527 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
529 if (sbuddy->is_oof_note)
530 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
531 else
532 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
534 g_free(sipe_private->note);
535 sipe_private->note = g_strdup(sbuddy->note);
537 sipe_private->note_since = time(NULL);
540 sipe_status_set_token(sipe_private,
541 sbuddy->last_non_cal_status_id);
544 g_free(cal_free_busy_base64);
545 g_free(activity);
547 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
548 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
549 sipe_status_token_to_activity(status_id));
551 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
552 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
555 g_free(note);
556 sipe_xml_free(xn_presentity);
557 g_free(uri);
558 g_free(self_uri);
561 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
562 const gchar *data,
563 unsigned len)
565 const char *uri;
566 struct sipe_buddy *sbuddy = NULL;
567 sipe_xml *xn_categories;
568 const sipe_xml *xn_category;
569 const char *status = NULL;
570 gboolean do_update_status = FALSE;
571 gboolean has_note_cleaned = FALSE;
572 gboolean has_free_busy_cleaned = FALSE;
574 xn_categories = sipe_xml_parse(data, len);
575 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
576 if (uri) {
577 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
580 if (!sbuddy) {
581 /* Got presence of a buddy not in our contact list, ignore. */
582 sipe_xml_free(xn_categories);
583 return;
586 for (xn_category = sipe_xml_child(xn_categories, "category");
587 xn_category ;
588 xn_category = sipe_xml_twin(xn_category) )
590 const sipe_xml *xn_node;
591 const char *tmp;
592 const char *attrVar = sipe_xml_attribute(xn_category, "name");
593 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
594 sipe_utils_str_to_time(tmp) : 0;
596 /* contactCard */
597 if (sipe_strequal(attrVar, "contactCard"))
599 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
601 if (card) {
602 const sipe_xml *node;
603 /* identity - Display Name and email */
604 node = sipe_xml_child(card, "identity");
605 if (node) {
606 char* display_name = sipe_xml_data(
607 sipe_xml_child(node, "name/displayName"));
608 char* email = sipe_xml_data(
609 sipe_xml_child(node, "email"));
611 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
612 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
614 g_free(display_name);
615 g_free(email);
617 /* company */
618 node = sipe_xml_child(card, "company");
619 if (node) {
620 char* company = sipe_xml_data(node);
621 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
622 g_free(company);
624 /* department */
625 node = sipe_xml_child(card, "department");
626 if (node) {
627 char* department = sipe_xml_data(node);
628 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
629 g_free(department);
631 /* title */
632 node = sipe_xml_child(card, "title");
633 if (node) {
634 char* title = sipe_xml_data(node);
635 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
636 g_free(title);
638 /* office */
639 node = sipe_xml_child(card, "office");
640 if (node) {
641 char* office = sipe_xml_data(node);
642 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
643 g_free(office);
645 /* site (url) */
646 node = sipe_xml_child(card, "url");
647 if (node) {
648 char* site = sipe_xml_data(node);
649 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
650 g_free(site);
652 /* phone */
653 for (node = sipe_xml_child(card, "phone");
654 node;
655 node = sipe_xml_twin(node))
657 const char *phone_type = sipe_xml_attribute(node, "type");
658 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
659 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
661 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
663 g_free(phone);
664 g_free(phone_display_string);
666 /* address */
667 for (node = sipe_xml_child(card, "address");
668 node;
669 node = sipe_xml_twin(node))
671 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
672 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
673 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
674 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
675 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
676 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
678 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
679 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
680 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
681 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
682 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
684 g_free(street);
685 g_free(city);
686 g_free(state);
687 g_free(zipcode);
688 g_free(country_code);
690 break;
693 /* photo */
694 for (node = sipe_xml_child(card, "photo");
695 node;
696 node = sipe_xml_twin(node)) {
697 gchar *photo_url = sipe_xml_data(sipe_xml_child(node, "uri"));
698 gchar *hash = sipe_xml_data(sipe_xml_child(node, "hash"));
699 gboolean found = FALSE;
701 if (!is_empty(uri) && !is_empty(hash)) {
702 sipe_buddy_update_photo(sipe_private,
703 uri,
704 hash,
705 photo_url,
706 NULL);
707 found = TRUE;
710 g_free(hash);
711 g_free(photo_url);
713 if (found)
714 break;
718 /* note */
719 else if (sipe_strequal(attrVar, "note"))
721 if (!has_note_cleaned) {
722 has_note_cleaned = TRUE;
724 g_free(sbuddy->note);
725 sbuddy->note = NULL;
726 sbuddy->is_oof_note = FALSE;
727 sbuddy->note_since = publish_time;
729 do_update_status = TRUE;
731 if (publish_time >= sbuddy->note_since) {
732 /* clean up in case no 'note' element is supplied
733 * which indicate note removal in client
735 g_free(sbuddy->note);
736 sbuddy->note = NULL;
737 sbuddy->is_oof_note = FALSE;
738 sbuddy->note_since = publish_time;
740 xn_node = sipe_xml_child(xn_category, "note/body");
741 if (xn_node) {
742 char *tmp;
743 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
744 g_free(tmp);
745 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
746 sbuddy->note_since = publish_time;
748 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
749 uri, sbuddy->note ? sbuddy->note : "");
751 /* to trigger UI refresh in case no status info is supplied in this update */
752 do_update_status = TRUE;
755 /* state */
756 else if(sipe_strequal(attrVar, "state"))
758 char *tmp;
759 int availability;
760 const sipe_xml *xn_availability;
761 const sipe_xml *xn_activity;
762 const sipe_xml *xn_device;
763 const sipe_xml *xn_meeting_subject;
764 const sipe_xml *xn_meeting_location;
765 const gchar *legacy_activity;
767 xn_node = sipe_xml_child(xn_category, "state");
768 if (!xn_node) continue;
769 xn_availability = sipe_xml_child(xn_node, "availability");
770 if (!xn_availability) continue;
771 xn_activity = sipe_xml_child(xn_node, "activity");
772 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
773 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
775 tmp = sipe_xml_data(xn_availability);
776 availability = atoi(tmp);
777 g_free(tmp);
779 sbuddy->is_mobile = FALSE;
780 xn_device = sipe_xml_child(xn_node, "device");
781 if (xn_device) {
782 tmp = sipe_xml_data(xn_device);
783 sbuddy->is_mobile = !g_ascii_strcasecmp(tmp, "Mobile");
784 g_free(tmp);
787 /* activity */
788 g_free(sbuddy->activity);
789 sbuddy->activity = NULL;
790 if (xn_activity) {
791 const char *token = sipe_xml_attribute(xn_activity, "token");
792 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
794 /* from token */
795 if (!is_empty(token)) {
796 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
798 /* from custom element */
799 if (xn_custom) {
800 char *custom = sipe_xml_data(xn_custom);
802 if (!is_empty(custom)) {
803 g_free(sbuddy->activity);
804 sbuddy->activity = custom;
805 custom = NULL;
807 g_free(custom);
810 /* meeting_subject */
811 g_free(sbuddy->meeting_subject);
812 sbuddy->meeting_subject = NULL;
813 if (xn_meeting_subject) {
814 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
816 if (!is_empty(meeting_subject)) {
817 sbuddy->meeting_subject = meeting_subject;
818 meeting_subject = NULL;
820 g_free(meeting_subject);
822 /* meeting_location */
823 g_free(sbuddy->meeting_location);
824 sbuddy->meeting_location = NULL;
825 if (xn_meeting_location) {
826 char *meeting_location = sipe_xml_data(xn_meeting_location);
828 if (!is_empty(meeting_location)) {
829 sbuddy->meeting_location = meeting_location;
830 meeting_location = NULL;
832 g_free(meeting_location);
835 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
836 legacy_activity = sipe_ocs2007_legacy_activity_description(availability);
837 if (sbuddy->activity && legacy_activity) {
838 gchar *tmp2 = sbuddy->activity;
840 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity);
841 g_free(tmp2);
842 } else if (legacy_activity) {
843 sbuddy->activity = g_strdup(legacy_activity);
846 do_update_status = TRUE;
848 /* calendarData */
849 else if(sipe_strequal(attrVar, "calendarData"))
851 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
852 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
854 if (xn_free_busy) {
855 if (!has_free_busy_cleaned) {
856 has_free_busy_cleaned = TRUE;
858 g_free(sbuddy->cal_start_time);
859 sbuddy->cal_start_time = NULL;
861 g_free(sbuddy->cal_free_busy_base64);
862 sbuddy->cal_free_busy_base64 = NULL;
864 g_free(sbuddy->cal_free_busy);
865 sbuddy->cal_free_busy = NULL;
867 sbuddy->cal_free_busy_published = publish_time;
870 if (publish_time >= sbuddy->cal_free_busy_published) {
871 g_free(sbuddy->cal_start_time);
872 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
874 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
875 15 : 0;
877 g_free(sbuddy->cal_free_busy_base64);
878 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
880 g_free(sbuddy->cal_free_busy);
881 sbuddy->cal_free_busy = NULL;
883 sbuddy->cal_free_busy_published = publish_time;
885 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: startTime=%s granularity=%d cal_free_busy_base64=\n%s", sbuddy->cal_start_time, sbuddy->cal_granularity, sbuddy->cal_free_busy_base64);
889 if (xn_working_hours) {
890 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
895 if (do_update_status) {
896 guint activity;
898 if (status) {
899 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
900 activity = sipe_status_token_to_activity(status);
901 } else {
902 /* no status category in this update,
903 using contact's current status */
904 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
905 uri);
908 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity);
911 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
913 sipe_xml_free(xn_categories);
916 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
917 const gchar *uri,
918 const gchar *activity,
919 gboolean is_online)
921 if (is_online) {
922 const gchar *status_id = NULL;
923 if (activity) {
924 if (sipe_strequal(activity,
925 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
926 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
927 } else if (sipe_strequal(activity,
928 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
929 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
933 if (!status_id) {
934 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
937 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
938 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
939 sipe_status_token_to_activity(status_id));
940 } else {
941 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
942 SIPE_ACTIVITY_OFFLINE);
946 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
947 const gchar *data,
948 unsigned len)
950 gchar *uri;
951 gchar *getbasic;
952 gchar *activity = NULL;
953 sipe_xml *pidf;
954 const sipe_xml *basicstatus = NULL, *tuple, *status;
955 gboolean isonline = FALSE;
956 const sipe_xml *display_name_node;
958 pidf = sipe_xml_parse(data, len);
959 if (!pidf) {
960 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
961 return;
964 if ((tuple = sipe_xml_child(pidf, "tuple")))
966 if ((status = sipe_xml_child(tuple, "status"))) {
967 basicstatus = sipe_xml_child(status, "basic");
971 if (!basicstatus) {
972 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
973 sipe_xml_free(pidf);
974 return;
977 getbasic = sipe_xml_data(basicstatus);
978 if (!getbasic) {
979 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
980 sipe_xml_free(pidf);
981 return;
984 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
985 if (strstr(getbasic, "open")) {
986 isonline = TRUE;
988 g_free(getbasic);
990 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
992 display_name_node = sipe_xml_child(pidf, "display-name");
993 if (display_name_node) {
994 char * display_name = sipe_xml_data(display_name_node);
996 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
997 g_free(display_name);
999 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
1002 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
1003 if ((status = sipe_xml_child(tuple, "status"))) {
1004 if ((basicstatus = sipe_xml_child(status, "activities"))) {
1005 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
1006 activity = sipe_xml_data(basicstatus);
1007 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
1013 sipe_buddy_status_from_activity(sipe_private,
1014 uri,
1015 activity,
1016 isonline);
1018 g_free(activity);
1019 g_free(uri);
1020 sipe_xml_free(pidf);
1023 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
1024 const GSList *fields,
1025 const gchar *body,
1026 gsize length)
1028 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
1030 if (strstr(type,"application/rlmi+xml")) {
1031 process_incoming_notify_rlmi_resub(user_data, body, length);
1032 } else if (strstr(type, "text/xml+msrtc.pidf")) {
1033 process_incoming_notify_msrtc(user_data, body, length);
1034 } else {
1035 process_incoming_notify_rlmi(user_data, body, length);
1039 static void sipe_process_presence(struct sipe_core_private *sipe_private,
1040 struct sipmsg *msg)
1042 const char *ctype = sipmsg_find_header(msg, "Content-Type");
1044 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
1046 if (ctype &&
1047 (strstr(ctype, "application/rlmi+xml") ||
1048 strstr(ctype, "application/msrtc-event-categories+xml")))
1050 if (strstr(ctype, "multipart"))
1052 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
1054 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
1056 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
1058 else if(strstr(ctype, "application/rlmi+xml"))
1060 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
1063 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
1065 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
1067 else
1069 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
1074 * Fires on deregistration event initiated by server.
1075 * [MS-SIPREGE] SIP extension.
1077 * OCS2007 Example
1079 * Content-Type: text/registration-event
1080 * subscription-state: terminated;expires=0
1081 * ms-diagnostics-public: 4141;reason="User disabled"
1083 * deregistered;event=rejected
1085 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
1086 struct sipmsg *msg)
1088 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1089 gchar *event = NULL;
1090 gchar *reason = NULL;
1091 gchar *warning;
1093 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1095 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1096 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1097 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1098 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1099 } else {
1100 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1101 return;
1104 reason = sipmsg_get_ms_diagnostics_reason(msg);
1105 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1106 if (!reason) { // for LCS2005
1107 if (event && sipe_strcase_equal(event, "unregistered")) {
1108 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1109 reason = g_strdup(_("you are already signed in at another location"));
1110 } else if (event && sipe_strcase_equal(event, "rejected")) {
1111 reason = g_strdup(_("user disabled")); // [MS-OCER]
1112 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1113 reason = g_strdup(_("user moved")); // [MS-OCER]
1116 g_free(event);
1117 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1118 g_free(reason);
1120 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1121 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1122 warning);
1123 g_free(warning);
1127 /* Replace "~" with localized version of "Other Contacts" */
1128 static const gchar *get_group_name(const sipe_xml *node)
1130 const gchar *name = sipe_xml_attribute(node, "name");
1131 return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name);
1134 static void add_new_group(struct sipe_core_private *sipe_private,
1135 const sipe_xml *node)
1137 sipe_group_add(sipe_private,
1138 get_group_name(node),
1139 NULL,
1140 NULL,
1141 sipe_xml_int_attribute(node, "id", 0));
1144 static void add_new_buddy(struct sipe_core_private *sipe_private,
1145 const sipe_xml *node,
1146 const gchar *uri)
1148 const gchar *name = sipe_xml_attribute(node, "name");
1149 struct sipe_buddy *buddy = NULL;
1150 gchar *tmp;
1151 gchar **item_groups;
1152 int i = 0;
1154 /* assign to group Other Contacts if nothing else received */
1155 tmp = g_strdup(sipe_xml_attribute(node, "groups"));
1156 if (is_empty(tmp)) {
1157 struct sipe_group *group = sipe_group_find_by_name(sipe_private,
1158 _("Other Contacts"));
1159 g_free(tmp);
1160 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1162 item_groups = g_strsplit(tmp, " ", 0);
1163 g_free(tmp);
1165 while (item_groups[i]) {
1166 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1167 g_ascii_strtod(item_groups[i],
1168 NULL));
1170 /* If couldn't find the right group for this contact, */
1171 /* then just put it in the first group we have */
1172 if (!group)
1173 group = sipe_group_first(sipe_private);
1175 if (group) {
1176 if (!buddy)
1177 buddy = sipe_buddy_add(sipe_private,
1178 uri,
1179 NULL,
1180 NULL);
1182 sipe_buddy_add_to_group(sipe_private,
1183 buddy,
1184 group,
1185 name);
1186 } else {
1187 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1188 name);
1191 i++;
1194 g_strfreev(item_groups);
1197 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1198 struct sipmsg *msg)
1200 int len = msg->bodylen;
1202 const gchar *tmp = sipmsg_find_header(msg, "Event");
1203 const sipe_xml *item;
1204 sipe_xml *isc;
1205 guint delta;
1206 const sipe_xml *group_node;
1208 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1209 return FALSE;
1212 /* Convert the contact from XML to backend Buddies */
1213 isc = sipe_xml_parse(msg->body, len);
1214 if (!isc) {
1215 return FALSE;
1218 /* [MS-SIP]: deltaNum MUST be non-zero */
1219 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1220 if (delta) {
1221 sipe_private->deltanum_contacts = delta;
1225 * Process whole buddy list
1227 * - Only sent once
1228 * * up to Lync 2010
1229 * * Lync 2013 (and later) with buddy list not migrated
1231 * - Lync 2013 with buddy list migrated to Unified Contact Store (UCS)
1232 * * Notify piggy-backed on SUBSCRIBE response with empty list
1233 * * NOTIFY send by server with standard list (ignored by us)
1235 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1236 const gchar *ucsmode = sipe_xml_attribute(isc, "ucsmode");
1238 SIPE_CORE_PRIVATE_FLAG_UNSET(LYNC2013);
1239 if (ucsmode) {
1240 gboolean migrated = sipe_strcase_equal(ucsmode,
1241 "migrated");
1242 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013);
1243 SIPE_DEBUG_INFO_NOFORMAT("contact list contains 'ucsmode' attribute (indicates Lync 2013+)");
1245 if (migrated)
1246 SIPE_DEBUG_INFO_NOFORMAT("contact list has been migrated to Unified Contact Store (UCS)");
1247 sipe_ucs_init(sipe_private, migrated);
1250 if (!sipe_ucs_is_migrated(sipe_private)) {
1251 /* Start processing contact list */
1252 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1254 /* Parse groups */
1255 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node))
1256 add_new_group(sipe_private, group_node);
1258 /* Make sure we have at least one group */
1259 if (sipe_group_count(sipe_private) == 0) {
1260 sipe_group_create(sipe_private,
1261 NULL,
1262 _("Other Contacts"),
1263 NULL);
1266 /* Parse contacts */
1267 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1268 const gchar *name = sipe_xml_attribute(item, "uri");
1269 gchar *uri = sip_uri_from_name(name);
1270 add_new_buddy(sipe_private, item, uri);
1271 g_free(uri);
1274 sipe_buddy_cleanup_local_list(sipe_private);
1276 /* Add self-contact if not there yet. 2005 systems. */
1277 /* This will resemble subscription to roaming_self in 2007 systems */
1278 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1279 gchar *self_uri = sip_uri_self(sipe_private);
1280 sipe_buddy_add(sipe_private,
1281 self_uri,
1282 NULL,
1283 NULL);
1284 g_free(self_uri);
1287 /* Finished processing contact list */
1288 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1291 /* Process buddy list updates */
1292 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1294 /* Process new groups */
1295 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1296 add_new_group(sipe_private, group_node);
1298 /* Process modified groups */
1299 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1300 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1301 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1302 NULL));
1303 if (group) {
1304 const gchar *name = get_group_name(group_node);
1306 if (!(is_empty(name) ||
1307 sipe_strequal(group->name, name)) &&
1308 sipe_group_rename(sipe_private,
1309 group,
1310 name))
1311 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1315 /* Process new buddies */
1316 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1317 add_new_buddy(sipe_private,
1318 item,
1319 sipe_xml_attribute(item, "uri"));
1322 /* Process modified buddies */
1323 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1324 const gchar *uri = sipe_xml_attribute(item, "uri");
1325 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1326 uri);
1328 if (buddy) {
1329 gchar **item_groups = g_strsplit(sipe_xml_attribute(item,
1330 "groups"),
1331 " ", 0);
1333 /* this should be defined. Otherwise we would get "deletedContact" */
1334 if (item_groups) {
1335 const gchar *name = sipe_xml_attribute(item, "name");
1336 gboolean empty_name = is_empty(name);
1337 GSList *found = NULL;
1338 int i = 0;
1340 while (item_groups[i]) {
1341 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1342 g_ascii_strtod(item_groups[i],
1343 NULL));
1344 /* ignore unkown groups */
1345 if (group) {
1346 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1347 uri,
1348 group->name);
1350 /* add group to found list */
1351 found = g_slist_prepend(found, group);
1353 if (b) {
1354 /* new alias? */
1355 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1358 if (!(empty_name ||
1359 sipe_strequal(b_alias, name))) {
1360 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1362 name);
1363 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1364 uri, group->name, b_alias, name);
1366 g_free(b_alias);
1368 } else {
1369 const gchar *alias = empty_name ? uri : name;
1370 /* buddy was not in this group */
1371 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1372 uri,
1373 alias,
1374 group->name);
1375 sipe_buddy_insert_group(buddy, group);
1376 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1377 uri, alias, group->name);
1381 /* next group */
1382 i++;
1384 g_strfreev(item_groups);
1386 /* removed from groups? */
1387 sipe_buddy_update_groups(sipe_private,
1388 buddy,
1389 found);
1390 g_slist_free(found);
1395 /* Process deleted buddies */
1396 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1397 const gchar *uri = sipe_xml_attribute(item, "uri");
1398 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1399 uri);
1401 if (buddy) {
1402 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1403 sipe_buddy_remove(sipe_private, buddy);
1407 /* Process deleted groups
1409 * NOTE: all buddies will already have been removed from the
1410 * group prior to this. The log shows that OCS actually
1411 * sends two separate updates when you delete a group:
1413 * - first one with "modifiedContact" removing buddies
1414 * from the group, leaving it empty, and
1416 * - then one with "deletedGroup" removing the group
1418 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1419 sipe_group_remove(sipe_private,
1420 sipe_group_find_by_id(sipe_private,
1421 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1422 NULL)));
1425 sipe_xml_free(isc);
1427 /* Subscribe to buddies, if contact list not migrated to UCS */
1428 if (!sipe_ucs_is_migrated(sipe_private))
1429 sipe_subscribe_presence_initial(sipe_private);
1431 /* for 2005 systems schedule contacts' status update
1432 * based on their calendar information
1434 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1435 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1438 return 0;
1441 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1442 struct sipmsg *msg)
1444 guint delta;
1445 sipe_xml *xml;
1447 xml = sipe_xml_parse(msg->body, msg->bodylen);
1448 if (!xml)
1449 return;
1451 /* [MS-SIP]: deltaNum MUST be non-zero */
1452 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1453 if (delta) {
1454 sipe_private->deltanum_acl = delta;
1457 sipe_xml_free(xml);
1460 struct sipe_auth_job {
1461 gchar *who;
1462 struct sipe_core_private *sipe_private;
1465 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1466 const gchar* who,
1467 gboolean allow)
1469 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1471 if (allow) {
1472 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1473 } else {
1474 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1477 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1478 sipe_ocs2007_change_access_level(sipe_private,
1479 (allow ? -1 : 32000),
1480 "user",
1481 sipe_get_no_sip_uri(who));
1482 } else {
1483 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1488 static void sipe_auth_user_cb(gpointer data)
1490 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1491 if (!job) return;
1493 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1494 job->who,
1495 TRUE);
1496 g_free(job);
1499 static void sipe_deny_user_cb(gpointer data)
1501 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1502 if (!job) return;
1504 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1505 job->who,
1506 FALSE);
1507 g_free(job);
1510 /* OCS2005- */
1511 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1512 struct sipmsg * msg)
1514 sipe_xml *watchers;
1515 const sipe_xml *watcher;
1516 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1517 if (msg->response != 0 && msg->response != 200) return;
1519 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1521 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1522 if (!watchers) return;
1524 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1525 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1526 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1527 gboolean on_list = sipe_buddy_find_by_uri(sipe_private, remote_user) != NULL;
1529 // TODO pull out optional displayName to pass as alias
1530 if (remote_user) {
1531 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1532 job->who = remote_user;
1533 job->sipe_private = sipe_private;
1534 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1535 remote_user,
1536 alias,
1537 on_list,
1538 sipe_auth_user_cb,
1539 sipe_deny_user_cb,
1540 (gpointer)job);
1545 sipe_xml_free(watchers);
1546 return;
1550 * Dispatcher for all incoming subscription information
1551 * whether it comes from NOTIFY, BENOTIFY requests or
1552 * piggy-backed to subscription's OK responce.
1554 void process_incoming_notify(struct sipe_core_private *sipe_private,
1555 struct sipmsg *msg)
1557 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1558 const gchar *event = sipmsg_find_header(msg, "Event");
1559 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1561 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1563 /* implicit subscriptions */
1564 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1565 sipe_process_imdn(sipe_private, msg);
1567 /* event subscriptions */
1568 } else if (event) {
1570 /* One-off subscriptions - sent with "Expires: 0" */
1571 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) {
1572 sipe_process_provisioning_v2(sipe_private, msg);
1573 } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) {
1574 sipe_process_provisioning(sipe_private, msg);
1575 } else if (sipe_strcase_equal(event, "presence")) {
1576 sipe_process_presence(sipe_private, msg);
1577 } else if (sipe_strcase_equal(event, "registration-notify")) {
1578 sipe_process_registration_notify(sipe_private, msg);
1580 /* Subscriptions with timeout */
1581 } else if (!subscription_state || strstr(subscription_state, "active")) {
1582 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
1583 sipe_process_roaming_contacts(sipe_private, msg);
1584 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) {
1585 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1586 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) {
1587 sipe_process_roaming_acl(sipe_private, msg);
1588 } else if (sipe_strcase_equal(event, "presence.wpending")) {
1589 sipe_process_presence_wpending(sipe_private, msg);
1590 } else if (sipe_strcase_equal(event, "conference")) {
1591 sipe_process_conference(sipe_private, msg);
1598 Local Variables:
1599 mode: c
1600 c-file-style: "bsd"
1601 indent-tabs-mode: t
1602 tab-width: 8
1603 End: