buddy: factor out local list cleanup code
[siplcs.git] / src / core / sipe-notify.c
blob9c288de557a3eab60c2792e5f0b94d131ef85972
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-status.h"
56 #include "sipe-subscriptions.h"
57 #include "sipe-ucs.h"
58 #include "sipe-utils.h"
59 #include "sipe-xml.h"
61 /* OCS2005 */
62 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
63 struct sipmsg *msg)
65 sipe_xml *xn_provision;
66 const sipe_xml *node;
68 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
69 if ((node = sipe_xml_child(xn_provision, "user"))) {
70 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
71 if ((node = sipe_xml_child(node, "line"))) {
72 const gchar *line_uri = sipe_xml_attribute(node, "uri");
73 const gchar *server = sipe_xml_attribute(node, "server");
74 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
75 sip_csta_open(sipe_private, line_uri, server);
78 sipe_xml_free(xn_provision);
81 /* OCS2007+ */
82 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
83 struct sipmsg *msg)
85 sipe_xml *xn_provision_group_list;
86 const sipe_xml *node;
88 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
90 /* provisionGroup */
91 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup");
92 node;
93 node = sipe_xml_twin(node)) {
94 const gchar *node_name = sipe_xml_attribute(node, "name");
96 /* ServerConfiguration */
97 if (sipe_strequal("ServerConfiguration", node_name)) {
98 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
99 "dlxExternalUrl" : "dlxInternalUrl";
100 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
101 "absExternalServerUrl" : "absInternalServerUrl";
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 /* persistentChatConfiguration */
134 } else if (sipe_strequal("persistentChatConfiguration", node_name)) {
135 const sipe_xml *property;
136 gboolean enabled = FALSE;
137 gchar *uri = NULL;
139 for (property = sipe_xml_child(node, "propertyEntryList/property");
140 property;
141 property = sipe_xml_twin(property)) {
142 const gchar *name = sipe_xml_attribute(property, "name");
143 gchar *value = sipe_xml_data(property);
145 if (sipe_strequal(name, "EnablePersistentChat")) {
146 enabled = sipe_strequal(value, "true");
148 } else if (sipe_strequal(name, "DefaultPersistentChatPoolUri")) {
149 g_free(uri);
150 uri = value;
151 value = NULL;
153 g_free(value);
156 if (enabled) {
157 g_free(sipe_private->persistentChatPool_uri);
158 sipe_private->persistentChatPool_uri = g_strdup(sipe_get_no_sip_uri(uri));
159 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->persistentChatPool_uri=%s",
160 sipe_private->persistentChatPool_uri ? sipe_private->persistentChatPool_uri : "");
162 g_free(uri);
166 sipe_xml_free(xn_provision_group_list);
168 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
169 /* Some buddies might have been added before we received this
170 * provisioning notify with DLX and addressbook URIs. Now we can
171 * trigger an update of their photos. */
172 sipe_buddy_refresh_photos(sipe_private);
175 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
176 /* persistentChatPool_uri has been set at this point */
177 sipe_groupchat_init(sipe_private);
180 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
181 const gchar *data, unsigned len)
183 sipe_xml *xn_list;
184 const sipe_xml *xn_resource;
185 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
186 g_free, NULL);
188 xn_list = sipe_xml_parse(data, len);
190 for (xn_resource = sipe_xml_child(xn_list, "resource");
191 xn_resource;
192 xn_resource = sipe_xml_twin(xn_resource) )
194 const char *uri, *state;
195 const sipe_xml *xn_instance;
197 xn_instance = sipe_xml_child(xn_resource, "instance");
198 if (!xn_instance) continue;
200 uri = sipe_xml_attribute(xn_resource, "uri");
201 state = sipe_xml_attribute(xn_instance, "state");
202 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
204 if (strstr(state, "resubscribe")) {
205 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
207 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
208 gchar *user = g_strdup(uri);
209 gchar *host = g_strdup(poolFqdn);
210 GSList *server = g_hash_table_lookup(servers,
211 host);
212 server = g_slist_append(server, user);
213 g_hash_table_insert(servers, host, server);
214 } else {
215 sipe_subscribe_presence_single(sipe_private,
216 uri,
217 uri);
222 /* Send out any deferred poolFqdn subscriptions */
223 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
224 g_hash_table_destroy(servers);
226 sipe_xml_free(xn_list);
230 * Update user phone
231 * Suitable for both 2005 and 2007 systems.
233 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
234 * @param phone_type
235 * @param phone may be modified to strip white space
236 * @param phone_display_string may be modified to strip white space
238 static void
239 sipe_update_user_phone(struct sipe_core_private *sipe_private,
240 const gchar *uri,
241 const gchar *phone_type,
242 gchar *phone,
243 gchar *phone_display_string)
245 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
246 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
248 if(!phone || strlen(phone) == 0) return;
250 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
251 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
252 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
253 } else if (sipe_strequal(phone_type, "home")) {
254 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
255 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
256 } else if (sipe_strequal(phone_type, "other")) {
257 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
258 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
259 } else if (sipe_strequal(phone_type, "custom1")) {
260 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
261 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
264 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
265 if (phone_display_string) {
266 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
270 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
271 const gchar *data,
272 unsigned len)
274 char *activity = NULL;
275 const char *epid;
276 const char *status_id = NULL;
277 const char *name;
278 char *uri;
279 char *self_uri = sip_uri_self(sipe_private);
280 int avl;
281 int act;
282 const char *device_name = NULL;
283 const char *cal_start_time = NULL;
284 const char *cal_granularity = NULL;
285 char *cal_free_busy_base64 = NULL;
286 struct sipe_buddy *sbuddy;
287 const sipe_xml *node;
288 sipe_xml *xn_presentity;
289 const sipe_xml *xn_availability;
290 const sipe_xml *xn_activity;
291 const sipe_xml *xn_display_name;
292 const sipe_xml *xn_email;
293 const sipe_xml *xn_phone_number;
294 const sipe_xml *xn_userinfo;
295 const sipe_xml *xn_note;
296 const sipe_xml *xn_oof;
297 const sipe_xml *xn_state;
298 const sipe_xml *xn_contact;
299 char *note;
300 int user_avail;
301 const char *user_avail_nil;
302 int res_avail;
303 time_t user_avail_since = 0;
304 time_t activity_since = 0;
306 /* fix for Reuters environment on Linux */
307 if (data && strstr(data, "encoding=\"utf-16\"")) {
308 char *tmp_data;
309 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
310 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
311 g_free(tmp_data);
312 } else {
313 xn_presentity = sipe_xml_parse(data, len);
316 xn_availability = sipe_xml_child(xn_presentity, "availability");
317 xn_activity = sipe_xml_child(xn_presentity, "activity");
318 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
319 xn_email = sipe_xml_child(xn_presentity, "email");
320 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
321 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
322 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
323 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
324 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
325 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
326 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
327 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
328 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
329 note = xn_note ? sipe_xml_data(xn_note) : NULL;
331 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
332 user_avail = 0;
333 user_avail_since = 0;
336 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
337 uri = sip_uri_from_name(name);
338 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
339 epid = sipe_xml_attribute(xn_availability, "epid");
340 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
342 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
343 activity = g_strdup(sipe_ocs2005_activity_description(act));
344 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
345 if (user_avail > res_avail) {
346 res_avail = user_avail;
347 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
350 if (xn_display_name) {
351 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
352 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
353 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
354 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
355 char *tel_uri = sip_to_tel_uri(phone_number);
357 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
358 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
359 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
360 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
362 g_free(tel_uri);
363 g_free(phone_label);
364 g_free(phone_number);
365 g_free(email);
366 g_free(display_name);
369 if (xn_contact) {
370 /* tel */
371 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
373 /* Ex.: <tel type="work">tel:+3222220000</tel> */
374 const char *phone_type = sipe_xml_attribute(node, "type");
375 char* phone = sipe_xml_data(node);
377 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
379 g_free(phone);
383 if (xn_display_name || xn_contact)
384 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
386 /* devicePresence */
387 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
388 const sipe_xml *xn_device_name;
389 const sipe_xml *xn_calendar_info;
390 const sipe_xml *xn_state;
391 char *state;
393 /* deviceName */
394 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
395 xn_device_name = sipe_xml_child(node, "deviceName");
396 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
399 /* calendarInfo */
400 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
401 if (xn_calendar_info) {
402 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
404 if (cal_start_time) {
405 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
406 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
408 if (cal_start_time_t_tmp > cal_start_time_t) {
409 cal_start_time = cal_start_time_tmp;
410 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
411 g_free(cal_free_busy_base64);
412 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
414 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);
416 } else {
417 cal_start_time = cal_start_time_tmp;
418 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
419 g_free(cal_free_busy_base64);
420 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
422 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);
426 /* state */
427 xn_state = sipe_xml_child(node, "states/state");
428 if (xn_state) {
429 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
430 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
432 state = sipe_xml_data(xn_state);
433 if (dev_avail_since > user_avail_since &&
434 dev_avail >= res_avail)
436 const gchar *new_desc;
437 res_avail = dev_avail;
438 if (!is_empty(state)) {
439 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
440 g_free(activity);
441 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
442 } else if (sipe_strequal(state, "presenting")) {
443 g_free(activity);
444 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
445 } else {
446 activity = state;
447 state = NULL;
449 activity_since = dev_avail_since;
451 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
452 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
453 if (new_desc) {
454 g_free(activity);
455 activity = g_strdup(new_desc);
458 g_free(state);
462 /* oof */
463 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
464 g_free(activity);
465 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
466 activity_since = 0;
469 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
470 if (sbuddy)
472 g_free(sbuddy->activity);
473 sbuddy->activity = activity;
474 activity = NULL;
476 sbuddy->activity_since = activity_since;
478 sbuddy->user_avail = user_avail;
479 sbuddy->user_avail_since = user_avail_since;
481 g_free(sbuddy->note);
482 sbuddy->note = NULL;
483 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
485 sbuddy->is_oof_note = (xn_oof != NULL);
487 g_free(sbuddy->device_name);
488 sbuddy->device_name = NULL;
489 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
491 if (!is_empty(cal_free_busy_base64)) {
492 g_free(sbuddy->cal_start_time);
493 sbuddy->cal_start_time = g_strdup(cal_start_time);
495 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
497 g_free(sbuddy->cal_free_busy_base64);
498 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
499 cal_free_busy_base64 = NULL;
501 g_free(sbuddy->cal_free_busy);
502 sbuddy->cal_free_busy = NULL;
505 sbuddy->last_non_cal_status_id = status_id;
506 g_free(sbuddy->last_non_cal_activity);
507 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
509 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
510 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
512 if (sbuddy->is_oof_note)
513 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
514 else
515 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
517 g_free(sipe_private->note);
518 sipe_private->note = g_strdup(sbuddy->note);
520 sipe_private->note_since = time(NULL);
523 sipe_status_set_token(sipe_private,
524 sbuddy->last_non_cal_status_id);
527 g_free(cal_free_busy_base64);
528 g_free(activity);
530 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
531 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
532 sipe_status_token_to_activity(status_id));
534 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
535 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
538 g_free(note);
539 sipe_xml_free(xn_presentity);
540 g_free(uri);
541 g_free(self_uri);
544 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
545 const gchar *data,
546 unsigned len)
548 const char *uri;
549 struct sipe_buddy *sbuddy = NULL;
550 sipe_xml *xn_categories;
551 const sipe_xml *xn_category;
552 const char *status = NULL;
553 gboolean do_update_status = FALSE;
554 gboolean has_note_cleaned = FALSE;
555 gboolean has_free_busy_cleaned = FALSE;
557 xn_categories = sipe_xml_parse(data, len);
558 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
559 if (uri) {
560 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
563 if (!sbuddy) {
564 /* Got presence of a buddy not in our contact list, ignore. */
565 sipe_xml_free(xn_categories);
566 return;
569 for (xn_category = sipe_xml_child(xn_categories, "category");
570 xn_category ;
571 xn_category = sipe_xml_twin(xn_category) )
573 const sipe_xml *xn_node;
574 const char *tmp;
575 const char *attrVar = sipe_xml_attribute(xn_category, "name");
576 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
577 sipe_utils_str_to_time(tmp) : 0;
579 /* contactCard */
580 if (sipe_strequal(attrVar, "contactCard"))
582 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
584 if (card) {
585 const sipe_xml *node;
586 /* identity - Display Name and email */
587 node = sipe_xml_child(card, "identity");
588 if (node) {
589 char* display_name = sipe_xml_data(
590 sipe_xml_child(node, "name/displayName"));
591 char* email = sipe_xml_data(
592 sipe_xml_child(node, "email"));
594 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
595 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
597 g_free(display_name);
598 g_free(email);
600 /* company */
601 node = sipe_xml_child(card, "company");
602 if (node) {
603 char* company = sipe_xml_data(node);
604 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
605 g_free(company);
607 /* department */
608 node = sipe_xml_child(card, "department");
609 if (node) {
610 char* department = sipe_xml_data(node);
611 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
612 g_free(department);
614 /* title */
615 node = sipe_xml_child(card, "title");
616 if (node) {
617 char* title = sipe_xml_data(node);
618 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
619 g_free(title);
621 /* office */
622 node = sipe_xml_child(card, "office");
623 if (node) {
624 char* office = sipe_xml_data(node);
625 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
626 g_free(office);
628 /* site (url) */
629 node = sipe_xml_child(card, "url");
630 if (node) {
631 char* site = sipe_xml_data(node);
632 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
633 g_free(site);
635 /* phone */
636 for (node = sipe_xml_child(card, "phone");
637 node;
638 node = sipe_xml_twin(node))
640 const char *phone_type = sipe_xml_attribute(node, "type");
641 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
642 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
644 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
646 g_free(phone);
647 g_free(phone_display_string);
649 /* address */
650 for (node = sipe_xml_child(card, "address");
651 node;
652 node = sipe_xml_twin(node))
654 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
655 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
656 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
657 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
658 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
659 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
661 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
662 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
663 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
664 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
665 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
667 g_free(street);
668 g_free(city);
669 g_free(state);
670 g_free(zipcode);
671 g_free(country_code);
673 break;
678 /* note */
679 else if (sipe_strequal(attrVar, "note"))
681 if (!has_note_cleaned) {
682 has_note_cleaned = TRUE;
684 g_free(sbuddy->note);
685 sbuddy->note = NULL;
686 sbuddy->is_oof_note = FALSE;
687 sbuddy->note_since = publish_time;
689 do_update_status = TRUE;
691 if (publish_time >= sbuddy->note_since) {
692 /* clean up in case no 'note' element is supplied
693 * which indicate note removal in client
695 g_free(sbuddy->note);
696 sbuddy->note = NULL;
697 sbuddy->is_oof_note = FALSE;
698 sbuddy->note_since = publish_time;
700 xn_node = sipe_xml_child(xn_category, "note/body");
701 if (xn_node) {
702 char *tmp;
703 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
704 g_free(tmp);
705 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
706 sbuddy->note_since = publish_time;
708 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
709 uri, sbuddy->note ? sbuddy->note : "");
711 /* to trigger UI refresh in case no status info is supplied in this update */
712 do_update_status = TRUE;
715 /* state */
716 else if(sipe_strequal(attrVar, "state"))
718 char *tmp;
719 int availability;
720 const sipe_xml *xn_availability;
721 const sipe_xml *xn_activity;
722 const sipe_xml *xn_meeting_subject;
723 const sipe_xml *xn_meeting_location;
724 const gchar *legacy_activity;
726 xn_node = sipe_xml_child(xn_category, "state");
727 if (!xn_node) continue;
728 xn_availability = sipe_xml_child(xn_node, "availability");
729 if (!xn_availability) continue;
730 xn_activity = sipe_xml_child(xn_node, "activity");
731 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
732 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
734 tmp = sipe_xml_data(xn_availability);
735 availability = atoi(tmp);
736 g_free(tmp);
738 /* activity */
739 g_free(sbuddy->activity);
740 sbuddy->activity = NULL;
741 if (xn_activity) {
742 const char *token = sipe_xml_attribute(xn_activity, "token");
743 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
745 /* from token */
746 if (!is_empty(token)) {
747 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
749 /* from custom element */
750 if (xn_custom) {
751 char *custom = sipe_xml_data(xn_custom);
753 if (!is_empty(custom)) {
754 g_free(sbuddy->activity);
755 sbuddy->activity = custom;
756 custom = NULL;
758 g_free(custom);
761 /* meeting_subject */
762 g_free(sbuddy->meeting_subject);
763 sbuddy->meeting_subject = NULL;
764 if (xn_meeting_subject) {
765 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
767 if (!is_empty(meeting_subject)) {
768 sbuddy->meeting_subject = meeting_subject;
769 meeting_subject = NULL;
771 g_free(meeting_subject);
773 /* meeting_location */
774 g_free(sbuddy->meeting_location);
775 sbuddy->meeting_location = NULL;
776 if (xn_meeting_location) {
777 char *meeting_location = sipe_xml_data(xn_meeting_location);
779 if (!is_empty(meeting_location)) {
780 sbuddy->meeting_location = meeting_location;
781 meeting_location = NULL;
783 g_free(meeting_location);
786 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
787 legacy_activity = sipe_ocs2007_legacy_activity_description(availability);
788 if (sbuddy->activity && legacy_activity) {
789 gchar *tmp2 = sbuddy->activity;
791 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity);
792 g_free(tmp2);
793 } else if (legacy_activity) {
794 sbuddy->activity = g_strdup(legacy_activity);
797 do_update_status = TRUE;
799 /* calendarData */
800 else if(sipe_strequal(attrVar, "calendarData"))
802 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
803 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
805 if (xn_free_busy) {
806 if (!has_free_busy_cleaned) {
807 has_free_busy_cleaned = TRUE;
809 g_free(sbuddy->cal_start_time);
810 sbuddy->cal_start_time = NULL;
812 g_free(sbuddy->cal_free_busy_base64);
813 sbuddy->cal_free_busy_base64 = NULL;
815 g_free(sbuddy->cal_free_busy);
816 sbuddy->cal_free_busy = NULL;
818 sbuddy->cal_free_busy_published = publish_time;
821 if (publish_time >= sbuddy->cal_free_busy_published) {
822 g_free(sbuddy->cal_start_time);
823 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
825 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
826 15 : 0;
828 g_free(sbuddy->cal_free_busy_base64);
829 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
831 g_free(sbuddy->cal_free_busy);
832 sbuddy->cal_free_busy = NULL;
834 sbuddy->cal_free_busy_published = publish_time;
836 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);
840 if (xn_working_hours) {
841 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
846 if (do_update_status) {
847 guint activity;
849 if (status) {
850 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
851 activity = sipe_status_token_to_activity(status);
852 } else {
853 /* no status category in this update,
854 using contact's current status */
855 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
856 uri);
859 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity);
862 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
864 sipe_xml_free(xn_categories);
867 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
868 const gchar *uri,
869 const gchar *activity,
870 gboolean is_online)
872 if (is_online) {
873 const gchar *status_id = NULL;
874 if (activity) {
875 if (sipe_strequal(activity,
876 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
877 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
878 } else if (sipe_strequal(activity,
879 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
880 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
884 if (!status_id) {
885 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
888 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
889 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
890 sipe_status_token_to_activity(status_id));
891 } else {
892 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
893 SIPE_ACTIVITY_OFFLINE);
897 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
898 const gchar *data,
899 unsigned len)
901 gchar *uri;
902 gchar *getbasic;
903 gchar *activity = NULL;
904 sipe_xml *pidf;
905 const sipe_xml *basicstatus = NULL, *tuple, *status;
906 gboolean isonline = FALSE;
907 const sipe_xml *display_name_node;
909 pidf = sipe_xml_parse(data, len);
910 if (!pidf) {
911 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
912 return;
915 if ((tuple = sipe_xml_child(pidf, "tuple")))
917 if ((status = sipe_xml_child(tuple, "status"))) {
918 basicstatus = sipe_xml_child(status, "basic");
922 if (!basicstatus) {
923 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
924 sipe_xml_free(pidf);
925 return;
928 getbasic = sipe_xml_data(basicstatus);
929 if (!getbasic) {
930 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
931 sipe_xml_free(pidf);
932 return;
935 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
936 if (strstr(getbasic, "open")) {
937 isonline = TRUE;
939 g_free(getbasic);
941 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
943 display_name_node = sipe_xml_child(pidf, "display-name");
944 if (display_name_node) {
945 char * display_name = sipe_xml_data(display_name_node);
947 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
948 g_free(display_name);
950 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
953 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
954 if ((status = sipe_xml_child(tuple, "status"))) {
955 if ((basicstatus = sipe_xml_child(status, "activities"))) {
956 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
957 activity = sipe_xml_data(basicstatus);
958 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
964 sipe_buddy_status_from_activity(sipe_private,
965 uri,
966 activity,
967 isonline);
969 g_free(activity);
970 g_free(uri);
971 sipe_xml_free(pidf);
974 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
975 const GSList *fields,
976 const gchar *body,
977 gsize length)
979 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
981 if (strstr(type,"application/rlmi+xml")) {
982 process_incoming_notify_rlmi_resub(user_data, body, length);
983 } else if (strstr(type, "text/xml+msrtc.pidf")) {
984 process_incoming_notify_msrtc(user_data, body, length);
985 } else {
986 process_incoming_notify_rlmi(user_data, body, length);
990 static void sipe_process_presence(struct sipe_core_private *sipe_private,
991 struct sipmsg *msg)
993 const char *ctype = sipmsg_find_header(msg, "Content-Type");
995 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
997 if (ctype &&
998 (strstr(ctype, "application/rlmi+xml") ||
999 strstr(ctype, "application/msrtc-event-categories+xml")))
1001 if (strstr(ctype, "multipart"))
1003 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
1005 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
1007 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
1009 else if(strstr(ctype, "application/rlmi+xml"))
1011 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
1014 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
1016 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
1018 else
1020 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
1025 * Fires on deregistration event initiated by server.
1026 * [MS-SIPREGE] SIP extension.
1028 * OCS2007 Example
1030 * Content-Type: text/registration-event
1031 * subscription-state: terminated;expires=0
1032 * ms-diagnostics-public: 4141;reason="User disabled"
1034 * deregistered;event=rejected
1036 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
1037 struct sipmsg *msg)
1039 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1040 gchar *event = NULL;
1041 gchar *reason = NULL;
1042 gchar *warning;
1044 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1046 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1047 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1048 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1049 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1050 } else {
1051 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1052 return;
1055 reason = sipmsg_get_ms_diagnostics_reason(msg);
1056 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1057 if (!reason) { // for LCS2005
1058 if (event && sipe_strcase_equal(event, "unregistered")) {
1059 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1060 reason = g_strdup(_("you are already signed in at another location"));
1061 } else if (event && sipe_strcase_equal(event, "rejected")) {
1062 reason = g_strdup(_("user disabled")); // [MS-OCER]
1063 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1064 reason = g_strdup(_("user moved")); // [MS-OCER]
1067 g_free(event);
1068 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1069 g_free(reason);
1071 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1072 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1073 warning);
1074 g_free(warning);
1078 /* Replace "~" with localized version of "Other Contacts" */
1079 static const gchar *get_group_name(const sipe_xml *node)
1081 const gchar *name = sipe_xml_attribute(node, "name");
1082 return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name);
1085 static void add_new_group(struct sipe_core_private *sipe_private,
1086 const sipe_xml *node)
1088 struct sipe_group *group = g_new0(struct sipe_group, 1);
1090 group->name = g_strdup(get_group_name(node));
1091 group->id = (int)g_ascii_strtod(sipe_xml_attribute(node, "id"), NULL);
1093 sipe_group_add(sipe_private, group);
1096 static void add_new_buddy(struct sipe_core_private *sipe_private,
1097 const sipe_xml *node,
1098 const gchar *uri,
1099 const gchar *alias)
1101 const gchar *name = sipe_xml_attribute(node, "name");
1102 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1103 gchar *normalized_uri = g_ascii_strdown(uri, -1);
1104 struct sipe_buddy *buddy = NULL;
1105 gchar *tmp;
1106 gchar **item_groups;
1107 int i = 0;
1109 /* assign to group Other Contacts if nothing else received */
1110 tmp = g_strdup(sipe_xml_attribute(node, "groups"));
1111 if (is_empty(tmp)) {
1112 struct sipe_group *group = sipe_group_find_by_name(sipe_private,
1113 _("Other Contacts"));
1114 g_free(tmp);
1115 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1117 item_groups = g_strsplit(tmp, " ", 0);
1118 g_free(tmp);
1120 while (item_groups[i]) {
1121 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1122 g_ascii_strtod(item_groups[i],
1123 NULL));
1125 /* If couldn't find the right group for this contact, */
1126 /* then just put it in the first group we have */
1127 if ((group == NULL) &&
1128 (g_slist_length(sipe_private->groups) > 0))
1129 group = sipe_private->groups->data;
1131 if (group) {
1132 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1133 normalized_uri,
1134 group->name);
1135 gchar *b_alias;
1137 if (!b) {
1138 b = sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1139 normalized_uri,
1140 alias,
1141 group->name);
1142 SIPE_DEBUG_INFO("Created new buddy %s with alias %s",
1143 normalized_uri, alias);
1146 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b);
1147 if (sipe_strcase_equal(alias, b_alias) &&
1148 !is_empty(name)) {
1149 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1151 name);
1152 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1153 normalized_uri, group->name, b_alias, name);
1155 g_free(b_alias);
1157 if (!buddy)
1158 buddy = sipe_buddy_add(sipe_private,
1159 normalized_uri,
1160 NULL);
1162 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
1163 group,
1164 (GCompareFunc)sipe_group_compare,
1165 NULL);
1167 SIPE_DEBUG_INFO("Added buddy %s to group %s",
1168 buddy->name, group->name);
1169 } else {
1170 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1171 name);
1174 i++;
1177 g_strfreev(item_groups);
1178 g_free(normalized_uri);
1181 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1182 struct sipmsg *msg)
1184 int len = msg->bodylen;
1186 const gchar *tmp = sipmsg_find_header(msg, "Event");
1187 const sipe_xml *item;
1188 sipe_xml *isc;
1189 guint delta;
1190 const sipe_xml *group_node;
1192 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1193 return FALSE;
1196 /* Convert the contact from XML to backend Buddies */
1197 isc = sipe_xml_parse(msg->body, len);
1198 if (!isc) {
1199 return FALSE;
1202 /* [MS-SIP]: deltaNum MUST be non-zero */
1203 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1204 if (delta) {
1205 sipe_private->deltanum_contacts = delta;
1209 * Process whole buddy list
1211 * - Only sent once
1212 * * up to Lync 2010
1213 * * Lync 2013 (and later) with buddy list not migrated
1215 * - Lync 2013 with buddy list migrated to Unified Contact Store (UCS)
1216 * * Notify piggy-backed on SUBSCRIBE response with empty list
1217 * * NOTIFY send by server with standard list (ignored by us)
1219 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1220 const gchar *ucsmode = sipe_xml_attribute(isc, "ucsmode");
1222 SIPE_CORE_PRIVATE_FLAG_UNSET(LYNC2013);
1223 if (ucsmode) {
1224 gboolean migrated = sipe_strcase_equal(ucsmode,
1225 "migrated");
1226 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013);
1227 SIPE_DEBUG_INFO_NOFORMAT("contact list contains 'ucsmode' attribute (indicates Lync 2013+)");
1229 if (migrated)
1230 SIPE_DEBUG_INFO_NOFORMAT("contact list has been migrated to Unified Contact Store (UCS)");
1231 sipe_ucs_init(sipe_private, migrated);
1234 if (!sipe_ucs_is_migrated(sipe_private)) {
1235 /* Start processing contact list */
1236 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1238 /* Parse groups */
1239 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node))
1240 add_new_group(sipe_private, group_node);
1242 /* Make sure we have at least one group */
1243 if (g_slist_length(sipe_private->groups) == 0) {
1244 sipe_group_create(sipe_private, _("Other Contacts"), NULL);
1247 /* Parse contacts */
1248 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1249 const gchar *name = sipe_xml_attribute(item, "uri");
1250 gchar *uri = sip_uri_from_name(name);
1251 add_new_buddy(sipe_private, item, uri, name);
1252 g_free(uri);
1255 sipe_buddy_cleanup_local_list(sipe_private);
1257 /* Add self-contact if not there yet. 2005 systems. */
1258 /* This will resemble subscription to roaming_self in 2007 systems */
1259 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1260 gchar *self_uri = sip_uri_self(sipe_private);
1261 sipe_buddy_add(sipe_private, self_uri, NULL);
1262 g_free(self_uri);
1265 /* Finished processing contact list */
1266 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1269 /* Process buddy list updates */
1270 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1272 /* Process new groups */
1273 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1274 add_new_group(sipe_private, group_node);
1276 /* Process modified groups */
1277 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1278 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1279 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1280 NULL));
1281 if (group) {
1282 const gchar *name = get_group_name(group_node);
1284 if (!(is_empty(name) ||
1285 sipe_strequal(group->name, name)) &&
1286 sipe_group_rename(sipe_private,
1287 group,
1288 name))
1289 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1293 /* Process new buddies */
1294 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1295 const gchar *uri = sipe_xml_attribute(item, "uri");
1296 const gchar *name = sipe_get_no_sip_uri(uri);
1297 add_new_buddy(sipe_private, item, uri, name);
1300 /* Process modified buddies */
1301 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1302 const gchar *uri = sipe_xml_attribute(item, "uri");
1303 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1304 uri);
1306 if (buddy) {
1307 gchar **item_groups = g_strsplit(sipe_xml_attribute(item,
1308 "groups"),
1309 " ", 0);
1311 /* this should be defined. Otherwise we would get "deletedContact" */
1312 if (item_groups) {
1313 const gchar *name = sipe_xml_attribute(item, "name");
1314 gboolean empty_name = is_empty(name);
1315 GSList *found = NULL;
1316 GSList *entry;
1317 int i = 0;
1319 while (item_groups[i]) {
1320 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1321 g_ascii_strtod(item_groups[i],
1322 NULL));
1323 /* ignore unkown groups */
1324 if (group) {
1325 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1326 uri,
1327 group->name);
1329 /* add group to found list */
1330 found = g_slist_prepend(found, group);
1332 if (b) {
1333 /* new alias? */
1334 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1337 if (!(empty_name ||
1338 sipe_strequal(b_alias, name))) {
1339 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1341 name);
1342 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1343 uri, group->name, b_alias, name);
1345 g_free(b_alias);
1347 } else {
1348 const gchar *alias = empty_name ? uri : name;
1349 /* buddy was not in this group */
1350 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1351 uri,
1352 alias,
1353 group->name);
1354 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
1355 group,
1356 (GCompareFunc) sipe_group_compare,
1357 NULL);
1358 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1359 uri, alias, group->name);
1363 /* next group */
1364 i++;
1366 g_strfreev(item_groups);
1368 /* removed from groups? */
1369 entry = buddy->groups;
1370 while (entry) {
1371 GSList *remove_link = entry;
1372 struct sipe_group *group = remove_link->data;
1374 /* next buddy group */
1375 entry = entry->next;
1377 /* old group NOT found in new list? */
1378 if (g_slist_find(found, group) == NULL) {
1379 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1380 uri,
1381 group->name);
1382 SIPE_DEBUG_INFO("Removing buddy %s from group '%s'",
1383 uri, group->name);
1384 /* this should never be NULL */
1385 if (oldb)
1386 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1387 oldb);
1388 buddy->groups = g_slist_remove_link(buddy->groups,
1389 remove_link);
1392 g_slist_free(found);
1397 /* Process deleted buddies */
1398 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1399 const gchar *uri = sipe_xml_attribute(item, "uri");
1400 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1401 uri);
1403 if (buddy) {
1404 GSList *entry = buddy->groups;
1406 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1407 while (entry) {
1408 struct sipe_group *group = entry->data;
1409 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1410 uri,
1411 group->name);
1412 /* this should never be NULL */
1413 if (oldb)
1414 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1415 oldb);
1417 /* next buddy group */
1418 entry = entry->next;
1420 sipe_buddy_remove(sipe_private, buddy);
1424 /* Process deleted groups
1426 * NOTE: all buddies will already have been removed from the
1427 * group prior to this. The log shows that OCS actually
1428 * sends two separate updates when you delete a group:
1430 * - first one with "modifiedContact" removing buddies
1431 * from the group, leaving it empty, and
1433 * - then one with "deletedGroup" removing the group
1435 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1436 sipe_group_remove(sipe_private,
1437 sipe_group_find_by_id(sipe_private,
1438 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1439 NULL)));
1442 sipe_xml_free(isc);
1444 /* Subscribe to buddies, if contact list not migrated to UCS */
1445 if (!sipe_ucs_is_migrated(sipe_private))
1446 sipe_subscribe_presence_initial(sipe_private);
1448 /* for 2005 systems schedule contacts' status update
1449 * based on their calendar information
1451 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1452 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1455 return 0;
1458 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1459 struct sipmsg *msg)
1461 guint delta;
1462 sipe_xml *xml;
1464 xml = sipe_xml_parse(msg->body, msg->bodylen);
1465 if (!xml)
1466 return;
1468 /* [MS-SIP]: deltaNum MUST be non-zero */
1469 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1470 if (delta) {
1471 sipe_private->deltanum_acl = delta;
1474 sipe_xml_free(xml);
1477 struct sipe_auth_job {
1478 gchar *who;
1479 struct sipe_core_private *sipe_private;
1482 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1483 const gchar* who,
1484 gboolean allow)
1486 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1488 if (allow) {
1489 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1490 } else {
1491 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1494 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1495 sipe_ocs2007_change_access_level(sipe_private,
1496 (allow ? -1 : 32000),
1497 "user",
1498 sipe_get_no_sip_uri(who));
1499 } else {
1500 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1505 static void sipe_auth_user_cb(gpointer data)
1507 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1508 if (!job) return;
1510 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1511 job->who,
1512 TRUE);
1513 g_free(job);
1516 static void sipe_deny_user_cb(gpointer data)
1518 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1519 if (!job) return;
1521 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1522 job->who,
1523 FALSE);
1524 g_free(job);
1527 /* OCS2005- */
1528 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1529 struct sipmsg * msg)
1531 sipe_xml *watchers;
1532 const sipe_xml *watcher;
1533 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1534 if (msg->response != 0 && msg->response != 200) return;
1536 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1538 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1539 if (!watchers) return;
1541 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1542 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1543 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1544 gboolean on_list = sipe_buddy_find_by_uri(sipe_private, remote_user) != NULL;
1546 // TODO pull out optional displayName to pass as alias
1547 if (remote_user) {
1548 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1549 job->who = remote_user;
1550 job->sipe_private = sipe_private;
1551 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1552 remote_user,
1553 alias,
1554 on_list,
1555 sipe_auth_user_cb,
1556 sipe_deny_user_cb,
1557 (gpointer)job);
1562 sipe_xml_free(watchers);
1563 return;
1567 * Dispatcher for all incoming subscription information
1568 * whether it comes from NOTIFY, BENOTIFY requests or
1569 * piggy-backed to subscription's OK responce.
1571 void process_incoming_notify(struct sipe_core_private *sipe_private,
1572 struct sipmsg *msg)
1574 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1575 const gchar *event = sipmsg_find_header(msg, "Event");
1576 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1578 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1580 /* implicit subscriptions */
1581 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1582 sipe_process_imdn(sipe_private, msg);
1584 /* event subscriptions */
1585 } else if (event) {
1587 /* One-off subscriptions - sent with "Expires: 0" */
1588 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) {
1589 sipe_process_provisioning_v2(sipe_private, msg);
1590 } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) {
1591 sipe_process_provisioning(sipe_private, msg);
1592 } else if (sipe_strcase_equal(event, "presence")) {
1593 sipe_process_presence(sipe_private, msg);
1594 } else if (sipe_strcase_equal(event, "registration-notify")) {
1595 sipe_process_registration_notify(sipe_private, msg);
1597 /* Subscriptions with timeout */
1598 } else if (!subscription_state || strstr(subscription_state, "active")) {
1599 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
1600 sipe_process_roaming_contacts(sipe_private, msg);
1601 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) {
1602 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1603 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) {
1604 sipe_process_roaming_acl(sipe_private, msg);
1605 } else if (sipe_strcase_equal(event, "presence.wpending")) {
1606 sipe_process_presence_wpending(sipe_private, msg);
1607 } else if (sipe_strcase_equal(event, "conference")) {
1608 sipe_process_conference(sipe_private, msg);
1615 Local Variables:
1616 mode: c
1617 c-file-style: "bsd"
1618 indent-tabs-mode: t
1619 tab-width: 8
1620 End: