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