Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / core / sipe-notify.c
blobf55a58fd5169009b1e8305b7f2608661a264a1dc
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2019 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 #define READ_INT_FROM_NODE(node_name, field) { \
85 gchar *s = g_strstrip(sipe_xml_data(sipe_xml_child(node, node_name))); \
86 sipe_private->field = s ? atoi(s) : 0; \
87 g_free(s); }
89 sipe_xml *xn_provision_group_list;
90 const sipe_xml *node;
92 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
94 /* provisionGroup */
95 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup");
96 node;
97 node = sipe_xml_twin(node)) {
98 const gchar *node_name = sipe_xml_attribute(node, "name");
100 /* ServerConfiguration */
101 if (sipe_strequal("ServerConfiguration", node_name)) {
102 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
103 "dlxExternalUrl" : "dlxInternalUrl";
104 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
105 "absExternalServerUrl" : "absInternalServerUrl";
106 gchar *ucPC2PCAVEncryption = NULL;
107 gchar *ucPortRangeEnabled = NULL;
109 g_free(sipe_private->focus_factory_uri);
110 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
111 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
112 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
114 g_free(sipe_private->dlx_uri);
115 sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
116 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s",
117 sipe_private->dlx_uri ? sipe_private->dlx_uri : "");
119 g_free(sipe_private->addressbook_uri);
120 sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str));
121 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s",
122 sipe_private->addressbook_uri ? sipe_private->addressbook_uri : "");
124 #ifdef HAVE_VV
125 g_free(sipe_private->test_call_bot_uri);
126 sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall"));
127 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s",
128 sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : "");
130 g_free(sipe_private->mras_uri);
131 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
132 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
133 sipe_private->mras_uri ? sipe_private->mras_uri : "");
135 if (sipe_private->mras_uri)
136 sipe_media_get_av_edge_credentials(sipe_private);
137 #endif
139 ucPC2PCAVEncryption = g_strstrip(sipe_xml_data(sipe_xml_child(node, "ucPC2PCAVEncryption")));
140 if (sipe_strequal(ucPC2PCAVEncryption, "SupportEncryption")) {
141 sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_OPTIONAL;
142 } else if (sipe_strequal(ucPC2PCAVEncryption, "DoNotSupportEncryption")) {
143 sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REJECTED;
144 } else {
145 // "RequireEncryption" or any unknown value.
146 sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REQUIRED;
148 g_free(ucPC2PCAVEncryption);
150 ucPortRangeEnabled = g_strstrip(sipe_xml_data(sipe_xml_child(node, "ucPortRangeEnabled")));
151 if (sipe_strequal(ucPortRangeEnabled, "true")) {
152 READ_INT_FROM_NODE("ucMinMediaPort", min_media_port)
153 READ_INT_FROM_NODE("ucMaxMediaPort", max_media_port)
154 READ_INT_FROM_NODE("ucMinAudioPort", min_audio_port)
155 READ_INT_FROM_NODE("ucMaxAudioPort", max_audio_port)
156 READ_INT_FROM_NODE("ucMinVideoPort", min_video_port)
157 READ_INT_FROM_NODE("ucMaxVideoPort", max_video_port)
158 READ_INT_FROM_NODE("ucMinAppSharingPort", min_appsharing_port)
159 READ_INT_FROM_NODE("ucMaxAppSharingPort", max_appsharing_port)
160 READ_INT_FROM_NODE("ucMinFileTransferPort", min_filetransfer_port)
161 READ_INT_FROM_NODE("ucMaxFileTransferPort", max_filetransfer_port)
162 } else {
163 sipe_private->min_media_port = 0;
164 sipe_private->max_media_port = 0;
165 sipe_private->min_audio_port = 0;
166 sipe_private->max_audio_port = 0;
167 sipe_private->min_video_port = 0;
168 sipe_private->max_video_port = 0;
169 sipe_private->min_appsharing_port = 0;
170 sipe_private->max_appsharing_port = 0;
171 sipe_private->min_filetransfer_port = 0;
172 sipe_private->max_filetransfer_port = 0;
174 g_free(ucPortRangeEnabled);
176 /* persistentChatConfiguration */
177 } else if (sipe_strequal("persistentChatConfiguration", node_name)) {
178 const sipe_xml *property;
179 gboolean enabled = FALSE;
180 gchar *uri = NULL;
182 for (property = sipe_xml_child(node, "propertyEntryList/property");
183 property;
184 property = sipe_xml_twin(property)) {
185 const gchar *name = sipe_xml_attribute(property, "name");
186 gchar *value = sipe_xml_data(property);
188 if (sipe_strequal(name, "EnablePersistentChat")) {
189 enabled = sipe_strequal(value, "true");
191 } else if (sipe_strequal(name, "DefaultPersistentChatPoolUri")) {
192 g_free(uri);
193 uri = value;
194 value = NULL;
196 g_free(value);
199 if (enabled) {
200 g_free(sipe_private->persistentChatPool_uri);
201 sipe_private->persistentChatPool_uri = g_strdup(sipe_get_no_sip_uri(uri));
202 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->persistentChatPool_uri=%s",
203 sipe_private->persistentChatPool_uri ? sipe_private->persistentChatPool_uri : "");
205 g_free(uri);
209 sipe_xml_free(xn_provision_group_list);
211 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
212 /* Some buddies might have been added before we received this
213 * provisioning notify with DLX and addressbook URIs. Now we can
214 * trigger an update of their photos. */
215 sipe_buddy_refresh_photos(sipe_private);
218 if (sipe_private->focus_factory_uri) {
219 /* Fill the list of conferencing capabilities enabled on
220 * the server. */
221 sipe_conf_get_capabilities(sipe_private);
224 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
225 /* persistentChatPool_uri has been set at this point */
226 sipe_groupchat_init(sipe_private);
229 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
230 const gchar *data, unsigned len)
232 sipe_xml *xn_list;
233 const sipe_xml *xn_resource;
234 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
235 g_free, NULL);
237 xn_list = sipe_xml_parse(data, len);
239 for (xn_resource = sipe_xml_child(xn_list, "resource");
240 xn_resource;
241 xn_resource = sipe_xml_twin(xn_resource) )
243 const char *uri, *state;
244 const sipe_xml *xn_instance;
246 xn_instance = sipe_xml_child(xn_resource, "instance");
247 if (!xn_instance) continue;
249 uri = sipe_xml_attribute(xn_resource, "uri");
250 state = sipe_xml_attribute(xn_instance, "state");
251 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
253 if (strstr(state, "resubscribe")) {
254 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
256 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
257 gchar *user = g_strdup(uri);
258 gchar *host = g_strdup(poolFqdn);
259 GSList *server = g_hash_table_lookup(servers,
260 host);
261 server = g_slist_append(server, user);
262 g_hash_table_insert(servers, host, server);
263 } else {
264 sipe_subscribe_presence_single(sipe_private,
265 uri,
266 uri);
271 /* Send out any deferred poolFqdn subscriptions */
272 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
273 g_hash_table_destroy(servers);
275 sipe_xml_free(xn_list);
279 * Update user phone
280 * Suitable for both 2005 and 2007 systems.
282 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
283 * @param phone_type
284 * @param phone may be modified to strip white space
285 * @param phone_display_string may be modified to strip white space
287 static void
288 sipe_update_user_phone(struct sipe_core_private *sipe_private,
289 const gchar *uri,
290 const gchar *phone_type,
291 gchar *phone,
292 gchar *phone_display_string)
294 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
295 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
297 if(!phone || strlen(phone) == 0) return;
299 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
300 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
301 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
302 } else if (sipe_strequal(phone_type, "home")) {
303 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
304 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
305 } else if (sipe_strequal(phone_type, "other")) {
306 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
307 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
308 } else if (sipe_strequal(phone_type, "custom1")) {
309 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
310 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
313 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
314 if (phone_display_string) {
315 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
319 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
320 const gchar *data,
321 unsigned len)
323 char *activity = NULL;
324 const char *epid;
325 const char *status_id = NULL;
326 const char *name;
327 char *uri;
328 char *self_uri = sip_uri_self(sipe_private);
329 int avl;
330 int act;
331 const char *device_name = NULL;
332 const char *cal_start_time = NULL;
333 const char *cal_granularity = NULL;
334 char *cal_free_busy_base64 = NULL;
335 struct sipe_buddy *sbuddy;
336 const sipe_xml *node;
337 sipe_xml *xn_presentity;
338 const sipe_xml *xn_availability;
339 const sipe_xml *xn_activity;
340 const sipe_xml *xn_display_name;
341 const sipe_xml *xn_email;
342 const sipe_xml *xn_phone_number;
343 const sipe_xml *xn_userinfo;
344 const sipe_xml *xn_note;
345 const sipe_xml *xn_oof;
346 const sipe_xml *xn_state;
347 const sipe_xml *xn_contact;
348 char *note;
349 int user_avail;
350 const char *user_avail_nil;
351 int res_avail;
352 time_t user_avail_since = 0;
353 time_t activity_since = 0;
355 /* fix for Reuters environment on Linux */
356 if (data && strstr(data, "encoding=\"utf-16\"")) {
357 char *tmp_data;
358 tmp_data = sipe_utils_str_replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
359 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
360 g_free(tmp_data);
361 } else {
362 xn_presentity = sipe_xml_parse(data, len);
365 xn_availability = sipe_xml_child(xn_presentity, "availability");
366 xn_activity = sipe_xml_child(xn_presentity, "activity");
367 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
368 xn_email = sipe_xml_child(xn_presentity, "email");
369 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
370 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
371 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
372 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
373 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
374 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
375 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
376 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
377 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
378 note = xn_note ? sipe_xml_data(xn_note) : NULL;
380 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
381 user_avail = 0;
382 user_avail_since = 0;
385 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
386 uri = sip_uri_from_name(name);
387 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
388 epid = sipe_xml_attribute(xn_availability, "epid");
389 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
391 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
392 activity = g_strdup(sipe_ocs2005_activity_description(act));
393 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
394 if (user_avail > res_avail) {
395 res_avail = user_avail;
396 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
399 if (xn_display_name) {
400 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
401 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
402 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
403 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
404 char *tel_uri = sip_to_tel_uri(phone_number);
406 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
407 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
408 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
409 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
411 g_free(tel_uri);
412 g_free(phone_label);
413 g_free(phone_number);
414 g_free(email);
415 g_free(display_name);
418 if (xn_contact) {
419 /* tel */
420 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
422 /* Ex.: <tel type="work">tel:+3222220000</tel> */
423 const char *phone_type = sipe_xml_attribute(node, "type");
424 char* phone = sipe_xml_data(node);
426 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
428 g_free(phone);
432 if (xn_display_name || xn_contact)
433 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
435 /* devicePresence */
436 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
437 const sipe_xml *xn_device_name;
438 const sipe_xml *xn_calendar_info;
439 const sipe_xml *xn_state;
440 char *state;
442 /* deviceName */
443 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
444 xn_device_name = sipe_xml_child(node, "deviceName");
445 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
448 /* calendarInfo */
449 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
450 if (xn_calendar_info) {
451 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
453 if (cal_start_time) {
454 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
455 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
457 if (cal_start_time_t_tmp > cal_start_time_t) {
458 cal_start_time = cal_start_time_tmp;
459 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
460 g_free(cal_free_busy_base64);
461 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
463 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);
465 } else {
466 cal_start_time = cal_start_time_tmp;
467 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
468 g_free(cal_free_busy_base64);
469 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
471 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);
475 /* state */
476 xn_state = sipe_xml_child(node, "states/state");
477 if (xn_state) {
478 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
479 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
481 state = sipe_xml_data(xn_state);
482 if (dev_avail_since > user_avail_since &&
483 dev_avail >= res_avail)
485 const gchar *new_desc;
486 res_avail = dev_avail;
487 if (!is_empty(state)) {
488 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
489 g_free(activity);
490 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
491 } else if (sipe_strequal(state, "presenting")) {
492 g_free(activity);
493 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
494 } else {
495 activity = state;
496 state = NULL;
498 activity_since = dev_avail_since;
500 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
501 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
502 if (new_desc) {
503 g_free(activity);
504 activity = g_strdup(new_desc);
507 g_free(state);
511 /* oof */
512 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
513 g_free(activity);
514 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
515 activity_since = 0;
518 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
519 if (sbuddy)
521 g_free(sbuddy->activity);
522 sbuddy->activity = activity;
523 activity = NULL;
525 sbuddy->activity_since = activity_since;
527 sbuddy->user_avail = user_avail;
528 sbuddy->user_avail_since = user_avail_since;
530 g_free(sbuddy->note);
531 sbuddy->note = NULL;
532 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
534 sbuddy->is_oof_note = (xn_oof != NULL);
536 g_free(sbuddy->device_name);
537 sbuddy->device_name = NULL;
538 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
540 if (!is_empty(cal_free_busy_base64)) {
541 g_free(sbuddy->cal_start_time);
542 sbuddy->cal_start_time = g_strdup(cal_start_time);
544 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
546 g_free(sbuddy->cal_free_busy_base64);
547 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
548 cal_free_busy_base64 = NULL;
550 g_free(sbuddy->cal_free_busy);
551 sbuddy->cal_free_busy = NULL;
554 sbuddy->last_non_cal_status_id = status_id;
555 g_free(sbuddy->last_non_cal_activity);
556 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
558 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
559 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
561 if (sbuddy->is_oof_note)
562 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
563 else
564 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
566 g_free(sipe_private->note);
567 sipe_private->note = g_strdup(sbuddy->note);
569 sipe_private->note_since = time(NULL);
572 sipe_status_set_token(sipe_private,
573 sbuddy->last_non_cal_status_id);
576 g_free(cal_free_busy_base64);
577 g_free(activity);
579 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
580 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC,
581 uri,
582 sipe_status_token_to_activity(status_id),
585 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
586 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
589 g_free(note);
590 sipe_xml_free(xn_presentity);
591 g_free(uri);
592 g_free(self_uri);
595 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
596 const gchar *data,
597 unsigned len)
599 const char *uri;
600 struct sipe_buddy *sbuddy = NULL;
601 sipe_xml *xn_categories;
602 const sipe_xml *xn_category;
603 const char *status = NULL;
604 gboolean do_update_status = FALSE;
605 gboolean has_note_cleaned = FALSE;
606 gboolean has_free_busy_cleaned = FALSE;
607 time_t last_active = 0;
609 xn_categories = sipe_xml_parse(data, len);
610 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
611 if (uri) {
612 sbuddy = sipe_buddy_find_by_uri(sipe_private, uri);
615 if (!sbuddy) {
616 /* Got presence of a buddy not in our contact list, ignore. */
617 sipe_xml_free(xn_categories);
618 return;
621 for (xn_category = sipe_xml_child(xn_categories, "category");
622 xn_category ;
623 xn_category = sipe_xml_twin(xn_category) )
625 const sipe_xml *xn_node;
626 const char *tmp;
627 const char *attrVar = sipe_xml_attribute(xn_category, "name");
628 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
629 sipe_utils_str_to_time(tmp) : 0;
631 /* contactCard */
632 if (sipe_strequal(attrVar, "contactCard"))
634 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
636 if (card) {
637 const sipe_xml *node;
638 /* identity - Display Name and email */
639 node = sipe_xml_child(card, "identity");
640 if (node) {
641 char* display_name = sipe_xml_data(
642 sipe_xml_child(node, "name/displayName"));
643 char* email = sipe_xml_data(
644 sipe_xml_child(node, "email"));
646 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
647 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
649 g_free(display_name);
650 g_free(email);
652 /* company */
653 node = sipe_xml_child(card, "company");
654 if (node) {
655 char* company = sipe_xml_data(node);
656 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
657 g_free(company);
659 /* department */
660 node = sipe_xml_child(card, "department");
661 if (node) {
662 char* department = sipe_xml_data(node);
663 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
664 g_free(department);
666 /* title */
667 node = sipe_xml_child(card, "title");
668 if (node) {
669 char* title = sipe_xml_data(node);
670 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
671 g_free(title);
673 /* office */
674 node = sipe_xml_child(card, "office");
675 if (node) {
676 char* office = sipe_xml_data(node);
677 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
678 g_free(office);
680 /* site (url) */
681 node = sipe_xml_child(card, "url");
682 if (node) {
683 char* site = sipe_xml_data(node);
684 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
685 g_free(site);
687 /* phone */
688 for (node = sipe_xml_child(card, "phone");
689 node;
690 node = sipe_xml_twin(node))
692 const char *phone_type = sipe_xml_attribute(node, "type");
693 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
694 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
696 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
698 g_free(phone);
699 g_free(phone_display_string);
701 /* address */
702 for (node = sipe_xml_child(card, "address");
703 node;
704 node = sipe_xml_twin(node))
706 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
707 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
708 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
709 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
710 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
711 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
713 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
714 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
715 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
716 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
717 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
719 g_free(street);
720 g_free(city);
721 g_free(state);
722 g_free(zipcode);
723 g_free(country_code);
725 break;
728 /* photo */
729 for (node = sipe_xml_child(card, "photo");
730 node;
731 node = sipe_xml_twin(node)) {
732 const gchar *type = sipe_xml_attribute(node, "type");
733 gchar *photo_url;
734 gchar *hash;
735 gboolean found = FALSE;
737 if (sipe_strequal(type, "default") &&
738 !SIPE_CORE_PUBLIC_FLAG_IS(ALLOW_WEB_PHOTO)) {
739 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: skipping download of web profile picture for %s", uri);
740 continue;
743 photo_url = sipe_xml_data(sipe_xml_child(node, "uri"));
744 hash = sipe_xml_data(sipe_xml_child(node, "hash"));
746 if (!is_empty(photo_url) && !is_empty(hash)) {
747 sipe_buddy_update_photo(sipe_private,
748 uri,
749 hash,
750 photo_url,
751 NULL);
752 found = TRUE;
755 g_free(hash);
756 g_free(photo_url);
758 if (found)
759 break;
763 /* note */
764 else if (sipe_strequal(attrVar, "note"))
766 if (!has_note_cleaned) {
767 has_note_cleaned = TRUE;
769 g_free(sbuddy->note);
770 sbuddy->note = NULL;
771 sbuddy->is_oof_note = FALSE;
772 sbuddy->note_since = publish_time;
774 do_update_status = TRUE;
776 if (publish_time >= sbuddy->note_since) {
777 /* clean up in case no 'note' element is supplied
778 * which indicate note removal in client
780 g_free(sbuddy->note);
781 sbuddy->note = NULL;
782 sbuddy->is_oof_note = FALSE;
783 sbuddy->note_since = publish_time;
785 xn_node = sipe_xml_child(xn_category, "note/body");
786 if (xn_node) {
787 char *tmp;
788 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
789 g_free(tmp);
790 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
791 sbuddy->note_since = publish_time;
793 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
794 uri, sbuddy->note ? sbuddy->note : "");
796 /* to trigger UI refresh in case no status info is supplied in this update */
797 do_update_status = TRUE;
800 /* state */
801 else if(sipe_strequal(attrVar, "state"))
803 char *tmp;
804 int availability;
805 const sipe_xml *xn_availability;
806 const sipe_xml *xn_activity;
807 const sipe_xml *xn_device;
808 const sipe_xml *xn_meeting_subject;
809 const sipe_xml *xn_meeting_location;
810 const gchar *legacy_activity;
811 const gchar *last_active_attr;
813 xn_node = sipe_xml_child(xn_category, "state");
814 if (!xn_node) continue;
815 xn_availability = sipe_xml_child(xn_node, "availability");
816 if (!xn_availability) continue;
817 xn_activity = sipe_xml_child(xn_node, "activity");
818 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
819 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
821 tmp = sipe_xml_data(xn_availability);
822 availability = atoi(tmp);
823 g_free(tmp);
825 sbuddy->is_mobile = FALSE;
826 xn_device = sipe_xml_child(xn_node, "device");
827 if (xn_device) {
828 tmp = sipe_xml_data(xn_device);
829 sbuddy->is_mobile = !g_ascii_strcasecmp(tmp, "Mobile");
830 g_free(tmp);
833 /* activity */
834 g_free(sbuddy->activity);
835 sbuddy->activity = NULL;
836 if (xn_activity) {
837 const char *token = sipe_xml_attribute(xn_activity, "token");
838 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
840 /* from token */
841 if (!is_empty(token)) {
842 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
844 /* from custom element */
845 if (xn_custom) {
846 char *custom = sipe_xml_data(xn_custom);
848 if (!is_empty(custom)) {
849 g_free(sbuddy->activity);
850 sbuddy->activity = custom;
851 custom = NULL;
853 g_free(custom);
856 /* meeting_subject */
857 g_free(sbuddy->meeting_subject);
858 sbuddy->meeting_subject = NULL;
859 if (xn_meeting_subject) {
860 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
862 if (!is_empty(meeting_subject)) {
863 sbuddy->meeting_subject = meeting_subject;
864 meeting_subject = NULL;
866 g_free(meeting_subject);
868 /* meeting_location */
869 g_free(sbuddy->meeting_location);
870 sbuddy->meeting_location = NULL;
871 if (xn_meeting_location) {
872 char *meeting_location = sipe_xml_data(xn_meeting_location);
874 if (!is_empty(meeting_location)) {
875 sbuddy->meeting_location = meeting_location;
876 meeting_location = NULL;
878 g_free(meeting_location);
881 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
882 legacy_activity = sipe_ocs2007_legacy_activity_description(availability);
883 if (sbuddy->activity && legacy_activity) {
884 gchar *tmp2 = sbuddy->activity;
886 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity);
887 g_free(tmp2);
888 } else if (legacy_activity) {
889 sbuddy->activity = g_strdup(legacy_activity);
892 /* lastActive */
893 last_active_attr = sipe_xml_attribute(xn_node, "lastActive");
894 if (last_active_attr) {
895 last_active = sipe_utils_str_to_time(last_active_attr);
898 do_update_status = TRUE;
900 /* calendarData */
901 else if(sipe_strequal(attrVar, "calendarData"))
903 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
904 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
906 if (xn_free_busy) {
907 if (!has_free_busy_cleaned) {
908 has_free_busy_cleaned = TRUE;
910 g_free(sbuddy->cal_start_time);
911 sbuddy->cal_start_time = NULL;
913 g_free(sbuddy->cal_free_busy_base64);
914 sbuddy->cal_free_busy_base64 = NULL;
916 g_free(sbuddy->cal_free_busy);
917 sbuddy->cal_free_busy = NULL;
919 sbuddy->cal_free_busy_published = publish_time;
922 if (publish_time >= sbuddy->cal_free_busy_published) {
923 g_free(sbuddy->cal_start_time);
924 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
926 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
927 15 : 0;
929 g_free(sbuddy->cal_free_busy_base64);
930 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
932 g_free(sbuddy->cal_free_busy);
933 sbuddy->cal_free_busy = NULL;
935 sbuddy->cal_free_busy_published = publish_time;
937 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);
941 if (xn_working_hours) {
942 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
947 if (do_update_status) {
948 guint activity;
950 if (status) {
951 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
952 activity = sipe_status_token_to_activity(status);
953 } else {
954 /* no status category in this update,
955 using contact's current status */
956 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
957 uri);
960 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC,
961 uri,
962 activity,
963 last_active);
966 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
968 sipe_xml_free(xn_categories);
971 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
972 const gchar *uri,
973 const gchar *activity,
974 gboolean is_online)
976 if (is_online) {
977 const gchar *status_id = NULL;
978 if (activity) {
979 if (sipe_strequal(activity,
980 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
981 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
982 } else if (sipe_strequal(activity,
983 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
984 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
988 if (!status_id) {
989 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
992 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
993 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC,
994 uri,
995 sipe_status_token_to_activity(status_id),
997 } else {
998 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC,
999 uri,
1000 SIPE_ACTIVITY_OFFLINE,
1005 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
1006 const gchar *data,
1007 unsigned len)
1009 gchar *uri;
1010 gchar *getbasic;
1011 gchar *activity = NULL;
1012 sipe_xml *pidf;
1013 const sipe_xml *basicstatus = NULL, *tuple, *status;
1014 gboolean isonline = FALSE;
1015 const sipe_xml *display_name_node;
1017 pidf = sipe_xml_parse(data, len);
1018 if (!pidf) {
1019 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
1020 return;
1023 if ((tuple = sipe_xml_child(pidf, "tuple")))
1025 if ((status = sipe_xml_child(tuple, "status"))) {
1026 basicstatus = sipe_xml_child(status, "basic");
1030 if (!basicstatus) {
1031 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
1032 sipe_xml_free(pidf);
1033 return;
1036 getbasic = sipe_xml_data(basicstatus);
1037 if (!getbasic) {
1038 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
1039 sipe_xml_free(pidf);
1040 return;
1043 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
1044 if (strstr(getbasic, "open")) {
1045 isonline = TRUE;
1047 g_free(getbasic);
1049 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
1051 display_name_node = sipe_xml_child(pidf, "display-name");
1052 if (display_name_node) {
1053 char * display_name = sipe_xml_data(display_name_node);
1055 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
1056 g_free(display_name);
1058 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
1061 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
1062 if ((status = sipe_xml_child(tuple, "status"))) {
1063 if ((basicstatus = sipe_xml_child(status, "activities"))) {
1064 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
1065 activity = sipe_xml_data(basicstatus);
1066 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
1072 sipe_buddy_status_from_activity(sipe_private,
1073 uri,
1074 activity,
1075 isonline);
1077 g_free(activity);
1078 g_free(uri);
1079 sipe_xml_free(pidf);
1082 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
1083 const GSList *fields,
1084 const gchar *body,
1085 gsize length)
1087 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
1089 if (strstr(type,"application/rlmi+xml")) {
1090 process_incoming_notify_rlmi_resub(user_data, body, length);
1091 } else if (strstr(type, "text/xml+msrtc.pidf")) {
1092 process_incoming_notify_msrtc(user_data, body, length);
1093 } else {
1094 process_incoming_notify_rlmi(user_data, body, length);
1098 static void sipe_process_presence(struct sipe_core_private *sipe_private,
1099 struct sipmsg *msg)
1101 const char *ctype = sipmsg_find_content_type_header(msg);
1103 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
1105 if (ctype &&
1106 (strstr(ctype, "application/rlmi+xml") ||
1107 strstr(ctype, "application/msrtc-event-categories+xml")))
1109 if (strstr(ctype, "multipart"))
1111 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
1113 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
1115 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
1117 else if(strstr(ctype, "application/rlmi+xml"))
1119 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
1122 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
1124 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
1126 else
1128 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
1133 * Fires on deregistration event initiated by server.
1134 * [MS-SIPREGE] SIP extension.
1136 * OCS2007 Example
1138 * Content-Type: text/registration-event
1139 * subscription-state: terminated;expires=0
1140 * ms-diagnostics-public: 4141;reason="User disabled"
1142 * deregistered;event=rejected
1144 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
1145 struct sipmsg *msg)
1147 const gchar *contenttype = sipmsg_find_content_type_header(msg);
1148 gchar *event = NULL;
1149 gchar *reason = NULL;
1150 gchar *warning;
1152 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1154 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1155 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1156 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1157 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1158 } else {
1159 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1160 return;
1163 reason = sipmsg_get_ms_diagnostics_reason(msg);
1164 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1165 if (!reason) { // for LCS2005
1166 if (event && sipe_strcase_equal(event, "unregistered")) {
1167 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1168 reason = g_strdup(_("you are already signed in at another location"));
1169 } else if (event && sipe_strcase_equal(event, "rejected")) {
1170 reason = g_strdup(_("user disabled")); // [MS-OCER]
1171 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1172 reason = g_strdup(_("user moved")); // [MS-OCER]
1175 g_free(event);
1176 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1177 g_free(reason);
1179 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1180 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1181 warning);
1182 g_free(warning);
1186 /* Replace "~" with localized version of "Other Contacts" */
1187 static const gchar *get_group_name(const sipe_xml *node)
1189 const gchar *name = sipe_xml_attribute(node, "name");
1190 return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name);
1193 static void add_new_group(struct sipe_core_private *sipe_private,
1194 const sipe_xml *node)
1196 sipe_group_add(sipe_private,
1197 get_group_name(node),
1198 NULL,
1199 NULL,
1200 sipe_xml_int_attribute(node, "id", 0));
1203 static void add_new_buddy(struct sipe_core_private *sipe_private,
1204 const sipe_xml *node,
1205 const gchar *uri)
1207 const gchar *name = sipe_xml_attribute(node, "name");
1208 struct sipe_buddy *buddy = NULL;
1209 gchar *tmp;
1210 gchar **item_groups;
1211 int i = 0;
1213 /* "name" attribute is a contact alias which user can manually assign by
1214 * renaming the item in the contact list. Empty string means no alias
1215 * and the display name from the contact card should be used instead. */
1216 if (name && strlen(name) == 0) {
1217 name = NULL;
1220 /* assign to group Other Contacts if nothing else received */
1221 tmp = g_strdup(sipe_xml_attribute(node, "groups"));
1222 if (is_empty(tmp)) {
1223 struct sipe_group *group = sipe_group_find_by_name(sipe_private,
1224 _("Other Contacts"));
1225 g_free(tmp);
1226 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1228 item_groups = g_strsplit(tmp, " ", 0);
1229 g_free(tmp);
1231 while (item_groups[i]) {
1232 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1233 g_ascii_strtod(item_groups[i],
1234 NULL));
1236 /* If couldn't find the right group for this contact, */
1237 /* then just put it in the first group we have */
1238 if (!group)
1239 group = sipe_group_first(sipe_private);
1241 if (group) {
1242 if (!buddy)
1243 buddy = sipe_buddy_add(sipe_private,
1244 uri,
1245 NULL,
1246 NULL);
1248 sipe_buddy_add_to_group(sipe_private,
1249 buddy,
1250 group,
1251 name);
1252 } else {
1253 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1254 uri);
1257 i++;
1260 g_strfreev(item_groups);
1263 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1264 struct sipmsg *msg)
1266 int len = msg->bodylen;
1268 const gchar *tmp = sipmsg_find_event_header(msg);
1269 const sipe_xml *item;
1270 sipe_xml *isc;
1271 guint delta;
1272 const sipe_xml *group_node;
1274 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1275 return FALSE;
1278 /* Convert the contact from XML to backend Buddies */
1279 isc = sipe_xml_parse(msg->body, len);
1280 if (!isc) {
1281 return FALSE;
1284 /* [MS-SIP]: deltaNum MUST be non-zero */
1285 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1286 if (delta) {
1287 sipe_private->deltanum_contacts = delta;
1291 * Process whole buddy list
1293 * - Only sent once
1294 * * up to Lync 2010
1295 * * Lync 2013 (and later) with buddy list not migrated
1297 * - Lync 2013 with buddy list migrated to Unified Contact Store (UCS)
1298 * * Notify piggy-backed on SUBSCRIBE response with empty list
1299 * * NOTIFY send by server with standard list (ignored by us)
1301 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1302 const gchar *ucsmode = sipe_xml_attribute(isc, "ucsmode");
1304 SIPE_CORE_PRIVATE_FLAG_UNSET(LYNC2013);
1305 if (ucsmode) {
1306 gboolean migrated = sipe_strcase_equal(ucsmode,
1307 "migrated");
1308 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013);
1309 SIPE_LOG_INFO_NOFORMAT("sipe_process_roaming_contacts: contact list contains 'ucsmode' attribute (indicates Lync 2013+)");
1311 if (migrated)
1312 SIPE_LOG_INFO_NOFORMAT("sipe_process_roaming_contacts: contact list has been migrated to Unified Contact Store (UCS)");
1313 sipe_ucs_init(sipe_private, migrated);
1316 if (!sipe_ucs_is_migrated(sipe_private)) {
1317 /* Start processing contact list */
1318 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1320 /* Parse groups */
1321 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node))
1322 add_new_group(sipe_private, group_node);
1324 /* Make sure we have at least one group */
1325 if (sipe_group_count(sipe_private) == 0) {
1326 sipe_group_create(sipe_private,
1327 NULL,
1328 _("Other Contacts"),
1329 NULL);
1332 /* Parse contacts */
1333 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1334 const gchar *name = sipe_xml_attribute(item, "uri");
1335 gchar *uri = sip_uri_from_name(name);
1336 add_new_buddy(sipe_private, item, uri);
1337 g_free(uri);
1340 sipe_buddy_cleanup_local_list(sipe_private);
1342 /* Add self-contact if not there yet. 2005 systems. */
1343 /* This will resemble subscription to roaming_self in 2007 systems */
1344 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1345 gchar *self_uri = sip_uri_self(sipe_private);
1346 sipe_buddy_add(sipe_private,
1347 self_uri,
1348 NULL,
1349 NULL);
1350 g_free(self_uri);
1353 /* Finished processing contact list */
1354 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1357 /* Process buddy list updates */
1358 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1360 /* Process new groups */
1361 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1362 add_new_group(sipe_private, group_node);
1364 /* Process modified groups */
1365 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1366 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1367 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1368 NULL));
1369 if (group) {
1370 const gchar *name = get_group_name(group_node);
1372 if (!(is_empty(name) ||
1373 sipe_strequal(group->name, name)) &&
1374 sipe_group_rename(sipe_private,
1375 group,
1376 name))
1377 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1381 /* Process new buddies */
1382 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1383 add_new_buddy(sipe_private,
1384 item,
1385 sipe_xml_attribute(item, "uri"));
1388 /* Process modified buddies */
1389 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1390 const gchar *uri = sipe_xml_attribute(item, "uri");
1391 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1392 uri);
1394 if (buddy) {
1395 gchar **item_groups = g_strsplit(sipe_xml_attribute(item,
1396 "groups"),
1397 " ", 0);
1399 /* this should be defined. Otherwise we would get "deletedContact" */
1400 if (item_groups) {
1401 const gchar *name = sipe_xml_attribute(item, "name");
1402 gboolean empty_name = is_empty(name);
1403 GSList *found = NULL;
1404 int i = 0;
1406 while (item_groups[i]) {
1407 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1408 g_ascii_strtod(item_groups[i],
1409 NULL));
1410 /* ignore unkown groups */
1411 if (group) {
1412 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1413 uri,
1414 group->name);
1416 /* add group to found list */
1417 found = g_slist_prepend(found, group);
1419 if (b) {
1420 /* new alias? */
1421 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1424 if (!(empty_name ||
1425 sipe_strequal(b_alias, name))) {
1426 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1428 name);
1429 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1430 uri, group->name, b_alias, name);
1432 g_free(b_alias);
1434 } else {
1435 const gchar *alias = empty_name ? uri : name;
1436 /* buddy was not in this group */
1437 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1438 uri,
1439 alias,
1440 group->name);
1441 sipe_buddy_insert_group(buddy, group);
1442 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1443 uri, alias, group->name);
1447 /* next group */
1448 i++;
1450 g_strfreev(item_groups);
1452 /* removed from groups? */
1453 sipe_buddy_update_groups(sipe_private,
1454 buddy,
1455 found);
1456 g_slist_free(found);
1461 /* Process deleted buddies */
1462 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1463 const gchar *uri = sipe_xml_attribute(item, "uri");
1464 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
1465 uri);
1467 if (buddy) {
1468 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1469 sipe_buddy_remove(sipe_private, buddy);
1473 /* Process deleted groups
1475 * NOTE: all buddies will already have been removed from the
1476 * group prior to this. The log shows that OCS actually
1477 * sends two separate updates when you delete a group:
1479 * - first one with "modifiedContact" removing buddies
1480 * from the group, leaving it empty, and
1482 * - then one with "deletedGroup" removing the group
1484 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1485 sipe_group_remove(sipe_private,
1486 sipe_group_find_by_id(sipe_private,
1487 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1488 NULL)));
1491 sipe_xml_free(isc);
1493 /* Subscribe to buddies, if contact list not migrated to UCS */
1494 if (!sipe_ucs_is_migrated(sipe_private))
1495 sipe_subscribe_presence_initial(sipe_private);
1497 /* for 2005 systems schedule contacts' status update
1498 * based on their calendar information
1500 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1501 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1504 return 0;
1507 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1508 struct sipmsg *msg)
1510 guint delta;
1511 sipe_xml *xml;
1513 xml = sipe_xml_parse(msg->body, msg->bodylen);
1514 if (!xml)
1515 return;
1517 /* [MS-SIP]: deltaNum MUST be non-zero */
1518 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1519 if (delta) {
1520 sipe_private->deltanum_acl = delta;
1523 sipe_xml_free(xml);
1526 struct sipe_auth_job {
1527 gchar *who;
1528 struct sipe_core_private *sipe_private;
1531 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1532 const gchar* who,
1533 gboolean allow)
1535 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1537 if (allow) {
1538 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1539 } else {
1540 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1543 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1544 sipe_ocs2007_change_access_level(sipe_private,
1545 (allow ? -1 : 32000),
1546 "user",
1547 sipe_get_no_sip_uri(who));
1548 } else {
1549 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1554 static void sipe_auth_user_cb(gpointer data)
1556 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1557 if (!job) return;
1559 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1560 job->who,
1561 TRUE);
1562 g_free(job);
1565 static void sipe_deny_user_cb(gpointer data)
1567 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1568 if (!job) return;
1570 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1571 job->who,
1572 FALSE);
1573 g_free(job);
1576 /* OCS2005- */
1577 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1578 struct sipmsg * msg)
1580 sipe_xml *watchers;
1581 const sipe_xml *watcher;
1582 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1583 if (msg->response != 0 && msg->response != 200) return;
1585 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_event_header(msg), "msrtc.wpending")) return;
1587 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1588 if (!watchers) return;
1590 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1591 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1592 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1593 gboolean on_list = sipe_buddy_find_by_uri(sipe_private, remote_user) != NULL;
1595 // TODO pull out optional displayName to pass as alias
1596 if (remote_user) {
1597 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1598 job->who = remote_user;
1599 job->sipe_private = sipe_private;
1600 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1601 remote_user,
1602 alias,
1603 on_list,
1604 sipe_auth_user_cb,
1605 sipe_deny_user_cb,
1606 (gpointer)job);
1611 sipe_xml_free(watchers);
1612 return;
1616 * Dispatcher for all incoming subscription information
1617 * whether it comes from NOTIFY, BENOTIFY requests or
1618 * piggy-backed to subscription's OK responce.
1620 void process_incoming_notify(struct sipe_core_private *sipe_private,
1621 struct sipmsg *msg)
1623 const gchar *content_type = sipmsg_find_content_type_header(msg);
1624 const gchar *event = sipmsg_find_event_header(msg);
1625 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1627 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1629 /* implicit subscriptions */
1630 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1631 sipe_process_imdn(sipe_private, msg);
1633 /* event subscriptions */
1634 } else if (event) {
1636 /* One-off subscriptions - sent with "Expires: 0" */
1637 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) {
1638 sipe_process_provisioning_v2(sipe_private, msg);
1639 } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) {
1640 sipe_process_provisioning(sipe_private, msg);
1641 } else if (sipe_strcase_equal(event, "presence")) {
1642 sipe_process_presence(sipe_private, msg);
1643 } else if (sipe_strcase_equal(event, "registration-notify")) {
1644 sipe_process_registration_notify(sipe_private, msg);
1646 /* Subscriptions with timeout */
1647 } else if (!subscription_state || strstr(subscription_state, "active")) {
1648 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
1649 sipe_process_roaming_contacts(sipe_private, msg);
1650 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) {
1651 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1652 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) {
1653 sipe_process_roaming_acl(sipe_private, msg);
1654 } else if (sipe_strcase_equal(event, "presence.wpending")) {
1655 sipe_process_presence_wpending(sipe_private, msg);
1656 } else if (sipe_strcase_equal(event, "conference")) {
1657 sipe_process_conference(sipe_private, msg);
1664 Local Variables:
1665 mode: c
1666 c-file-style: "bsd"
1667 indent-tabs-mode: t
1668 tab-width: 8
1669 End: