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
40 #include "sipe-backend.h"
41 #include "sipe-buddy.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"
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"
57 #include "sipe-utils.h"
61 static void sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
64 sipe_xml
*xn_provision
;
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
);
81 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
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; \
89 sipe_xml
*xn_provision_group_list
;
92 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
95 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup");
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
: "");
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
);
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
;
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
)
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
;
182 for (property
= sipe_xml_child(node
, "propertyEntryList/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")) {
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
: "");
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
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
)
233 const sipe_xml
*xn_resource
;
234 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
237 xn_list
= sipe_xml_parse(data
, len
);
239 for (xn_resource
= sipe_xml_child(xn_list
, "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
,
261 server
= g_slist_append(server
, user
);
262 g_hash_table_insert(servers
, host
, server
);
264 sipe_subscribe_presence_single(sipe_private
,
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
);
280 * Suitable for both 2005 and 2007 systems.
282 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
284 * @param phone may be modified to strip white space
285 * @param phone_display_string may be modified to strip white space
288 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
290 const gchar
*phone_type
,
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
,
323 char *activity
= NULL
;
325 const char *status_id
= NULL
;
328 char *self_uri
= sip_uri_self(sipe_private
);
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
;
350 const char *user_avail_nil
;
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\"")) {
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
));
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 */
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
);
413 g_free(phone_number
);
415 g_free(display_name
);
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
);
432 if (xn_display_name
|| xn_contact
)
433 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC
, uri
);
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
;
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
;
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
);
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
);
476 xn_state
= sipe_xml_child(node
, "states/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
))) {
490 activity
= g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE
));
491 } else if (sipe_strequal(state
, "presenting")) {
493 activity
= g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF
));
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
);
504 activity
= g_strdup(new_desc
);
512 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
514 activity
= g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF
));
518 sbuddy
= sipe_buddy_find_by_uri(sipe_private
, uri
);
521 g_free(sbuddy
->activity
);
522 sbuddy
->activity
= activity
;
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
);
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
);
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
);
579 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
580 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
,
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
);
590 sipe_xml_free(xn_presentity
);
595 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
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 */
612 sbuddy
= sipe_buddy_find_by_uri(sipe_private
, uri
);
616 /* Got presence of a buddy not in our contact list, ignore. */
617 sipe_xml_free(xn_categories
);
621 for (xn_category
= sipe_xml_child(xn_categories
, "category");
623 xn_category
= sipe_xml_twin(xn_category
) )
625 const sipe_xml
*xn_node
;
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;
632 if (sipe_strequal(attrVar
, "contactCard"))
634 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
637 const sipe_xml
*node
;
638 /* identity - Display Name and email */
639 node
= sipe_xml_child(card
, "identity");
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
);
653 node
= sipe_xml_child(card
, "company");
655 char* company
= sipe_xml_data(node
);
656 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_COMPANY
, company
);
660 node
= sipe_xml_child(card
, "department");
662 char* department
= sipe_xml_data(node
);
663 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DEPARTMENT
, department
);
667 node
= sipe_xml_child(card
, "title");
669 char* title
= sipe_xml_data(node
);
670 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_JOB_TITLE
, title
);
674 node
= sipe_xml_child(card
, "office");
676 char* office
= sipe_xml_data(node
);
677 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_OFFICE
, office
);
681 node
= sipe_xml_child(card
, "url");
683 char* site
= sipe_xml_data(node
);
684 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_SITE
, site
);
688 for (node
= sipe_xml_child(card
, "phone");
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
);
699 g_free(phone_display_string
);
702 for (node
= sipe_xml_child(card
, "address");
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
);
723 g_free(country_code
);
729 for (node
= sipe_xml_child(card
, "photo");
731 node
= sipe_xml_twin(node
)) {
732 const gchar
*type
= sipe_xml_attribute(node
, "type");
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
);
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
,
764 else if (sipe_strequal(attrVar
, "note"))
766 if (!has_note_cleaned
) {
767 has_note_cleaned
= TRUE
;
769 g_free(sbuddy
->note
);
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
);
782 sbuddy
->is_oof_note
= FALSE
;
783 sbuddy
->note_since
= publish_time
;
785 xn_node
= sipe_xml_child(xn_category
, "note/body");
788 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
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
;
801 else if(sipe_strequal(attrVar
, "state"))
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
);
825 sbuddy
->is_mobile
= FALSE
;
826 xn_device
= sipe_xml_child(xn_node
, "device");
828 tmp
= sipe_xml_data(xn_device
);
829 sbuddy
->is_mobile
= !g_ascii_strcasecmp(tmp
, "Mobile");
834 g_free(sbuddy
->activity
);
835 sbuddy
->activity
= NULL
;
837 const char *token
= sipe_xml_attribute(xn_activity
, "token");
838 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
841 if (!is_empty(token
)) {
842 sbuddy
->activity
= g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token
)));
844 /* from custom element */
846 char *custom
= sipe_xml_data(xn_custom
);
848 if (!is_empty(custom
)) {
849 g_free(sbuddy
->activity
);
850 sbuddy
->activity
= 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
);
888 } else if (legacy_activity
) {
889 sbuddy
->activity
= g_strdup(legacy_activity
);
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
;
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");
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") ?
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
) {
951 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
952 activity
= sipe_status_token_to_activity(status
);
954 /* no status category in this update,
955 using contact's current status */
956 activity
= sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC
,
960 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
,
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
,
973 const gchar
*activity
,
977 const gchar
*status_id
= NULL
;
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
);
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
,
995 sipe_status_token_to_activity(status_id
),
998 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
,
1000 SIPE_ACTIVITY_OFFLINE
,
1005 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
1011 gchar
*activity
= NULL
;
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
);
1019 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
1023 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
1025 if ((status
= sipe_xml_child(tuple
, "status"))) {
1026 basicstatus
= sipe_xml_child(status
, "basic");
1031 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
1032 sipe_xml_free(pidf
);
1036 getbasic
= sipe_xml_data(basicstatus
);
1038 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
1039 sipe_xml_free(pidf
);
1043 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
1044 if (strstr(getbasic
, "open")) {
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
,
1079 sipe_xml_free(pidf
);
1082 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
1083 const GSList
*fields
,
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
);
1094 process_incoming_notify_rlmi(user_data
, body
, length
);
1098 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
1101 const char *ctype
= sipmsg_find_content_type_header(msg
);
1103 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? 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
);
1128 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
1133 * Fires on deregistration event initiated by server.
1134 * [MS-SIPREGE] SIP extension.
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
,
1147 const gchar
*contenttype
= sipmsg_find_content_type_header(msg
);
1148 gchar
*event
= NULL
;
1149 gchar
*reason
= NULL
;
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
);
1159 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
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]
1176 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
1179 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
1180 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
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
),
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
,
1207 const gchar
*name
= sipe_xml_attribute(node
, "name");
1208 struct sipe_buddy
*buddy
= NULL
;
1210 gchar
**item_groups
;
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) {
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"));
1226 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1228 item_groups
= g_strsplit(tmp
, " ", 0);
1231 while (item_groups
[i
]) {
1232 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
,
1233 g_ascii_strtod(item_groups
[i
],
1236 /* If couldn't find the right group for this contact, */
1237 /* then just put it in the first group we have */
1239 group
= sipe_group_first(sipe_private
);
1243 buddy
= sipe_buddy_add(sipe_private
,
1248 sipe_buddy_add_to_group(sipe_private
,
1253 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1260 g_strfreev(item_groups
);
1263 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1266 int len
= msg
->bodylen
;
1268 const gchar
*tmp
= sipmsg_find_event_header(msg
);
1269 const sipe_xml
*item
;
1272 const sipe_xml
*group_node
;
1274 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1278 /* Convert the contact from XML to backend Buddies */
1279 isc
= sipe_xml_parse(msg
->body
, len
);
1284 /* [MS-SIP]: deltaNum MUST be non-zero */
1285 delta
= sipe_xml_int_attribute(isc
, "deltaNum", 0);
1287 sipe_private
->deltanum_contacts
= delta
;
1291 * Process whole buddy list
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
);
1306 gboolean migrated
= sipe_strcase_equal(ucsmode
,
1308 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013
);
1309 SIPE_LOG_INFO_NOFORMAT("sipe_process_roaming_contacts: contact list contains 'ucsmode' attribute (indicates Lync 2013+)");
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
);
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
,
1328 _("Other Contacts"),
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
);
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
,
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"),
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
,
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
,
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
,
1395 gchar
**item_groups
= g_strsplit(sipe_xml_attribute(item
,
1399 /* this should be defined. Otherwise we would get "deletedContact" */
1401 const gchar
*name
= sipe_xml_attribute(item
, "name");
1402 gboolean empty_name
= is_empty(name
);
1403 GSList
*found
= NULL
;
1406 while (item_groups
[i
]) {
1407 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
,
1408 g_ascii_strtod(item_groups
[i
],
1410 /* ignore unkown groups */
1412 sipe_backend_buddy b
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
,
1416 /* add group to found list */
1417 found
= g_slist_prepend(found
, group
);
1421 gchar
*b_alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
,
1425 sipe_strequal(b_alias
, name
))) {
1426 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
,
1429 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1430 uri
, group
->name
, b_alias
, name
);
1435 const gchar
*alias
= empty_name
? uri
: name
;
1436 /* buddy was not in this group */
1437 sipe_backend_buddy_add(SIPE_CORE_PUBLIC
,
1441 sipe_buddy_insert_group(buddy
, group
);
1442 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1443 uri
, alias
, group
->name
);
1450 g_strfreev(item_groups
);
1452 /* removed from groups? */
1453 sipe_buddy_update_groups(sipe_private
,
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
,
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"),
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
));
1507 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1513 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1517 /* [MS-SIP]: deltaNum MUST be non-zero */
1518 delta
= sipe_xml_int_attribute(xml
, "deltaNum", 0);
1520 sipe_private
->deltanum_acl
= delta
;
1526 struct sipe_auth_job
{
1528 struct sipe_core_private
*sipe_private
;
1531 void sipe_core_contact_allow_deny(struct sipe_core_public
*sipe_public
,
1535 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
1538 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who
);
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),
1547 sipe_get_no_sip_uri(who
));
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
;
1559 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
,
1565 static void sipe_deny_user_cb(gpointer data
)
1567 struct sipe_auth_job
*job
= (struct sipe_auth_job
*) data
;
1570 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
,
1577 static void sipe_process_presence_wpending (struct sipe_core_private
*sipe_private
,
1578 struct sipmsg
* msg
)
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
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
,
1611 sipe_xml_free(watchers
);
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
,
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 */
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
);