Feature #73: Support buddy photos from contactCard
[siplcs.git] / src / core / sipe-notify.c
blobbbfef3d393f842d733886fd355f5b9ab3d01e657
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2014 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Process incoming SIP NOTIFY/BENOTIFY messages
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include "sipmsg.h"
38 #include "sip-csta.h"
39 #include "sip-soap.h"
40 #include "sipe-backend.h"
41 #include "sipe-buddy.h"
42 #include "sipe-cal.h"
43 #include "sipe-conf.h"
44 #include "sipe-core.h"
45 #include "sipe-core-private.h"
46 #include "sipe-group.h"
47 #include "sipe-groupchat.h"
48 #include "sipe-media.h"
49 #include "sipe-mime.h"
50 #include "sipe-nls.h"
51 #include "sipe-notify.h"
52 #include "sipe-ocs2005.h"
53 #include "sipe-ocs2007.h"
54 #include "sipe-status.h"
55 #include "sipe-subscriptions.h"
56 #include "sipe-ucs.h"
57 #include "sipe-utils.h"
58 #include "sipe-xml.h"
60 /* OCS2005 */
61 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
62 struct sipmsg *msg)
64 sipe_xml *xn_provision;
65 const sipe_xml *node;
67 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
68 if ((node = sipe_xml_child(xn_provision, "user"))) {
69 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
70 if ((node = sipe_xml_child(node, "line"))) {
71 const gchar *line_uri = sipe_xml_attribute(node, "uri");
72 const gchar *server = sipe_xml_attribute(node, "server");
73 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
74 sip_csta_open(sipe_private, line_uri, server);
77 sipe_xml_free(xn_provision);
80 /* OCS2007+ */
81 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
82 struct sipmsg *msg)
84 sipe_xml *xn_provision_group_list;
85 const sipe_xml *node;
87 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
89 /* provisionGroup */
90 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup");
91 node;
92 node = sipe_xml_twin(node)) {
93 const gchar *node_name = sipe_xml_attribute(node, "name");
95 /* ServerConfiguration */
96 if (sipe_strequal("ServerConfiguration", node_name)) {
97 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
98 "dlxExternalUrl" : "dlxInternalUrl";
99 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
100 "absExternalServerUrl" : "absInternalServerUrl";
102 g_free(sipe_private->focus_factory_uri);
103 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
104 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
105 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
107 g_free(sipe_private->dlx_uri);
108 sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
109 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s",
110 sipe_private->dlx_uri ? sipe_private->dlx_uri : "");
112 g_free(sipe_private->addressbook_uri);
113 sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str));
114 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s",
115 sipe_private->addressbook_uri ? sipe_private->addressbook_uri : "");
117 #ifdef HAVE_VV
118 g_free(sipe_private->test_call_bot_uri);
119 sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall"));
120 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s",
121 sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : "");
123 g_free(sipe_private->mras_uri);
124 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
125 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
126 sipe_private->mras_uri ? sipe_private->mras_uri : "");
128 if (sipe_private->mras_uri)
129 sipe_media_get_av_edge_credentials(sipe_private);
130 #endif
132 /* persistentChatConfiguration */
133 } else if (sipe_strequal("persistentChatConfiguration", node_name)) {
134 const sipe_xml *property;
135 gboolean enabled = FALSE;
136 gchar *uri = NULL;
138 for (property = sipe_xml_child(node, "propertyEntryList/property");
139 property;
140 property = sipe_xml_twin(property)) {
141 const gchar *name = sipe_xml_attribute(property, "name");
142 gchar *value = sipe_xml_data(property);
144 if (sipe_strequal(name, "EnablePersistentChat")) {
145 enabled = sipe_strequal(value, "true");
147 } else if (sipe_strequal(name, "DefaultPersistentChatPoolUri")) {
148 g_free(uri);
149 uri = value;
150 value = NULL;
152 g_free(value);
155 if (enabled) {
156 g_free(sipe_private->persistentChatPool_uri);
157 sipe_private->persistentChatPool_uri = g_strdup(sipe_get_no_sip_uri(uri));
158 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->persistentChatPool_uri=%s",
159 sipe_private->persistentChatPool_uri ? sipe_private->persistentChatPool_uri : "");
161 g_free(uri);
165 sipe_xml_free(xn_provision_group_list);
167 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
168 /* Some buddies might have been added before we received this
169 * provisioning notify with DLX and addressbook URIs. Now we can
170 * trigger an update of their photos. */
171 sipe_buddy_refresh_photos(sipe_private);
174 if (sipe_private->focus_factory_uri) {
175 /* Fill the list of conferencing capabilities enabled on
176 * the server. */
177 sipe_conf_get_capabilities(sipe_private);
180 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
181 /* persistentChatPool_uri has been set at this point */
182 sipe_groupchat_init(sipe_private);
185 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
186 const gchar *data, unsigned len)
188 sipe_xml *xn_list;
189 const sipe_xml *xn_resource;
190 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
191 g_free, NULL);
193 xn_list = sipe_xml_parse(data, len);
195 for (xn_resource = sipe_xml_child(xn_list, "resource");
196 xn_resource;
197 xn_resource = sipe_xml_twin(xn_resource) )
199 const char *uri, *state;
200 const sipe_xml *xn_instance;
202 xn_instance = sipe_xml_child(xn_resource, "instance");
203 if (!xn_instance) continue;
205 uri = sipe_xml_attribute(xn_resource, "uri");
206 state = sipe_xml_attribute(xn_instance, "state");
207 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
209 if (strstr(state, "resubscribe")) {
210 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
212 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
213 gchar *user = g_strdup(uri);
214 gchar *host = g_strdup(poolFqdn);
215 GSList *server = g_hash_table_lookup(servers,
216 host);
217 server = g_slist_append(server, user);
218 g_hash_table_insert(servers, host, server);
219 } else {
220 sipe_subscribe_presence_single(sipe_private,
221 uri,
222 uri);
227 /* Send out any deferred poolFqdn subscriptions */
228 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
229 g_hash_table_destroy(servers);
231 sipe_xml_free(xn_list);
235 * Update user phone
236 * Suitable for both 2005 and 2007 systems.
238 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
239 * @param phone_type
240 * @param phone may be modified to strip white space
241 * @param phone_display_string may be modified to strip white space
243 static void
244 sipe_update_user_phone(struct sipe_core_private *sipe_private,
245 const gchar *uri,
246 const gchar *phone_type,
247 gchar *phone,
248 gchar *phone_display_string)
250 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
251 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
253 if(!phone || strlen(phone) == 0) return;
255 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
256 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
257 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
258 } else if (sipe_strequal(phone_type, "home")) {
259 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
260 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
261 } else if (sipe_strequal(phone_type, "other")) {
262 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
263 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
264 } else if (sipe_strequal(phone_type, "custom1")) {
265 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
266 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
269 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
270 if (phone_display_string) {
271 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
275 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
276 const gchar *data,
277 unsigned len)
279 char *activity = NULL;
280 const char *epid;
281 const char *status_id = NULL;
282 const char *name;
283 char *uri;
284 char *self_uri = sip_uri_self(sipe_private);
285 int avl;
286 int act;
287 const char *device_name = NULL;
288 const char *cal_start_time = NULL;
289 const char *cal_granularity = NULL;
290 char *cal_free_busy_base64 = NULL;
291 struct sipe_buddy *sbuddy;
292 const sipe_xml *node;
293 sipe_xml *xn_presentity;
294 const sipe_xml *xn_availability;
295 const sipe_xml *xn_activity;
296 const sipe_xml *xn_display_name;
297 const sipe_xml *xn_email;
298 const sipe_xml *xn_phone_number;
299 const sipe_xml *xn_userinfo;
300 const sipe_xml *xn_note;
301 const sipe_xml *xn_oof;
302 const sipe_xml *xn_state;
303 const sipe_xml *xn_contact;
304 char *note;
305 int user_avail;
306 const char *user_avail_nil;
307 int res_avail;
308 time_t user_avail_since = 0;
309 time_t activity_since = 0;
311 /* fix for Reuters environment on Linux */
312 if (data && strstr(data, "encoding=\"utf-16\"")) {
313 char *tmp_data;
314 tmp_data = sipe_utils_str_replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
315 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
316 g_free(tmp_data);
317 } else {
318 xn_presentity = sipe_xml_parse(data, len);
321 xn_availability = sipe_xml_child(xn_presentity, "availability");
322 xn_activity = sipe_xml_child(xn_presentity, "activity");
323 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
324 xn_email = sipe_xml_child(xn_presentity, "email");
325 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
326 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
327 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
328 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
329 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
330 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
331 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
332 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
333 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
334 note = xn_note ? sipe_xml_data(xn_note) : NULL;
336 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
337 user_avail = 0;
338 user_avail_since = 0;
341 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
342 uri = sip_uri_from_name(name);
343 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
344 epid = sipe_xml_attribute(xn_availability, "epid");
345 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
347 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
348 activity = g_strdup(sipe_ocs2005_activity_description(act));
349 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
350 if (user_avail > res_avail) {
351 res_avail = user_avail;
352 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
355 if (xn_display_name) {
356 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
357 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
358 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
359 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
360 char *tel_uri = sip_to_tel_uri(phone_number);
362 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
363 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
364 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
365 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
367 g_free(tel_uri);
368 g_free(phone_label);
369 g_free(phone_number);
370 g_free(email);
371 g_free(display_name);
374 if (xn_contact) {
375 /* tel */
376 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
378 /* Ex.: <tel type="work">tel:+3222220000</tel> */
379 const char *phone_type = sipe_xml_attribute(node, "type");
380 char* phone = sipe_xml_data(node);
382 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
384 g_free(phone);
388 if (xn_display_name || xn_contact)
389 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
391 /* devicePresence */
392 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
393 const sipe_xml *xn_device_name;
394 const sipe_xml *xn_calendar_info;
395 const sipe_xml *xn_state;
396 char *state;
398 /* deviceName */
399 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
400 xn_device_name = sipe_xml_child(node, "deviceName");
401 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
404 /* calendarInfo */
405 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
406 if (xn_calendar_info) {
407 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
409 if (cal_start_time) {
410 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
411 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
413 if (cal_start_time_t_tmp > cal_start_time_t) {
414 cal_start_time = cal_start_time_tmp;
415 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
416 g_free(cal_free_busy_base64);
417 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
419 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);
421 } else {
422 cal_start_time = cal_start_time_tmp;
423 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
424 g_free(cal_free_busy_base64);
425 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
427 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);
431 /* state */
432 xn_state = sipe_xml_child(node, "states/state");
433 if (xn_state) {
434 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
435 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
437 state = sipe_xml_data(xn_state);
438 if (dev_avail_since > user_avail_since &&
439 dev_avail >= res_avail)
441 const gchar *new_desc;
442 res_avail = dev_avail;
443 if (!is_empty(state)) {
444 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
445 g_free(activity);
446 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
447 } else if (sipe_strequal(state, "presenting")) {
448 g_free(activity);
449 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
450 } else {
451 activity = state;
452 state = NULL;
454 activity_since = dev_avail_since;
456 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
457 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
458 if (new_desc) {
459 g_free(activity);
460 activity = g_strdup(new_desc);
463 g_free(state);
467 /* oof */
468 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
469 g_free(activity);
470 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
471 activity_since = 0;
474 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
475 if (sbuddy)
477 g_free(sbuddy->activity);
478 sbuddy->activity = activity;
479 activity = NULL;
481 sbuddy->activity_since = activity_since;
483 sbuddy->user_avail = user_avail;
484 sbuddy->user_avail_since = user_avail_since;
486 g_free(sbuddy->note);
487 sbuddy->note = NULL;
488 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
490 sbuddy->is_oof_note = (xn_oof != NULL);
492 g_free(sbuddy->device_name);
493 sbuddy->device_name = NULL;
494 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
496 if (!is_empty(cal_free_busy_base64)) {
497 g_free(sbuddy->cal_start_time);
498 sbuddy->cal_start_time = g_strdup(cal_start_time);
500 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
502 g_free(sbuddy->cal_free_busy_base64);
503 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
504 cal_free_busy_base64 = NULL;
506 g_free(sbuddy->cal_free_busy);
507 sbuddy->cal_free_busy = NULL;
510 sbuddy->last_non_cal_status_id = status_id;
511 g_free(sbuddy->last_non_cal_activity);
512 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
514 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
515 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
517 if (sbuddy->is_oof_note)
518 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
519 else
520 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
522 g_free(sipe_private->note);
523 sipe_private->note = g_strdup(sbuddy->note);
525 sipe_private->note_since = time(NULL);
528 sipe_status_set_token(sipe_private,
529 sbuddy->last_non_cal_status_id);
532 g_free(cal_free_busy_base64);
533 g_free(activity);
535 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
536 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
537 sipe_status_token_to_activity(status_id));
539 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
540 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
543 g_free(note);
544 sipe_xml_free(xn_presentity);
545 g_free(uri);
546 g_free(self_uri);
549 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
550 const gchar *data,
551 unsigned len)
553 const char *uri;
554 struct sipe_buddy *sbuddy = NULL;
555 sipe_xml *xn_categories;
556 const sipe_xml *xn_category;
557 const char *status = NULL;
558 gboolean do_update_status = FALSE;
559 gboolean has_note_cleaned = FALSE;
560 gboolean has_free_busy_cleaned = FALSE;
562 xn_categories = sipe_xml_parse(data, len);
563 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
564 if (uri) {
565 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
568 if (!sbuddy) {
569 /* Got presence of a buddy not in our contact list, ignore. */
570 sipe_xml_free(xn_categories);
571 return;
574 for (xn_category = sipe_xml_child(xn_categories, "category");
575 xn_category ;
576 xn_category = sipe_xml_twin(xn_category) )
578 const sipe_xml *xn_node;
579 const char *tmp;
580 const char *attrVar = sipe_xml_attribute(xn_category, "name");
581 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
582 sipe_utils_str_to_time(tmp) : 0;
584 /* contactCard */
585 if (sipe_strequal(attrVar, "contactCard"))
587 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
589 if (card) {
590 const sipe_xml *node;
591 /* identity - Display Name and email */
592 node = sipe_xml_child(card, "identity");
593 if (node) {
594 char* display_name = sipe_xml_data(
595 sipe_xml_child(node, "name/displayName"));
596 char* email = sipe_xml_data(
597 sipe_xml_child(node, "email"));
599 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
600 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
602 g_free(display_name);
603 g_free(email);
605 /* company */
606 node = sipe_xml_child(card, "company");
607 if (node) {
608 char* company = sipe_xml_data(node);
609 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
610 g_free(company);
612 /* department */
613 node = sipe_xml_child(card, "department");
614 if (node) {
615 char* department = sipe_xml_data(node);
616 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
617 g_free(department);
619 /* title */
620 node = sipe_xml_child(card, "title");
621 if (node) {
622 char* title = sipe_xml_data(node);
623 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
624 g_free(title);
626 /* office */
627 node = sipe_xml_child(card, "office");
628 if (node) {
629 char* office = sipe_xml_data(node);
630 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
631 g_free(office);
633 /* site (url) */
634 node = sipe_xml_child(card, "url");
635 if (node) {
636 char* site = sipe_xml_data(node);
637 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
638 g_free(site);
640 /* phone */
641 for (node = sipe_xml_child(card, "phone");
642 node;
643 node = sipe_xml_twin(node))
645 const char *phone_type = sipe_xml_attribute(node, "type");
646 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
647 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
649 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
651 g_free(phone);
652 g_free(phone_display_string);
654 /* address */
655 for (node = sipe_xml_child(card, "address");
656 node;
657 node = sipe_xml_twin(node))
659 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
660 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
661 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
662 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
663 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
664 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
666 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
667 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
668 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
669 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
670 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
672 g_free(street);
673 g_free(city);
674 g_free(state);
675 g_free(zipcode);
676 g_free(country_code);
678 break;
681 /* photo */
682 for (node = sipe_xml_child(card, "photo");
683 node;
684 node = sipe_xml_twin(node)) {
685 gchar *photo_url = sipe_xml_data(sipe_xml_child(node, "uri"));
686 gchar *hash = sipe_xml_data(sipe_xml_child(node, "hash"));
687 gboolean found = FALSE;
689 if (!is_empty(uri) && !is_empty(hash)) {
690 sipe_buddy_update_photo(sipe_private,
691 uri,
692 photo_url,
693 hash,
694 NULL);
695 found = TRUE;
698 g_free(hash);
699 g_free(photo_url);
701 if (found)
702 break;
706 /* note */
707 else if (sipe_strequal(attrVar, "note"))
709 if (!has_note_cleaned) {
710 has_note_cleaned = TRUE;
712 g_free(sbuddy->note);
713 sbuddy->note = NULL;
714 sbuddy->is_oof_note = FALSE;
715 sbuddy->note_since = publish_time;
717 do_update_status = TRUE;
719 if (publish_time >= sbuddy->note_since) {
720 /* clean up in case no 'note' element is supplied
721 * which indicate note removal in client
723 g_free(sbuddy->note);
724 sbuddy->note = NULL;
725 sbuddy->is_oof_note = FALSE;
726 sbuddy->note_since = publish_time;
728 xn_node = sipe_xml_child(xn_category, "note/body");
729 if (xn_node) {
730 char *tmp;
731 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
732 g_free(tmp);
733 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
734 sbuddy->note_since = publish_time;
736 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
737 uri, sbuddy->note ? sbuddy->note : "");
739 /* to trigger UI refresh in case no status info is supplied in this update */
740 do_update_status = TRUE;
743 /* state */
744 else if(sipe_strequal(attrVar, "state"))
746 char *tmp;
747 int availability;
748 const sipe_xml *xn_availability;
749 const sipe_xml *xn_activity;
750 const sipe_xml *xn_device;
751 const sipe_xml *xn_meeting_subject;
752 const sipe_xml *xn_meeting_location;
753 const gchar *legacy_activity;
755 xn_node = sipe_xml_child(xn_category, "state");
756 if (!xn_node) continue;
757 xn_availability = sipe_xml_child(xn_node, "availability");
758 if (!xn_availability) continue;
759 xn_activity = sipe_xml_child(xn_node, "activity");
760 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
761 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
763 tmp = sipe_xml_data(xn_availability);
764 availability = atoi(tmp);
765 g_free(tmp);
767 sbuddy->is_mobile = FALSE;
768 xn_device = sipe_xml_child(xn_node, "device");
769 if (xn_device) {
770 tmp = sipe_xml_data(xn_device);
771 sbuddy->is_mobile = !g_ascii_strcasecmp(tmp, "Mobile");
772 g_free(tmp);
775 /* activity */
776 g_free(sbuddy->activity);
777 sbuddy->activity = NULL;
778 if (xn_activity) {
779 const char *token = sipe_xml_attribute(xn_activity, "token");
780 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
782 /* from token */
783 if (!is_empty(token)) {
784 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
786 /* from custom element */
787 if (xn_custom) {
788 char *custom = sipe_xml_data(xn_custom);
790 if (!is_empty(custom)) {
791 g_free(sbuddy->activity);
792 sbuddy->activity = custom;
793 custom = NULL;
795 g_free(custom);
798 /* meeting_subject */
799 g_free(sbuddy->meeting_subject);
800 sbuddy->meeting_subject = NULL;
801 if (xn_meeting_subject) {
802 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
804 if (!is_empty(meeting_subject)) {
805 sbuddy->meeting_subject = meeting_subject;
806 meeting_subject = NULL;
808 g_free(meeting_subject);
810 /* meeting_location */
811 g_free(sbuddy->meeting_location);
812 sbuddy->meeting_location = NULL;
813 if (xn_meeting_location) {
814 char *meeting_location = sipe_xml_data(xn_meeting_location);
816 if (!is_empty(meeting_location)) {
817 sbuddy->meeting_location = meeting_location;
818 meeting_location = NULL;
820 g_free(meeting_location);
823 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
824 legacy_activity = sipe_ocs2007_legacy_activity_description(availability);
825 if (sbuddy->activity && legacy_activity) {
826 gchar *tmp2 = sbuddy->activity;
828 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity);
829 g_free(tmp2);
830 } else if (legacy_activity) {
831 sbuddy->activity = g_strdup(legacy_activity);
834 do_update_status = TRUE;
836 /* calendarData */
837 else if(sipe_strequal(attrVar, "calendarData"))
839 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
840 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
842 if (xn_free_busy) {
843 if (!has_free_busy_cleaned) {
844 has_free_busy_cleaned = TRUE;
846 g_free(sbuddy->cal_start_time);
847 sbuddy->cal_start_time = NULL;
849 g_free(sbuddy->cal_free_busy_base64);
850 sbuddy->cal_free_busy_base64 = NULL;
852 g_free(sbuddy->cal_free_busy);
853 sbuddy->cal_free_busy = NULL;
855 sbuddy->cal_free_busy_published = publish_time;
858 if (publish_time >= sbuddy->cal_free_busy_published) {
859 g_free(sbuddy->cal_start_time);
860 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
862 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
863 15 : 0;
865 g_free(sbuddy->cal_free_busy_base64);
866 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
868 g_free(sbuddy->cal_free_busy);
869 sbuddy->cal_free_busy = NULL;
871 sbuddy->cal_free_busy_published = publish_time;
873 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);
877 if (xn_working_hours) {
878 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
883 if (do_update_status) {
884 guint activity;
886 if (status) {
887 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
888 activity = sipe_status_token_to_activity(status);
889 } else {
890 /* no status category in this update,
891 using contact's current status */
892 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
893 uri);
896 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity);
899 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
901 sipe_xml_free(xn_categories);
904 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
905 const gchar *uri,
906 const gchar *activity,
907 gboolean is_online)
909 if (is_online) {
910 const gchar *status_id = NULL;
911 if (activity) {
912 if (sipe_strequal(activity,
913 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
914 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
915 } else if (sipe_strequal(activity,
916 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
917 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
921 if (!status_id) {
922 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
925 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
926 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
927 sipe_status_token_to_activity(status_id));
928 } else {
929 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
930 SIPE_ACTIVITY_OFFLINE);
934 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
935 const gchar *data,
936 unsigned len)
938 gchar *uri;
939 gchar *getbasic;
940 gchar *activity = NULL;
941 sipe_xml *pidf;
942 const sipe_xml *basicstatus = NULL, *tuple, *status;
943 gboolean isonline = FALSE;
944 const sipe_xml *display_name_node;
946 pidf = sipe_xml_parse(data, len);
947 if (!pidf) {
948 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
949 return;
952 if ((tuple = sipe_xml_child(pidf, "tuple")))
954 if ((status = sipe_xml_child(tuple, "status"))) {
955 basicstatus = sipe_xml_child(status, "basic");
959 if (!basicstatus) {
960 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
961 sipe_xml_free(pidf);
962 return;
965 getbasic = sipe_xml_data(basicstatus);
966 if (!getbasic) {
967 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
968 sipe_xml_free(pidf);
969 return;
972 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
973 if (strstr(getbasic, "open")) {
974 isonline = TRUE;
976 g_free(getbasic);
978 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
980 display_name_node = sipe_xml_child(pidf, "display-name");
981 if (display_name_node) {
982 char * display_name = sipe_xml_data(display_name_node);
984 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
985 g_free(display_name);
987 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
990 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
991 if ((status = sipe_xml_child(tuple, "status"))) {
992 if ((basicstatus = sipe_xml_child(status, "activities"))) {
993 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
994 activity = sipe_xml_data(basicstatus);
995 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
1001 sipe_buddy_status_from_activity(sipe_private,
1002 uri,
1003 activity,
1004 isonline);
1006 g_free(activity);
1007 g_free(uri);
1008 sipe_xml_free(pidf);
1011 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
1012 const GSList *fields,
1013 const gchar *body,
1014 gsize length)
1016 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
1018 if (strstr(type,"application/rlmi+xml")) {
1019 process_incoming_notify_rlmi_resub(user_data, body, length);
1020 } else if (strstr(type, "text/xml+msrtc.pidf")) {
1021 process_incoming_notify_msrtc(user_data, body, length);
1022 } else {
1023 process_incoming_notify_rlmi(user_data, body, length);
1027 static void sipe_process_presence(struct sipe_core_private *sipe_private,
1028 struct sipmsg *msg)
1030 const char *ctype = sipmsg_find_header(msg, "Content-Type");
1032 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
1034 if (ctype &&
1035 (strstr(ctype, "application/rlmi+xml") ||
1036 strstr(ctype, "application/msrtc-event-categories+xml")))
1038 if (strstr(ctype, "multipart"))
1040 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
1042 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
1044 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
1046 else if(strstr(ctype, "application/rlmi+xml"))
1048 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
1051 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
1053 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
1055 else
1057 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
1062 * Fires on deregistration event initiated by server.
1063 * [MS-SIPREGE] SIP extension.
1065 * OCS2007 Example
1067 * Content-Type: text/registration-event
1068 * subscription-state: terminated;expires=0
1069 * ms-diagnostics-public: 4141;reason="User disabled"
1071 * deregistered;event=rejected
1073 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
1074 struct sipmsg *msg)
1076 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1077 gchar *event = NULL;
1078 gchar *reason = NULL;
1079 gchar *warning;
1081 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1083 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1084 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1085 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1086 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1087 } else {
1088 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1089 return;
1092 reason = sipmsg_get_ms_diagnostics_reason(msg);
1093 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1094 if (!reason) { // for LCS2005
1095 if (event && sipe_strcase_equal(event, "unregistered")) {
1096 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1097 reason = g_strdup(_("you are already signed in at another location"));
1098 } else if (event && sipe_strcase_equal(event, "rejected")) {
1099 reason = g_strdup(_("user disabled")); // [MS-OCER]
1100 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1101 reason = g_strdup(_("user moved")); // [MS-OCER]
1104 g_free(event);
1105 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1106 g_free(reason);
1108 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1109 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1110 warning);
1111 g_free(warning);
1115 /* Replace "~" with localized version of "Other Contacts" */
1116 static const gchar *get_group_name(const sipe_xml *node)
1118 const gchar *name = sipe_xml_attribute(node, "name");
1119 return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name);
1122 static void add_new_group(struct sipe_core_private *sipe_private,
1123 const sipe_xml *node)
1125 sipe_group_add(sipe_private,
1126 get_group_name(node),
1127 NULL,
1128 NULL,
1129 sipe_xml_int_attribute(node, "id", 0));
1132 static void add_new_buddy(struct sipe_core_private *sipe_private,
1133 const sipe_xml *node,
1134 const gchar *uri)
1136 const gchar *name = sipe_xml_attribute(node, "name");
1137 struct sipe_buddy *buddy = NULL;
1138 gchar *tmp;
1139 gchar **item_groups;
1140 int i = 0;
1142 /* assign to group Other Contacts if nothing else received */
1143 tmp = g_strdup(sipe_xml_attribute(node, "groups"));
1144 if (is_empty(tmp)) {
1145 struct sipe_group *group = sipe_group_find_by_name(sipe_private,
1146 _("Other Contacts"));
1147 g_free(tmp);
1148 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1150 item_groups = g_strsplit(tmp, " ", 0);
1151 g_free(tmp);
1153 while (item_groups[i]) {
1154 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1155 g_ascii_strtod(item_groups[i],
1156 NULL));
1158 /* If couldn't find the right group for this contact, */
1159 /* then just put it in the first group we have */
1160 if (!group)
1161 group = sipe_group_first(sipe_private);
1163 if (group) {
1164 if (!buddy)
1165 buddy = sipe_buddy_add(sipe_private,
1166 uri,
1167 NULL,
1168 NULL);
1170 sipe_buddy_add_to_group(sipe_private,
1171 buddy,
1172 group,
1173 name);
1174 } else {
1175 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1176 name);
1179 i++;
1182 g_strfreev(item_groups);
1185 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1186 struct sipmsg *msg)
1188 int len = msg->bodylen;
1190 const gchar *tmp = sipmsg_find_header(msg, "Event");
1191 const sipe_xml *item;
1192 sipe_xml *isc;
1193 guint delta;
1194 const sipe_xml *group_node;
1196 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1197 return FALSE;
1200 /* Convert the contact from XML to backend Buddies */
1201 isc = sipe_xml_parse(msg->body, len);
1202 if (!isc) {
1203 return FALSE;
1206 /* [MS-SIP]: deltaNum MUST be non-zero */
1207 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1208 if (delta) {
1209 sipe_private->deltanum_contacts = delta;
1213 * Process whole buddy list
1215 * - Only sent once
1216 * * up to Lync 2010
1217 * * Lync 2013 (and later) with buddy list not migrated
1219 * - Lync 2013 with buddy list migrated to Unified Contact Store (UCS)
1220 * * Notify piggy-backed on SUBSCRIBE response with empty list
1221 * * NOTIFY send by server with standard list (ignored by us)
1223 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1224 const gchar *ucsmode = sipe_xml_attribute(isc, "ucsmode");
1226 SIPE_CORE_PRIVATE_FLAG_UNSET(LYNC2013);
1227 if (ucsmode) {
1228 gboolean migrated = sipe_strcase_equal(ucsmode,
1229 "migrated");
1230 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013);
1231 SIPE_DEBUG_INFO_NOFORMAT("contact list contains 'ucsmode' attribute (indicates Lync 2013+)");
1233 if (migrated)
1234 SIPE_DEBUG_INFO_NOFORMAT("contact list has been migrated to Unified Contact Store (UCS)");
1235 sipe_ucs_init(sipe_private, migrated);
1238 if (!sipe_ucs_is_migrated(sipe_private)) {
1239 /* Start processing contact list */
1240 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1242 /* Parse groups */
1243 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node))
1244 add_new_group(sipe_private, group_node);
1246 /* Make sure we have at least one group */
1247 if (sipe_group_count(sipe_private) == 0) {
1248 sipe_group_create(sipe_private,
1249 NULL,
1250 _("Other Contacts"),
1251 NULL);
1254 /* Parse contacts */
1255 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1256 const gchar *name = sipe_xml_attribute(item, "uri");
1257 gchar *uri = sip_uri_from_name(name);
1258 add_new_buddy(sipe_private, item, uri);
1259 g_free(uri);
1262 sipe_buddy_cleanup_local_list(sipe_private);
1264 /* Add self-contact if not there yet. 2005 systems. */
1265 /* This will resemble subscription to roaming_self in 2007 systems */
1266 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1267 gchar *self_uri = sip_uri_self(sipe_private);
1268 sipe_buddy_add(sipe_private,
1269 self_uri,
1270 NULL,
1271 NULL);
1272 g_free(self_uri);
1275 /* Finished processing contact list */
1276 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1279 /* Process buddy list updates */
1280 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1282 /* Process new groups */
1283 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1284 add_new_group(sipe_private, group_node);
1286 /* Process modified groups */
1287 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1288 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1289 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1290 NULL));
1291 if (group) {
1292 const gchar *name = get_group_name(group_node);
1294 if (!(is_empty(name) ||
1295 sipe_strequal(group->name, name)) &&
1296 sipe_group_rename(sipe_private,
1297 group,
1298 name))
1299 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1303 /* Process new buddies */
1304 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1305 add_new_buddy(sipe_private,
1306 item,
1307 sipe_xml_attribute(item, "uri"));
1310 /* Process modified buddies */
1311 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1312 const gchar *uri = sipe_xml_attribute(item, "uri");
1313 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1314 uri);
1316 if (buddy) {
1317 gchar **item_groups = g_strsplit(sipe_xml_attribute(item,
1318 "groups"),
1319 " ", 0);
1321 /* this should be defined. Otherwise we would get "deletedContact" */
1322 if (item_groups) {
1323 const gchar *name = sipe_xml_attribute(item, "name");
1324 gboolean empty_name = is_empty(name);
1325 GSList *found = NULL;
1326 int i = 0;
1328 while (item_groups[i]) {
1329 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1330 g_ascii_strtod(item_groups[i],
1331 NULL));
1332 /* ignore unkown groups */
1333 if (group) {
1334 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1335 uri,
1336 group->name);
1338 /* add group to found list */
1339 found = g_slist_prepend(found, group);
1341 if (b) {
1342 /* new alias? */
1343 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1346 if (!(empty_name ||
1347 sipe_strequal(b_alias, name))) {
1348 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1350 name);
1351 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1352 uri, group->name, b_alias, name);
1354 g_free(b_alias);
1356 } else {
1357 const gchar *alias = empty_name ? uri : name;
1358 /* buddy was not in this group */
1359 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1360 uri,
1361 alias,
1362 group->name);
1363 sipe_buddy_insert_group(buddy, group);
1364 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1365 uri, alias, group->name);
1369 /* next group */
1370 i++;
1372 g_strfreev(item_groups);
1374 /* removed from groups? */
1375 sipe_buddy_update_groups(sipe_private,
1376 buddy,
1377 found);
1378 g_slist_free(found);
1383 /* Process deleted buddies */
1384 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1385 const gchar *uri = sipe_xml_attribute(item, "uri");
1386 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1387 uri);
1389 if (buddy) {
1390 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1391 sipe_buddy_remove(sipe_private, buddy);
1395 /* Process deleted groups
1397 * NOTE: all buddies will already have been removed from the
1398 * group prior to this. The log shows that OCS actually
1399 * sends two separate updates when you delete a group:
1401 * - first one with "modifiedContact" removing buddies
1402 * from the group, leaving it empty, and
1404 * - then one with "deletedGroup" removing the group
1406 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1407 sipe_group_remove(sipe_private,
1408 sipe_group_find_by_id(sipe_private,
1409 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1410 NULL)));
1413 sipe_xml_free(isc);
1415 /* Subscribe to buddies, if contact list not migrated to UCS */
1416 if (!sipe_ucs_is_migrated(sipe_private))
1417 sipe_subscribe_presence_initial(sipe_private);
1419 /* for 2005 systems schedule contacts' status update
1420 * based on their calendar information
1422 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1423 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1426 return 0;
1429 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1430 struct sipmsg *msg)
1432 guint delta;
1433 sipe_xml *xml;
1435 xml = sipe_xml_parse(msg->body, msg->bodylen);
1436 if (!xml)
1437 return;
1439 /* [MS-SIP]: deltaNum MUST be non-zero */
1440 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1441 if (delta) {
1442 sipe_private->deltanum_acl = delta;
1445 sipe_xml_free(xml);
1448 struct sipe_auth_job {
1449 gchar *who;
1450 struct sipe_core_private *sipe_private;
1453 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1454 const gchar* who,
1455 gboolean allow)
1457 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1459 if (allow) {
1460 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1461 } else {
1462 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1465 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1466 sipe_ocs2007_change_access_level(sipe_private,
1467 (allow ? -1 : 32000),
1468 "user",
1469 sipe_get_no_sip_uri(who));
1470 } else {
1471 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1476 static void sipe_auth_user_cb(gpointer data)
1478 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1479 if (!job) return;
1481 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1482 job->who,
1483 TRUE);
1484 g_free(job);
1487 static void sipe_deny_user_cb(gpointer data)
1489 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1490 if (!job) return;
1492 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1493 job->who,
1494 FALSE);
1495 g_free(job);
1498 /* OCS2005- */
1499 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1500 struct sipmsg * msg)
1502 sipe_xml *watchers;
1503 const sipe_xml *watcher;
1504 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1505 if (msg->response != 0 && msg->response != 200) return;
1507 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1509 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1510 if (!watchers) return;
1512 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1513 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1514 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1515 gboolean on_list = sipe_buddy_find_by_uri(sipe_private, remote_user) != NULL;
1517 // TODO pull out optional displayName to pass as alias
1518 if (remote_user) {
1519 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1520 job->who = remote_user;
1521 job->sipe_private = sipe_private;
1522 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1523 remote_user,
1524 alias,
1525 on_list,
1526 sipe_auth_user_cb,
1527 sipe_deny_user_cb,
1528 (gpointer)job);
1533 sipe_xml_free(watchers);
1534 return;
1538 * Dispatcher for all incoming subscription information
1539 * whether it comes from NOTIFY, BENOTIFY requests or
1540 * piggy-backed to subscription's OK responce.
1542 void process_incoming_notify(struct sipe_core_private *sipe_private,
1543 struct sipmsg *msg)
1545 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1546 const gchar *event = sipmsg_find_header(msg, "Event");
1547 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1549 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1551 /* implicit subscriptions */
1552 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1553 sipe_process_imdn(sipe_private, msg);
1555 /* event subscriptions */
1556 } else if (event) {
1558 /* One-off subscriptions - sent with "Expires: 0" */
1559 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) {
1560 sipe_process_provisioning_v2(sipe_private, msg);
1561 } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) {
1562 sipe_process_provisioning(sipe_private, msg);
1563 } else if (sipe_strcase_equal(event, "presence")) {
1564 sipe_process_presence(sipe_private, msg);
1565 } else if (sipe_strcase_equal(event, "registration-notify")) {
1566 sipe_process_registration_notify(sipe_private, msg);
1568 /* Subscriptions with timeout */
1569 } else if (!subscription_state || strstr(subscription_state, "active")) {
1570 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
1571 sipe_process_roaming_contacts(sipe_private, msg);
1572 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) {
1573 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1574 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) {
1575 sipe_process_roaming_acl(sipe_private, msg);
1576 } else if (sipe_strcase_equal(event, "presence.wpending")) {
1577 sipe_process_presence_wpending(sipe_private, msg);
1578 } else if (sipe_strcase_equal(event, "conference")) {
1579 sipe_process_conference(sipe_private, msg);
1586 Local Variables:
1587 mode: c
1588 c-file-style: "bsd"
1589 indent-tabs-mode: t
1590 tab-width: 8
1591 End: