ucs: initialize & destroy UCS data structure
[siplcs.git] / src / core / sipe-notify.c
blob4f69ef0febcd7f3503d2cf17591e488b421a4ed5
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 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 "sipe-common.h"
38 #include "sipmsg.h"
39 #include "sip-csta.h"
40 #include "sip-soap.h"
41 #include "sipe-backend.h"
42 #include "sipe-buddy.h"
43 #include "sipe-cal.h"
44 #include "sipe-conf.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
47 #include "sipe-group.h"
48 #include "sipe-groupchat.h"
49 #include "sipe-media.h"
50 #include "sipe-mime.h"
51 #include "sipe-nls.h"
52 #include "sipe-notify.h"
53 #include "sipe-ocs2005.h"
54 #include "sipe-ocs2007.h"
55 #include "sipe-schedule.h"
56 #include "sipe-status.h"
57 #include "sipe-subscriptions.h"
58 #include "sipe-ucs.h"
59 #include "sipe-utils.h"
60 #include "sipe-xml.h"
62 /* OCS2005 */
63 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
64 struct sipmsg *msg)
66 sipe_xml *xn_provision;
67 const sipe_xml *node;
69 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
70 if ((node = sipe_xml_child(xn_provision, "user"))) {
71 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
72 if ((node = sipe_xml_child(node, "line"))) {
73 const gchar *line_uri = sipe_xml_attribute(node, "uri");
74 const gchar *server = sipe_xml_attribute(node, "server");
75 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
76 sip_csta_open(sipe_private, line_uri, server);
79 sipe_xml_free(xn_provision);
82 /* OCS2007+ */
83 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
84 struct sipmsg *msg)
86 sipe_xml *xn_provision_group_list;
87 const sipe_xml *node;
89 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
91 /* provisionGroup */
92 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup");
93 node;
94 node = sipe_xml_twin(node)) {
95 const gchar *node_name = sipe_xml_attribute(node, "name");
97 /* ServerConfiguration */
98 if (sipe_strequal("ServerConfiguration", node_name)) {
99 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
100 "dlxExternalUrl" : "dlxInternalUrl";
101 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
102 "absExternalServerUrl" : "absInternalServerUrl";
104 g_free(sipe_private->focus_factory_uri);
105 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
106 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
107 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
109 g_free(sipe_private->dlx_uri);
110 sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
111 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s",
112 sipe_private->dlx_uri ? sipe_private->dlx_uri : "");
114 g_free(sipe_private->addressbook_uri);
115 sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str));
116 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s",
117 sipe_private->addressbook_uri ? sipe_private->addressbook_uri : "");
119 #ifdef HAVE_VV
120 g_free(sipe_private->test_call_bot_uri);
121 sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall"));
122 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s",
123 sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : "");
125 g_free(sipe_private->mras_uri);
126 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
127 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
128 sipe_private->mras_uri ? sipe_private->mras_uri : "");
130 if (sipe_private->mras_uri)
131 sipe_media_get_av_edge_credentials(sipe_private);
132 #endif
134 /* persistentChatConfiguration */
135 } else if (sipe_strequal("persistentChatConfiguration", node_name)) {
136 const sipe_xml *property;
137 gboolean enabled = FALSE;
138 gchar *uri = NULL;
140 for (property = sipe_xml_child(node, "propertyEntryList/property");
141 property;
142 property = sipe_xml_twin(property)) {
143 const gchar *name = sipe_xml_attribute(property, "name");
144 gchar *value = sipe_xml_data(property);
146 if (sipe_strequal(name, "EnablePersistentChat")) {
147 enabled = sipe_strequal(value, "true");
149 } else if (sipe_strequal(name, "DefaultPersistentChatPoolUri")) {
150 g_free(uri);
151 uri = value;
152 value = NULL;
154 g_free(value);
157 if (enabled) {
158 g_free(sipe_private->persistentChatPool_uri);
159 sipe_private->persistentChatPool_uri = g_strdup(sipe_get_no_sip_uri(uri));
160 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->persistentChatPool_uri=%s",
161 sipe_private->persistentChatPool_uri ? sipe_private->persistentChatPool_uri : "");
163 g_free(uri);
167 sipe_xml_free(xn_provision_group_list);
169 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
170 /* Some buddies might have been added before we received this
171 * provisioning notify with DLX and addressbook URIs. Now we can
172 * trigger an update of their photos. */
173 sipe_buddy_refresh_photos(sipe_private);
176 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
177 /* persistentChatPool_uri has been set at this point */
178 sipe_groupchat_init(sipe_private);
181 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
182 const gchar *data, unsigned len)
184 sipe_xml *xn_list;
185 const sipe_xml *xn_resource;
186 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
187 g_free, NULL);
189 xn_list = sipe_xml_parse(data, len);
191 for (xn_resource = sipe_xml_child(xn_list, "resource");
192 xn_resource;
193 xn_resource = sipe_xml_twin(xn_resource) )
195 const char *uri, *state;
196 const sipe_xml *xn_instance;
198 xn_instance = sipe_xml_child(xn_resource, "instance");
199 if (!xn_instance) continue;
201 uri = sipe_xml_attribute(xn_resource, "uri");
202 state = sipe_xml_attribute(xn_instance, "state");
203 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
205 if (strstr(state, "resubscribe")) {
206 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
208 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
209 gchar *user = g_strdup(uri);
210 gchar *host = g_strdup(poolFqdn);
211 GSList *server = g_hash_table_lookup(servers,
212 host);
213 server = g_slist_append(server, user);
214 g_hash_table_insert(servers, host, server);
215 } else {
216 sipe_subscribe_presence_single(sipe_private,
217 uri,
218 uri);
223 /* Send out any deferred poolFqdn subscriptions */
224 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
225 g_hash_table_destroy(servers);
227 sipe_xml_free(xn_list);
231 * Update user phone
232 * Suitable for both 2005 and 2007 systems.
234 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
235 * @param phone_type
236 * @param phone may be modified to strip white space
237 * @param phone_display_string may be modified to strip white space
239 static void
240 sipe_update_user_phone(struct sipe_core_private *sipe_private,
241 const gchar *uri,
242 const gchar *phone_type,
243 gchar *phone,
244 gchar *phone_display_string)
246 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
247 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
249 if(!phone || strlen(phone) == 0) return;
251 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
252 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
253 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
254 } else if (sipe_strequal(phone_type, "home")) {
255 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
256 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
257 } else if (sipe_strequal(phone_type, "other")) {
258 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
259 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
260 } else if (sipe_strequal(phone_type, "custom1")) {
261 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
262 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
265 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
266 if (phone_display_string) {
267 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
271 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
272 const gchar *data,
273 unsigned len)
275 char *activity = NULL;
276 const char *epid;
277 const char *status_id = NULL;
278 const char *name;
279 char *uri;
280 char *self_uri = sip_uri_self(sipe_private);
281 int avl;
282 int act;
283 const char *device_name = NULL;
284 const char *cal_start_time = NULL;
285 const char *cal_granularity = NULL;
286 char *cal_free_busy_base64 = NULL;
287 struct sipe_buddy *sbuddy;
288 const sipe_xml *node;
289 sipe_xml *xn_presentity;
290 const sipe_xml *xn_availability;
291 const sipe_xml *xn_activity;
292 const sipe_xml *xn_display_name;
293 const sipe_xml *xn_email;
294 const sipe_xml *xn_phone_number;
295 const sipe_xml *xn_userinfo;
296 const sipe_xml *xn_note;
297 const sipe_xml *xn_oof;
298 const sipe_xml *xn_state;
299 const sipe_xml *xn_contact;
300 char *note;
301 int user_avail;
302 const char *user_avail_nil;
303 int res_avail;
304 time_t user_avail_since = 0;
305 time_t activity_since = 0;
307 /* fix for Reuters environment on Linux */
308 if (data && strstr(data, "encoding=\"utf-16\"")) {
309 char *tmp_data;
310 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
311 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
312 g_free(tmp_data);
313 } else {
314 xn_presentity = sipe_xml_parse(data, len);
317 xn_availability = sipe_xml_child(xn_presentity, "availability");
318 xn_activity = sipe_xml_child(xn_presentity, "activity");
319 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
320 xn_email = sipe_xml_child(xn_presentity, "email");
321 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
322 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
323 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
324 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
325 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
326 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
327 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
328 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
329 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
330 note = xn_note ? sipe_xml_data(xn_note) : NULL;
332 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
333 user_avail = 0;
334 user_avail_since = 0;
337 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
338 uri = sip_uri_from_name(name);
339 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
340 epid = sipe_xml_attribute(xn_availability, "epid");
341 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
343 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
344 activity = g_strdup(sipe_ocs2005_activity_description(act));
345 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
346 if (user_avail > res_avail) {
347 res_avail = user_avail;
348 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
351 if (xn_display_name) {
352 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
353 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
354 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
355 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
356 char *tel_uri = sip_to_tel_uri(phone_number);
358 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
359 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
360 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
361 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
363 g_free(tel_uri);
364 g_free(phone_label);
365 g_free(phone_number);
366 g_free(email);
367 g_free(display_name);
370 if (xn_contact) {
371 /* tel */
372 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
374 /* Ex.: <tel type="work">tel:+3222220000</tel> */
375 const char *phone_type = sipe_xml_attribute(node, "type");
376 char* phone = sipe_xml_data(node);
378 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
380 g_free(phone);
384 if (xn_display_name || xn_contact)
385 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
387 /* devicePresence */
388 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
389 const sipe_xml *xn_device_name;
390 const sipe_xml *xn_calendar_info;
391 const sipe_xml *xn_state;
392 char *state;
394 /* deviceName */
395 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
396 xn_device_name = sipe_xml_child(node, "deviceName");
397 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
400 /* calendarInfo */
401 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
402 if (xn_calendar_info) {
403 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
405 if (cal_start_time) {
406 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
407 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
409 if (cal_start_time_t_tmp > cal_start_time_t) {
410 cal_start_time = cal_start_time_tmp;
411 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
412 g_free(cal_free_busy_base64);
413 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
415 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);
417 } else {
418 cal_start_time = cal_start_time_tmp;
419 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
420 g_free(cal_free_busy_base64);
421 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
423 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);
427 /* state */
428 xn_state = sipe_xml_child(node, "states/state");
429 if (xn_state) {
430 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
431 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
433 state = sipe_xml_data(xn_state);
434 if (dev_avail_since > user_avail_since &&
435 dev_avail >= res_avail)
437 const gchar *new_desc;
438 res_avail = dev_avail;
439 if (!is_empty(state)) {
440 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
441 g_free(activity);
442 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
443 } else if (sipe_strequal(state, "presenting")) {
444 g_free(activity);
445 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
446 } else {
447 activity = state;
448 state = NULL;
450 activity_since = dev_avail_since;
452 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
453 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
454 if (new_desc) {
455 g_free(activity);
456 activity = g_strdup(new_desc);
459 g_free(state);
463 /* oof */
464 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
465 g_free(activity);
466 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
467 activity_since = 0;
470 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
471 if (sbuddy)
473 g_free(sbuddy->activity);
474 sbuddy->activity = activity;
475 activity = NULL;
477 sbuddy->activity_since = activity_since;
479 sbuddy->user_avail = user_avail;
480 sbuddy->user_avail_since = user_avail_since;
482 g_free(sbuddy->note);
483 sbuddy->note = NULL;
484 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
486 sbuddy->is_oof_note = (xn_oof != NULL);
488 g_free(sbuddy->device_name);
489 sbuddy->device_name = NULL;
490 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
492 if (!is_empty(cal_free_busy_base64)) {
493 g_free(sbuddy->cal_start_time);
494 sbuddy->cal_start_time = g_strdup(cal_start_time);
496 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
498 g_free(sbuddy->cal_free_busy_base64);
499 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
500 cal_free_busy_base64 = NULL;
502 g_free(sbuddy->cal_free_busy);
503 sbuddy->cal_free_busy = NULL;
506 sbuddy->last_non_cal_status_id = status_id;
507 g_free(sbuddy->last_non_cal_activity);
508 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
510 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
511 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
513 if (sbuddy->is_oof_note)
514 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
515 else
516 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
518 g_free(sipe_private->note);
519 sipe_private->note = g_strdup(sbuddy->note);
521 sipe_private->note_since = time(NULL);
524 sipe_status_set_token(sipe_private,
525 sbuddy->last_non_cal_status_id);
528 g_free(cal_free_busy_base64);
529 g_free(activity);
531 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
532 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
533 sipe_status_token_to_activity(status_id));
535 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
536 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
539 g_free(note);
540 sipe_xml_free(xn_presentity);
541 g_free(uri);
542 g_free(self_uri);
545 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
546 const gchar *data,
547 unsigned len)
549 const char *uri;
550 struct sipe_buddy *sbuddy = NULL;
551 sipe_xml *xn_categories;
552 const sipe_xml *xn_category;
553 const char *status = NULL;
554 gboolean do_update_status = FALSE;
555 gboolean has_note_cleaned = FALSE;
556 gboolean has_free_busy_cleaned = FALSE;
558 xn_categories = sipe_xml_parse(data, len);
559 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
560 if (uri) {
561 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
564 if (!sbuddy) {
565 /* Got presence of a buddy not in our contact list, ignore. */
566 sipe_xml_free(xn_categories);
567 return;
570 for (xn_category = sipe_xml_child(xn_categories, "category");
571 xn_category ;
572 xn_category = sipe_xml_twin(xn_category) )
574 const sipe_xml *xn_node;
575 const char *tmp;
576 const char *attrVar = sipe_xml_attribute(xn_category, "name");
577 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
578 sipe_utils_str_to_time(tmp) : 0;
580 /* contactCard */
581 if (sipe_strequal(attrVar, "contactCard"))
583 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
585 if (card) {
586 const sipe_xml *node;
587 /* identity - Display Name and email */
588 node = sipe_xml_child(card, "identity");
589 if (node) {
590 char* display_name = sipe_xml_data(
591 sipe_xml_child(node, "name/displayName"));
592 char* email = sipe_xml_data(
593 sipe_xml_child(node, "email"));
595 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
596 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
598 g_free(display_name);
599 g_free(email);
601 /* company */
602 node = sipe_xml_child(card, "company");
603 if (node) {
604 char* company = sipe_xml_data(node);
605 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
606 g_free(company);
608 /* department */
609 node = sipe_xml_child(card, "department");
610 if (node) {
611 char* department = sipe_xml_data(node);
612 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
613 g_free(department);
615 /* title */
616 node = sipe_xml_child(card, "title");
617 if (node) {
618 char* title = sipe_xml_data(node);
619 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
620 g_free(title);
622 /* office */
623 node = sipe_xml_child(card, "office");
624 if (node) {
625 char* office = sipe_xml_data(node);
626 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
627 g_free(office);
629 /* site (url) */
630 node = sipe_xml_child(card, "url");
631 if (node) {
632 char* site = sipe_xml_data(node);
633 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
634 g_free(site);
636 /* phone */
637 for (node = sipe_xml_child(card, "phone");
638 node;
639 node = sipe_xml_twin(node))
641 const char *phone_type = sipe_xml_attribute(node, "type");
642 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
643 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
645 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
647 g_free(phone);
648 g_free(phone_display_string);
650 /* address */
651 for (node = sipe_xml_child(card, "address");
652 node;
653 node = sipe_xml_twin(node))
655 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
656 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
657 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
658 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
659 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
660 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
662 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
663 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
664 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
665 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
666 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
668 g_free(street);
669 g_free(city);
670 g_free(state);
671 g_free(zipcode);
672 g_free(country_code);
674 break;
679 /* note */
680 else if (sipe_strequal(attrVar, "note"))
682 if (!has_note_cleaned) {
683 has_note_cleaned = TRUE;
685 g_free(sbuddy->note);
686 sbuddy->note = NULL;
687 sbuddy->is_oof_note = FALSE;
688 sbuddy->note_since = publish_time;
690 do_update_status = TRUE;
692 if (publish_time >= sbuddy->note_since) {
693 /* clean up in case no 'note' element is supplied
694 * which indicate note removal in client
696 g_free(sbuddy->note);
697 sbuddy->note = NULL;
698 sbuddy->is_oof_note = FALSE;
699 sbuddy->note_since = publish_time;
701 xn_node = sipe_xml_child(xn_category, "note/body");
702 if (xn_node) {
703 char *tmp;
704 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
705 g_free(tmp);
706 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
707 sbuddy->note_since = publish_time;
709 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
710 uri, sbuddy->note ? sbuddy->note : "");
712 /* to trigger UI refresh in case no status info is supplied in this update */
713 do_update_status = TRUE;
716 /* state */
717 else if(sipe_strequal(attrVar, "state"))
719 char *tmp;
720 int availability;
721 const sipe_xml *xn_availability;
722 const sipe_xml *xn_activity;
723 const sipe_xml *xn_meeting_subject;
724 const sipe_xml *xn_meeting_location;
725 const gchar *legacy_activity;
727 xn_node = sipe_xml_child(xn_category, "state");
728 if (!xn_node) continue;
729 xn_availability = sipe_xml_child(xn_node, "availability");
730 if (!xn_availability) continue;
731 xn_activity = sipe_xml_child(xn_node, "activity");
732 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
733 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
735 tmp = sipe_xml_data(xn_availability);
736 availability = atoi(tmp);
737 g_free(tmp);
739 /* activity */
740 g_free(sbuddy->activity);
741 sbuddy->activity = NULL;
742 if (xn_activity) {
743 const char *token = sipe_xml_attribute(xn_activity, "token");
744 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
746 /* from token */
747 if (!is_empty(token)) {
748 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
750 /* from custom element */
751 if (xn_custom) {
752 char *custom = sipe_xml_data(xn_custom);
754 if (!is_empty(custom)) {
755 g_free(sbuddy->activity);
756 sbuddy->activity = custom;
757 custom = NULL;
759 g_free(custom);
762 /* meeting_subject */
763 g_free(sbuddy->meeting_subject);
764 sbuddy->meeting_subject = NULL;
765 if (xn_meeting_subject) {
766 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
768 if (!is_empty(meeting_subject)) {
769 sbuddy->meeting_subject = meeting_subject;
770 meeting_subject = NULL;
772 g_free(meeting_subject);
774 /* meeting_location */
775 g_free(sbuddy->meeting_location);
776 sbuddy->meeting_location = NULL;
777 if (xn_meeting_location) {
778 char *meeting_location = sipe_xml_data(xn_meeting_location);
780 if (!is_empty(meeting_location)) {
781 sbuddy->meeting_location = meeting_location;
782 meeting_location = NULL;
784 g_free(meeting_location);
787 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
788 legacy_activity = sipe_ocs2007_legacy_activity_description(availability);
789 if (sbuddy->activity && legacy_activity) {
790 gchar *tmp2 = sbuddy->activity;
792 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity);
793 g_free(tmp2);
794 } else if (legacy_activity) {
795 sbuddy->activity = g_strdup(legacy_activity);
798 do_update_status = TRUE;
800 /* calendarData */
801 else if(sipe_strequal(attrVar, "calendarData"))
803 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
804 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
806 if (xn_free_busy) {
807 if (!has_free_busy_cleaned) {
808 has_free_busy_cleaned = TRUE;
810 g_free(sbuddy->cal_start_time);
811 sbuddy->cal_start_time = NULL;
813 g_free(sbuddy->cal_free_busy_base64);
814 sbuddy->cal_free_busy_base64 = NULL;
816 g_free(sbuddy->cal_free_busy);
817 sbuddy->cal_free_busy = NULL;
819 sbuddy->cal_free_busy_published = publish_time;
822 if (publish_time >= sbuddy->cal_free_busy_published) {
823 g_free(sbuddy->cal_start_time);
824 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
826 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
827 15 : 0;
829 g_free(sbuddy->cal_free_busy_base64);
830 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
832 g_free(sbuddy->cal_free_busy);
833 sbuddy->cal_free_busy = NULL;
835 sbuddy->cal_free_busy_published = publish_time;
837 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);
841 if (xn_working_hours) {
842 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
847 if (do_update_status) {
848 guint activity;
850 if (status) {
851 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
852 activity = sipe_status_token_to_activity(status);
853 } else {
854 /* no status category in this update,
855 using contact's current status */
856 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
857 uri);
860 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity);
863 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
865 sipe_xml_free(xn_categories);
868 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
869 const gchar *uri,
870 const gchar *activity,
871 gboolean is_online)
873 if (is_online) {
874 const gchar *status_id = NULL;
875 if (activity) {
876 if (sipe_strequal(activity,
877 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
878 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
879 } else if (sipe_strequal(activity,
880 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
881 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
885 if (!status_id) {
886 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
889 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
890 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
891 sipe_status_token_to_activity(status_id));
892 } else {
893 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
894 SIPE_ACTIVITY_OFFLINE);
898 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
899 const gchar *data,
900 unsigned len)
902 gchar *uri;
903 gchar *getbasic;
904 gchar *activity = NULL;
905 sipe_xml *pidf;
906 const sipe_xml *basicstatus = NULL, *tuple, *status;
907 gboolean isonline = FALSE;
908 const sipe_xml *display_name_node;
910 pidf = sipe_xml_parse(data, len);
911 if (!pidf) {
912 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
913 return;
916 if ((tuple = sipe_xml_child(pidf, "tuple")))
918 if ((status = sipe_xml_child(tuple, "status"))) {
919 basicstatus = sipe_xml_child(status, "basic");
923 if (!basicstatus) {
924 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
925 sipe_xml_free(pidf);
926 return;
929 getbasic = sipe_xml_data(basicstatus);
930 if (!getbasic) {
931 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
932 sipe_xml_free(pidf);
933 return;
936 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
937 if (strstr(getbasic, "open")) {
938 isonline = TRUE;
940 g_free(getbasic);
942 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
944 display_name_node = sipe_xml_child(pidf, "display-name");
945 if (display_name_node) {
946 char * display_name = sipe_xml_data(display_name_node);
948 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
949 g_free(display_name);
951 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
954 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
955 if ((status = sipe_xml_child(tuple, "status"))) {
956 if ((basicstatus = sipe_xml_child(status, "activities"))) {
957 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
958 activity = sipe_xml_data(basicstatus);
959 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
965 sipe_buddy_status_from_activity(sipe_private,
966 uri,
967 activity,
968 isonline);
970 g_free(activity);
971 g_free(uri);
972 sipe_xml_free(pidf);
975 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
976 const GSList *fields,
977 const gchar *body,
978 gsize length)
980 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
982 if (strstr(type,"application/rlmi+xml")) {
983 process_incoming_notify_rlmi_resub(user_data, body, length);
984 } else if (strstr(type, "text/xml+msrtc.pidf")) {
985 process_incoming_notify_msrtc(user_data, body, length);
986 } else {
987 process_incoming_notify_rlmi(user_data, body, length);
991 static void sipe_process_presence(struct sipe_core_private *sipe_private,
992 struct sipmsg *msg)
994 const char *ctype = sipmsg_find_header(msg, "Content-Type");
996 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
998 if (ctype &&
999 (strstr(ctype, "application/rlmi+xml") ||
1000 strstr(ctype, "application/msrtc-event-categories+xml")))
1002 if (strstr(ctype, "multipart"))
1004 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
1006 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
1008 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
1010 else if(strstr(ctype, "application/rlmi+xml"))
1012 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
1015 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
1017 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
1019 else
1021 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
1026 * Fires on deregistration event initiated by server.
1027 * [MS-SIPREGE] SIP extension.
1029 * OCS2007 Example
1031 * Content-Type: text/registration-event
1032 * subscription-state: terminated;expires=0
1033 * ms-diagnostics-public: 4141;reason="User disabled"
1035 * deregistered;event=rejected
1037 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
1038 struct sipmsg *msg)
1040 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1041 gchar *event = NULL;
1042 gchar *reason = NULL;
1043 gchar *warning;
1045 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1047 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1048 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1049 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1050 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1051 } else {
1052 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1053 return;
1056 reason = sipmsg_get_ms_diagnostics_reason(msg);
1057 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1058 if (!reason) { // for LCS2005
1059 if (event && sipe_strcase_equal(event, "unregistered")) {
1060 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1061 reason = g_strdup(_("you are already signed in at another location"));
1062 } else if (event && sipe_strcase_equal(event, "rejected")) {
1063 reason = g_strdup(_("user disabled")); // [MS-OCER]
1064 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1065 reason = g_strdup(_("user moved")); // [MS-OCER]
1068 g_free(event);
1069 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1070 g_free(reason);
1072 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1073 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1074 warning);
1075 g_free(warning);
1080 * Removes entries from local buddy list
1081 * that does not correspond ones in the roaming contact list.
1083 static void sipe_cleanup_local_blist(struct sipe_core_private *sipe_private)
1085 GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC,
1086 NULL, NULL);
1087 GSList *entry = buddies;
1088 struct sipe_buddy *buddy;
1089 sipe_backend_buddy b;
1090 gchar *bname;
1091 gchar *gname;
1093 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies));
1094 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private->buddies));
1095 while (entry) {
1096 b = entry->data;
1097 gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC, b);
1098 bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
1099 buddy = g_hash_table_lookup(sipe_private->buddies, bname);
1100 if(buddy) {
1101 gboolean in_sipe_groups = FALSE;
1102 GSList *entry2 = buddy->groups;
1103 while (entry2) {
1104 struct sipe_group *group = entry2->data;
1105 if (sipe_strequal(group->name, gname)) {
1106 in_sipe_groups = TRUE;
1107 break;
1109 entry2 = entry2->next;
1111 if(!in_sipe_groups) {
1112 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname, gname);
1113 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1115 } else {
1116 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname, gname);
1117 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1119 g_free(bname);
1120 g_free(gname);
1121 entry = entry->next;
1123 g_slist_free(buddies);
1127 * A callback for g_hash_table_foreach
1129 static void sipe_buddy_subscribe_cb(char *buddy_name,
1130 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1131 struct sipe_core_private *sipe_private)
1133 guint time_range = (g_hash_table_size(sipe_private->buddies) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1136 * g_hash_table_size() can never return 0, otherwise this function
1137 * wouldn't be called :-) But to keep Coverity happy...
1139 if (time_range) {
1140 gchar *action_name = sipe_utils_presence_key(buddy_name);
1141 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
1143 sipe_schedule_mseconds(sipe_private,
1144 action_name,
1145 g_strdup(buddy_name),
1146 timeout,
1147 sipe_subscribe_presence_single_cb,
1148 g_free);
1149 g_free(action_name);
1153 /* Replace "~" with localized version of "Other Contacts" */
1154 static const gchar *get_group_name(const sipe_xml *node)
1156 const gchar *name = sipe_xml_attribute(node, "name");
1157 return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name);
1160 static void add_new_group(struct sipe_core_private *sipe_private,
1161 const sipe_xml *node)
1163 struct sipe_group *group = g_new0(struct sipe_group, 1);
1165 group->name = g_strdup(get_group_name(node));
1166 group->id = (int)g_ascii_strtod(sipe_xml_attribute(node, "id"), NULL);
1168 sipe_group_add(sipe_private, group);
1171 static void add_new_buddy(struct sipe_core_private *sipe_private,
1172 const sipe_xml *node,
1173 const gchar *uri,
1174 const gchar *alias)
1176 const gchar *name = sipe_xml_attribute(node, "name");
1177 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1178 gchar *normalized_uri = g_ascii_strdown(uri, -1);
1179 struct sipe_buddy *buddy = NULL;
1180 gchar *tmp;
1181 gchar **item_groups;
1182 int i = 0;
1184 /* assign to group Other Contacts if nothing else received */
1185 tmp = g_strdup(sipe_xml_attribute(node, "groups"));
1186 if (is_empty(tmp)) {
1187 struct sipe_group *group = sipe_group_find_by_name(sipe_private,
1188 _("Other Contacts"));
1189 g_free(tmp);
1190 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1192 item_groups = g_strsplit(tmp, " ", 0);
1193 g_free(tmp);
1195 while (item_groups[i]) {
1196 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1197 g_ascii_strtod(item_groups[i],
1198 NULL));
1200 /* If couldn't find the right group for this contact, */
1201 /* then just put it in the first group we have */
1202 if ((group == NULL) &&
1203 (g_slist_length(sipe_private->groups) > 0))
1204 group = sipe_private->groups->data;
1206 if (group) {
1207 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1208 normalized_uri,
1209 group->name);
1210 gchar *b_alias;
1212 if (!b) {
1213 b = sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1214 normalized_uri,
1215 alias,
1216 group->name);
1217 SIPE_DEBUG_INFO("Created new buddy %s with alias %s",
1218 normalized_uri, alias);
1221 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b);
1222 if (sipe_strcase_equal(alias, b_alias) &&
1223 !is_empty(name)) {
1224 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1226 name);
1227 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1228 normalized_uri, group->name, b_alias, name);
1230 g_free(b_alias);
1232 if (!buddy)
1233 buddy = sipe_buddy_add(sipe_private,
1234 normalized_uri);
1236 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
1237 group,
1238 (GCompareFunc)sipe_group_compare,
1239 NULL);
1241 SIPE_DEBUG_INFO("Added buddy %s to group %s",
1242 buddy->name, group->name);
1243 } else {
1244 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1245 name);
1248 i++;
1251 g_strfreev(item_groups);
1252 g_free(normalized_uri);
1255 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1256 struct sipmsg *msg)
1258 int len = msg->bodylen;
1260 const gchar *tmp = sipmsg_find_header(msg, "Event");
1261 const sipe_xml *item;
1262 sipe_xml *isc;
1263 guint delta;
1264 const sipe_xml *group_node;
1266 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1267 return FALSE;
1270 /* Convert the contact from XML to backend Buddies */
1271 isc = sipe_xml_parse(msg->body, len);
1272 if (!isc) {
1273 return FALSE;
1276 /* [MS-SIP]: deltaNum MUST be non-zero */
1277 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1278 if (delta) {
1279 sipe_private->deltanum_contacts = delta;
1283 * Process whole buddy list
1285 * - Only sent once
1286 * * up to Lync 2010
1287 * * Lync 2013 (and later) with buddy list not migrated
1289 * - Lync 2013 with buddy list migrated to Unified Contact Store (UCS)
1290 * * Notify piggy-backed on SUBSCRIBE response with empty list
1291 * * NOTIFY send by server with standard list
1293 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1294 const gchar *ucsmode = sipe_xml_attribute(isc, "ucsmode");
1296 SIPE_CORE_PRIVATE_FLAG_UNSET(LYNC2013);
1297 SIPE_CORE_PRIVATE_FLAG_UNSET(UCS);
1298 if (ucsmode) {
1299 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013);
1300 SIPE_DEBUG_INFO_NOFORMAT("contact list contains 'ucsmode' attribute (indicates Lync 2013+)");
1302 if (sipe_strcase_equal(ucsmode, "migrated")) {
1303 SIPE_CORE_PRIVATE_FLAG_SET(UCS);
1304 SIPE_DEBUG_INFO_NOFORMAT("contact list has been migrated to Unified Contact Store (UCS)");
1305 sipe_ucs_init(sipe_private);
1309 group_node = sipe_xml_child(isc, "group");
1310 item = sipe_xml_child(isc, "contact");
1312 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) || group_node || item) {
1314 /* Start processing contact list */
1315 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1317 /* Parse groups */
1318 for (; group_node; group_node = sipe_xml_twin(group_node))
1319 add_new_group(sipe_private, group_node);
1321 /* Make sure we have at least one group */
1322 if (g_slist_length(sipe_private->groups) == 0) {
1323 sipe_group_create(sipe_private, _("Other Contacts"), NULL);
1326 /* Parse contacts */
1327 for (; item; item = sipe_xml_twin(item)) {
1328 const gchar *name = sipe_xml_attribute(item, "uri");
1329 gchar *uri = sip_uri_from_name(name);
1330 add_new_buddy(sipe_private, item, uri, name);
1331 g_free(uri);
1334 sipe_cleanup_local_blist(sipe_private);
1336 /* Add self-contact if not there yet. 2005 systems. */
1337 /* This will resemble subscription to roaming_self in 2007 systems */
1338 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1339 gchar *self_uri = sip_uri_self(sipe_private);
1340 sipe_buddy_add(sipe_private, self_uri);
1341 g_free(self_uri);
1344 /* Finished processing contact list */
1345 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1348 /* Process buddy list updates */
1349 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1351 /* Process new groups */
1352 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1353 add_new_group(sipe_private, group_node);
1355 /* Process modified groups */
1356 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1357 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1358 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1359 NULL));
1360 if (group) {
1361 const gchar *name = get_group_name(group_node);
1363 if (!(is_empty(name) ||
1364 sipe_strequal(group->name, name)) &&
1365 sipe_group_rename(sipe_private,
1366 group,
1367 name))
1368 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1372 /* Process new buddies */
1373 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1374 const gchar *uri = sipe_xml_attribute(item, "uri");
1375 const gchar *name = sipe_get_no_sip_uri(uri);
1376 add_new_buddy(sipe_private, item, uri, name);
1379 /* Process modified buddies */
1380 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1381 const gchar *uri = sipe_xml_attribute(item, "uri");
1382 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies,
1383 uri);
1385 if (buddy) {
1386 gchar **item_groups = g_strsplit(sipe_xml_attribute(item,
1387 "groups"),
1388 " ", 0);
1390 /* this should be defined. Otherwise we would get "deletedContact" */
1391 if (item_groups) {
1392 const gchar *name = sipe_xml_attribute(item, "name");
1393 gboolean empty_name = is_empty(name);
1394 GSList *found = NULL;
1395 GSList *entry;
1396 int i = 0;
1398 while (item_groups[i]) {
1399 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1400 g_ascii_strtod(item_groups[i],
1401 NULL));
1402 /* ignore unkown groups */
1403 if (group) {
1404 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1405 uri,
1406 group->name);
1408 /* add group to found list */
1409 found = g_slist_prepend(found, group);
1411 if (b) {
1412 /* new alias? */
1413 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1416 if (!(empty_name ||
1417 sipe_strequal(b_alias, name))) {
1418 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1420 name);
1421 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1422 uri, group->name, b_alias, name);
1424 g_free(b_alias);
1426 } else {
1427 const gchar *alias = empty_name ? uri : name;
1428 /* buddy was not in this group */
1429 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1430 uri,
1431 alias,
1432 group->name);
1433 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
1434 group,
1435 (GCompareFunc) sipe_group_compare,
1436 NULL);
1437 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1438 uri, alias, group->name);
1442 /* next group */
1443 i++;
1445 g_strfreev(item_groups);
1447 /* removed from groups? */
1448 entry = buddy->groups;
1449 while (entry) {
1450 GSList *remove_link = entry;
1451 struct sipe_group *group = remove_link->data;
1453 /* next buddy group */
1454 entry = entry->next;
1456 /* old group NOT found in new list? */
1457 if (g_slist_find(found, group) == NULL) {
1458 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1459 uri,
1460 group->name);
1461 SIPE_DEBUG_INFO("Removing buddy %s from group '%s'",
1462 uri, group->name);
1463 /* this should never be NULL */
1464 if (oldb)
1465 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1466 oldb);
1467 buddy->groups = g_slist_remove_link(buddy->groups,
1468 remove_link);
1471 g_slist_free(found);
1476 /* Process deleted buddies */
1477 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1478 const gchar *uri = sipe_xml_attribute(item, "uri");
1479 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies,
1480 uri);
1482 if (buddy) {
1483 GSList *entry = buddy->groups;
1485 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1486 while (entry) {
1487 struct sipe_group *group = entry->data;
1488 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1489 uri,
1490 group->name);
1491 /* this should never be NULL */
1492 if (oldb)
1493 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1494 oldb);
1496 /* next buddy group */
1497 entry = entry->next;
1499 sipe_buddy_remove(sipe_private, buddy);
1503 /* Process deleted groups
1505 * NOTE: all buddies will already have been removed from the
1506 * group prior to this. The log shows that OCS actually
1507 * sends two separate updates when you delete a group:
1509 * - first one with "modifiedContact" removing buddies
1510 * from the group, leaving it empty, and
1512 * - then one with "deletedGroup" removing the group
1514 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1515 sipe_group_remove(sipe_private,
1516 sipe_group_find_by_id(sipe_private,
1517 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1518 NULL)));
1521 sipe_xml_free(isc);
1524 * Subscribe to buddies (if any), but only do it once.
1525 * We'll resubsribe to them based on the Expire field values.
1527 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES) &&
1528 g_hash_table_size(sipe_private->buddies)) {
1529 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
1530 sipe_subscribe_presence_batched(sipe_private);
1531 } else {
1532 g_hash_table_foreach(sipe_private->buddies,
1533 (GHFunc)sipe_buddy_subscribe_cb,
1534 sipe_private);
1536 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES);
1539 /* for 2005 systems schedule contacts' status update
1540 * based on their calendar information
1542 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1543 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1546 return 0;
1549 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1550 struct sipmsg *msg)
1552 guint delta;
1553 sipe_xml *xml;
1555 xml = sipe_xml_parse(msg->body, msg->bodylen);
1556 if (!xml)
1557 return;
1559 /* [MS-SIP]: deltaNum MUST be non-zero */
1560 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1561 if (delta) {
1562 sipe_private->deltanum_acl = delta;
1565 sipe_xml_free(xml);
1568 struct sipe_auth_job {
1569 gchar *who;
1570 struct sipe_core_private *sipe_private;
1573 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1574 const gchar* who,
1575 gboolean allow)
1577 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1579 if (allow) {
1580 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1581 } else {
1582 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1585 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1586 sipe_ocs2007_change_access_level(sipe_private,
1587 (allow ? -1 : 32000),
1588 "user",
1589 sipe_get_no_sip_uri(who));
1590 } else {
1591 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1596 static void sipe_auth_user_cb(gpointer data)
1598 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1599 if (!job) return;
1601 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1602 job->who,
1603 TRUE);
1604 g_free(job);
1607 static void sipe_deny_user_cb(gpointer data)
1609 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1610 if (!job) return;
1612 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1613 job->who,
1614 FALSE);
1615 g_free(job);
1618 /* OCS2005- */
1619 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1620 struct sipmsg * msg)
1622 sipe_xml *watchers;
1623 const sipe_xml *watcher;
1624 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1625 if (msg->response != 0 && msg->response != 200) return;
1627 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1629 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1630 if (!watchers) return;
1632 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1633 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1634 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1635 gboolean on_list = g_hash_table_lookup(sipe_private->buddies, remote_user) != NULL;
1637 // TODO pull out optional displayName to pass as alias
1638 if (remote_user) {
1639 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1640 job->who = remote_user;
1641 job->sipe_private = sipe_private;
1642 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1643 remote_user,
1644 alias,
1645 on_list,
1646 sipe_auth_user_cb,
1647 sipe_deny_user_cb,
1648 (gpointer)job);
1653 sipe_xml_free(watchers);
1654 return;
1658 * Dispatcher for all incoming subscription information
1659 * whether it comes from NOTIFY, BENOTIFY requests or
1660 * piggy-backed to subscription's OK responce.
1662 void process_incoming_notify(struct sipe_core_private *sipe_private,
1663 struct sipmsg *msg)
1665 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1666 const gchar *event = sipmsg_find_header(msg, "Event");
1667 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1669 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1671 /* implicit subscriptions */
1672 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1673 sipe_process_imdn(sipe_private, msg);
1675 /* event subscriptions */
1676 } else if (event) {
1678 /* One-off subscriptions - sent with "Expires: 0" */
1679 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) {
1680 sipe_process_provisioning_v2(sipe_private, msg);
1681 } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) {
1682 sipe_process_provisioning(sipe_private, msg);
1683 } else if (sipe_strcase_equal(event, "presence")) {
1684 sipe_process_presence(sipe_private, msg);
1685 } else if (sipe_strcase_equal(event, "registration-notify")) {
1686 sipe_process_registration_notify(sipe_private, msg);
1688 /* Subscriptions with timeout */
1689 } else if (!subscription_state || strstr(subscription_state, "active")) {
1690 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
1691 sipe_process_roaming_contacts(sipe_private, msg);
1692 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) {
1693 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1694 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) {
1695 sipe_process_roaming_acl(sipe_private, msg);
1696 } else if (sipe_strcase_equal(event, "presence.wpending")) {
1697 sipe_process_presence_wpending(sipe_private, msg);
1698 } else if (sipe_strcase_equal(event, "conference")) {
1699 sipe_process_conference(sipe_private, msg);
1706 Local Variables:
1707 mode: c
1708 c-file-style: "bsd"
1709 indent-tabs-mode: t
1710 tab-width: 8
1711 End: