6 * Copyright (C) 2011 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"
38 #include "http-conn.h" /* sipe-cal.h requires this */
42 #include "sip-transport.h"
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
46 #include "sipe-conf.h"
47 #include "sipe-core.h"
48 #include "sipe-core-private.h"
49 #include "sipe-group.h"
50 #include "sipe-media.h"
51 #include "sipe-mime.h"
53 #include "sipe-notify.h"
54 #include "sipe-ocs2005.h"
55 #include "sipe-ocs2007.h"
56 #include "sipe-schedule.h"
57 #include "sipe-subscriptions.h"
58 #include "sipe-utils.h"
60 #define _SIPE_NEED_ACTIVITIES
64 static void sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
67 sipe_xml
*xn_provision
;
70 xn_provision
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
71 if ((node
= sipe_xml_child(xn_provision
, "user"))) {
72 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node
, "uri"));
73 if ((node
= sipe_xml_child(node
, "line"))) {
74 const gchar
*line_uri
= sipe_xml_attribute(node
, "uri");
75 const gchar
*server
= sipe_xml_attribute(node
, "server");
76 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri
, server
);
77 sip_csta_open(sipe_private
, line_uri
, server
);
80 sipe_xml_free(xn_provision
);
84 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
87 sipe_xml
*xn_provision_group_list
;
90 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
93 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup"); node
; node
= sipe_xml_twin(node
)) {
94 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node
, "name"))) {
95 g_free(sipe_private
->focus_factory_uri
);
96 sipe_private
->focus_factory_uri
= sipe_xml_data(sipe_xml_child(node
, "focusFactoryUri"));
97 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
98 sipe_private
->focus_factory_uri
? sipe_private
->focus_factory_uri
: "");
101 g_free(sipe_private
->mras_uri
);
102 sipe_private
->mras_uri
= g_strstrip(sipe_xml_data(sipe_xml_child(node
, "mrasUri")));
103 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
104 sipe_private
->mras_uri
? sipe_private
->mras_uri
: "");
106 if (sipe_private
->mras_uri
)
107 sipe_media_get_av_edge_credentials(sipe_private
);
112 sipe_xml_free(xn_provision_group_list
);
115 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
116 const gchar
*data
, unsigned len
)
119 const sipe_xml
*xn_resource
;
120 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
125 xn_list
= sipe_xml_parse(data
, len
);
127 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
129 xn_resource
= sipe_xml_twin(xn_resource
) )
131 const char *uri
, *state
;
132 const sipe_xml
*xn_instance
;
134 xn_instance
= sipe_xml_child(xn_resource
, "instance");
135 if (!xn_instance
) continue;
137 uri
= sipe_xml_attribute(xn_resource
, "uri");
138 state
= sipe_xml_attribute(xn_instance
, "state");
139 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
141 if (strstr(state
, "resubscribe")) {
142 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
144 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
145 gchar
*user
= g_strdup(uri
);
146 host
= g_strdup(poolFqdn
);
147 server
= g_hash_table_lookup(servers
, host
);
148 server
= g_slist_append(server
, user
);
149 g_hash_table_insert(servers
, host
, server
);
151 sipe_subscribe_presence_single(sipe_private
,
157 /* Send out any deferred poolFqdn subscriptions */
158 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
159 g_hash_table_destroy(servers
);
161 sipe_xml_free(xn_list
);
166 * Suitable for both 2005 and 2007 systems.
168 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
170 * @param phone may be modified to strip white space
171 * @param phone_display_string may be modified to strip white space
174 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
176 const gchar
*phone_type
,
178 gchar
*phone_display_string
)
180 sipe_buddy_info_fields phone_node
= SIPE_BUDDY_INFO_WORK_PHONE
; /* work phone by default */
181 sipe_buddy_info_fields phone_display_node
= SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
; /* work phone by default */
183 if(!phone
|| strlen(phone
) == 0) return;
185 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
186 phone_node
= SIPE_BUDDY_INFO_MOBILE_PHONE
;
187 phone_display_node
= SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY
;
188 } else if (sipe_strequal(phone_type
, "home")) {
189 phone_node
= SIPE_BUDDY_INFO_HOME_PHONE
;
190 phone_display_node
= SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY
;
191 } else if (sipe_strequal(phone_type
, "other")) {
192 phone_node
= SIPE_BUDDY_INFO_OTHER_PHONE
;
193 phone_display_node
= SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY
;
194 } else if (sipe_strequal(phone_type
, "custom1")) {
195 phone_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE
;
196 phone_display_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY
;
199 sipe_buddy_update_property(sipe_private
, uri
, phone_node
, phone
);
200 if (phone_display_string
) {
201 sipe_buddy_update_property(sipe_private
, uri
, phone_display_node
, phone_display_string
);
205 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
209 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
210 char *activity
= NULL
;
212 const char *status_id
= NULL
;
215 char *self_uri
= sip_uri_self(sipe_private
);
218 const char *device_name
= NULL
;
219 const char *cal_start_time
= NULL
;
220 const char *cal_granularity
= NULL
;
221 char *cal_free_busy_base64
= NULL
;
222 struct sipe_buddy
*sbuddy
;
223 const sipe_xml
*node
;
224 sipe_xml
*xn_presentity
;
225 const sipe_xml
*xn_availability
;
226 const sipe_xml
*xn_activity
;
227 const sipe_xml
*xn_display_name
;
228 const sipe_xml
*xn_email
;
229 const sipe_xml
*xn_phone_number
;
230 const sipe_xml
*xn_userinfo
;
231 const sipe_xml
*xn_note
;
232 const sipe_xml
*xn_oof
;
233 const sipe_xml
*xn_state
;
234 const sipe_xml
*xn_contact
;
237 const char *user_avail_nil
;
239 time_t user_avail_since
= 0;
240 time_t activity_since
= 0;
242 /* fix for Reuters environment on Linux */
243 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
245 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
246 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
249 xn_presentity
= sipe_xml_parse(data
, len
);
252 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
253 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
254 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
255 xn_email
= sipe_xml_child(xn_presentity
, "email");
256 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
257 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
258 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
259 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
260 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
261 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
262 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
263 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
264 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
265 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
267 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
269 user_avail_since
= 0;
272 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
273 uri
= sip_uri_from_name(name
);
274 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
275 epid
= sipe_xml_attribute(xn_availability
, "epid");
276 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
278 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
279 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
280 if (user_avail
> res_avail
) {
281 res_avail
= user_avail
;
282 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
285 if (xn_display_name
) {
286 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
287 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
288 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
289 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
290 char *tel_uri
= sip_to_tel_uri(phone_number
);
292 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
293 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
294 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
295 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, !is_empty(phone_label
) ? phone_label
: phone_number
);
299 g_free(phone_number
);
301 g_free(display_name
);
306 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
308 /* Ex.: <tel type="work">tel:+3222220000</tel> */
309 const char *phone_type
= sipe_xml_attribute(node
, "type");
310 char* phone
= sipe_xml_data(node
);
312 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
319 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
320 const sipe_xml
*xn_device_name
;
321 const sipe_xml
*xn_calendar_info
;
322 const sipe_xml
*xn_state
;
326 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
327 xn_device_name
= sipe_xml_child(node
, "deviceName");
328 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
332 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
333 if (xn_calendar_info
) {
334 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
336 if (cal_start_time
) {
337 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
338 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
340 if (cal_start_time_t_tmp
> cal_start_time_t
) {
341 cal_start_time
= cal_start_time_tmp
;
342 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
343 g_free(cal_free_busy_base64
);
344 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
346 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
);
349 cal_start_time
= cal_start_time_tmp
;
350 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
351 g_free(cal_free_busy_base64
);
352 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
354 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
);
359 xn_state
= sipe_xml_child(node
, "states/state");
361 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
362 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
364 state
= sipe_xml_data(xn_state
);
365 if (dev_avail_since
> user_avail_since
&&
366 dev_avail
>= res_avail
)
368 res_avail
= dev_avail
;
369 if (!is_empty(state
))
371 if (sipe_strequal(state
, sipe_activity_to_token(SIPE_ACTIVITY_ON_PHONE
))) {
373 activity
= g_strdup(sipe_activity_description(SIPE_ACTIVITY_ON_PHONE
));
374 } else if (sipe_strequal(state
, "presenting")) {
376 activity
= g_strdup(sipe_activity_description(SIPE_ACTIVITY_IN_CONF
));
381 activity_since
= dev_avail_since
;
383 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
390 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
392 activity
= g_strdup(sipe_activity_description(SIPE_ACTIVITY_OOF
));
396 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
399 g_free(sbuddy
->activity
);
400 sbuddy
->activity
= activity
;
403 sbuddy
->activity_since
= activity_since
;
405 sbuddy
->user_avail
= user_avail
;
406 sbuddy
->user_avail_since
= user_avail_since
;
408 g_free(sbuddy
->note
);
410 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
412 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
414 g_free(sbuddy
->device_name
);
415 sbuddy
->device_name
= NULL
;
416 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
418 if (!is_empty(cal_free_busy_base64
)) {
419 g_free(sbuddy
->cal_start_time
);
420 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
422 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
424 g_free(sbuddy
->cal_free_busy_base64
);
425 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
426 cal_free_busy_base64
= NULL
;
428 g_free(sbuddy
->cal_free_busy
);
429 sbuddy
->cal_free_busy
= NULL
;
432 sbuddy
->last_non_cal_status_id
= status_id
;
433 g_free(sbuddy
->last_non_cal_activity
);
434 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
436 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
437 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
439 sip
->is_oof_note
= sbuddy
->is_oof_note
;
442 sip
->note
= g_strdup(sbuddy
->note
);
444 sip
->note_since
= time(NULL
);
448 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
451 g_free(cal_free_busy_base64
);
454 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
455 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
457 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
458 sipe_ocs2005_user_info_has_updated(sipe_private
, xn_userinfo
);
462 sipe_xml_free(xn_presentity
);
467 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
472 sipe_xml
*xn_categories
;
473 const sipe_xml
*xn_category
;
474 const char *status
= NULL
;
475 gboolean do_update_status
= FALSE
;
476 gboolean has_note_cleaned
= FALSE
;
477 gboolean has_free_busy_cleaned
= FALSE
;
479 xn_categories
= sipe_xml_parse(data
, len
);
480 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
482 for (xn_category
= sipe_xml_child(xn_categories
, "category");
484 xn_category
= sipe_xml_twin(xn_category
) )
486 const sipe_xml
*xn_node
;
488 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
489 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
490 sipe_utils_str_to_time(tmp
) : 0;
493 if (sipe_strequal(attrVar
, "contactCard"))
495 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
498 const sipe_xml
*node
;
499 /* identity - Display Name and email */
500 node
= sipe_xml_child(card
, "identity");
502 char* display_name
= sipe_xml_data(
503 sipe_xml_child(node
, "name/displayName"));
504 char* email
= sipe_xml_data(
505 sipe_xml_child(node
, "email"));
507 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
508 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
510 g_free(display_name
);
514 node
= sipe_xml_child(card
, "company");
516 char* company
= sipe_xml_data(node
);
517 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_COMPANY
, company
);
521 node
= sipe_xml_child(card
, "department");
523 char* department
= sipe_xml_data(node
);
524 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DEPARTMENT
, department
);
528 node
= sipe_xml_child(card
, "title");
530 char* title
= sipe_xml_data(node
);
531 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_JOB_TITLE
, title
);
535 node
= sipe_xml_child(card
, "office");
537 char* office
= sipe_xml_data(node
);
538 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_OFFICE
, office
);
542 node
= sipe_xml_child(card
, "url");
544 char* site
= sipe_xml_data(node
);
545 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_SITE
, site
);
549 for (node
= sipe_xml_child(card
, "phone");
551 node
= sipe_xml_twin(node
))
553 const char *phone_type
= sipe_xml_attribute(node
, "type");
554 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
555 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
557 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
560 g_free(phone_display_string
);
563 for (node
= sipe_xml_child(card
, "address");
565 node
= sipe_xml_twin(node
))
567 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
568 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
569 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
570 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
571 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
572 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
574 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_STREET
, street
);
575 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_CITY
, city
);
576 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_STATE
, state
);
577 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_ZIPCODE
, zipcode
);
578 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_COUNTRY
, country_code
);
584 g_free(country_code
);
592 else if (sipe_strequal(attrVar
, "note"))
595 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
597 if (!has_note_cleaned
) {
598 has_note_cleaned
= TRUE
;
600 g_free(sbuddy
->note
);
602 sbuddy
->is_oof_note
= FALSE
;
603 sbuddy
->note_since
= publish_time
;
605 do_update_status
= TRUE
;
607 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
608 /* clean up in case no 'note' element is supplied
609 * which indicate note removal in client
611 g_free(sbuddy
->note
);
613 sbuddy
->is_oof_note
= FALSE
;
614 sbuddy
->note_since
= publish_time
;
616 xn_node
= sipe_xml_child(xn_category
, "note/body");
619 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
621 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
622 sbuddy
->note_since
= publish_time
;
624 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
625 uri
, sbuddy
->note
? sbuddy
->note
: "");
627 /* to trigger UI refresh in case no status info is supplied in this update */
628 do_update_status
= TRUE
;
633 else if(sipe_strequal(attrVar
, "state"))
637 const sipe_xml
*xn_availability
;
638 const sipe_xml
*xn_activity
;
639 const sipe_xml
*xn_meeting_subject
;
640 const sipe_xml
*xn_meeting_location
;
641 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
643 xn_node
= sipe_xml_child(xn_category
, "state");
644 if (!xn_node
) continue;
645 xn_availability
= sipe_xml_child(xn_node
, "availability");
646 if (!xn_availability
) continue;
647 xn_activity
= sipe_xml_child(xn_node
, "activity");
648 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
649 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
651 tmp
= sipe_xml_data(xn_availability
);
652 availability
= atoi(tmp
);
655 /* activity, meeting_subject, meeting_location */
660 g_free(sbuddy
->activity
);
661 sbuddy
->activity
= NULL
;
663 const char *token
= sipe_xml_attribute(xn_activity
, "token");
664 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
667 if (!is_empty(token
)) {
668 sbuddy
->activity
= g_strdup(sipe_activity_description_from_token(token
));
670 /* from custom element */
672 char *custom
= sipe_xml_data(xn_custom
);
674 if (!is_empty(custom
)) {
675 sbuddy
->activity
= custom
;
681 /* meeting_subject */
682 g_free(sbuddy
->meeting_subject
);
683 sbuddy
->meeting_subject
= NULL
;
684 if (xn_meeting_subject
) {
685 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
687 if (!is_empty(meeting_subject
)) {
688 sbuddy
->meeting_subject
= meeting_subject
;
689 meeting_subject
= NULL
;
691 g_free(meeting_subject
);
693 /* meeting_location */
694 g_free(sbuddy
->meeting_location
);
695 sbuddy
->meeting_location
= NULL
;
696 if (xn_meeting_location
) {
697 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
699 if (!is_empty(meeting_location
)) {
700 sbuddy
->meeting_location
= meeting_location
;
701 meeting_location
= NULL
;
703 g_free(meeting_location
);
706 status
= sipe_get_status_by_availability(availability
, &tmp
);
707 if (sbuddy
->activity
&& tmp
) {
708 char *tmp2
= sbuddy
->activity
;
710 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
714 sbuddy
->activity
= tmp
;
718 do_update_status
= TRUE
;
721 else if(sipe_strequal(attrVar
, "calendarData"))
723 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
724 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
725 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
727 if (sbuddy
&& xn_free_busy
) {
728 if (!has_free_busy_cleaned
) {
729 has_free_busy_cleaned
= TRUE
;
731 g_free(sbuddy
->cal_start_time
);
732 sbuddy
->cal_start_time
= NULL
;
734 g_free(sbuddy
->cal_free_busy_base64
);
735 sbuddy
->cal_free_busy_base64
= NULL
;
737 g_free(sbuddy
->cal_free_busy
);
738 sbuddy
->cal_free_busy
= NULL
;
740 sbuddy
->cal_free_busy_published
= publish_time
;
743 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
744 g_free(sbuddy
->cal_start_time
);
745 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
747 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
750 g_free(sbuddy
->cal_free_busy_base64
);
751 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
753 g_free(sbuddy
->cal_free_busy
);
754 sbuddy
->cal_free_busy
= NULL
;
756 sbuddy
->cal_free_busy_published
= publish_time
;
758 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
);
762 if (sbuddy
&& xn_working_hours
) {
763 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
768 if (do_update_status
) {
769 if (!status
) { /* no status category in this update, using contact's current status */
770 status
= sipe_get_buddy_status(sipe_private
,
774 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
775 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status
);
778 sipe_xml_free(xn_categories
);
781 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
787 gchar
*activity
= NULL
;
789 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
790 gboolean isonline
= FALSE
;
791 const sipe_xml
*display_name_node
;
793 pidf
= sipe_xml_parse(data
, len
);
795 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
799 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
801 if ((status
= sipe_xml_child(tuple
, "status"))) {
802 basicstatus
= sipe_xml_child(status
, "basic");
807 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
812 getbasic
= sipe_xml_data(basicstatus
);
814 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
819 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
820 if (strstr(getbasic
, "open")) {
825 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
827 display_name_node
= sipe_xml_child(pidf
, "display-name");
828 if (display_name_node
) {
829 char * display_name
= sipe_xml_data(display_name_node
);
831 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
832 g_free(display_name
);
835 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
836 if ((status
= sipe_xml_child(tuple
, "status"))) {
837 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
838 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
839 activity
= sipe_xml_data(basicstatus
);
840 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
846 sipe_buddy_status_from_activity(sipe_private
,
856 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
857 const GSList
*fields
,
861 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
863 if (strstr(type
,"application/rlmi+xml")) {
864 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
865 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
866 process_incoming_notify_msrtc(user_data
, body
, length
);
868 process_incoming_notify_rlmi(user_data
, body
, length
);
872 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
875 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
877 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
880 (strstr(ctype
, "application/rlmi+xml") ||
881 strstr(ctype
, "application/msrtc-event-categories+xml")))
883 if (strstr(ctype
, "multipart"))
885 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
887 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
889 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
891 else if(strstr(ctype
, "application/rlmi+xml"))
893 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
896 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
898 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
902 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
907 * Fires on deregistration event initiated by server.
908 * [MS-SIPREGE] SIP extension.
912 * Content-Type: text/registration-event
913 * subscription-state: terminated;expires=0
914 * ms-diagnostics-public: 4141;reason="User disabled"
916 * deregistered;event=rejected
918 static void sipe_process_registration_notify(struct sipe_core_private
*sipe_private
,
921 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
923 gchar
*reason
= NULL
;
926 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
928 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
929 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
930 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
931 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
933 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
937 reason
= sipmsg_get_ms_diagnostics_reason(msg
);
938 reason
= reason
? reason
: sipmsg_get_ms_diagnostics_public_reason(msg
);
939 if (!reason
) { // for LCS2005
940 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
941 //reason = g_strdup(_("User logged out")); // [MS-OCER]
942 reason
= g_strdup(_("you are already signed in at another location"));
943 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
944 reason
= g_strdup(_("user disabled")); // [MS-OCER]
945 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
946 reason
= g_strdup(_("user moved")); // [MS-OCER]
950 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
953 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
954 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
961 * Removes entries from local buddy list
962 * that does not correspond ones in the roaming contact list.
964 static void sipe_cleanup_local_blist(struct sipe_core_private
*sipe_private
)
966 GSList
*buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
,
968 GSList
*entry
= buddies
;
969 struct sipe_buddy
*buddy
;
970 sipe_backend_buddy b
;
974 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies
));
975 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private
->buddies
));
978 gname
= sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC
, b
);
979 bname
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
980 buddy
= g_hash_table_lookup(sipe_private
->buddies
, bname
);
982 gboolean in_sipe_groups
= FALSE
;
983 GSList
*entry2
= buddy
->groups
;
985 struct sipe_group
*group
= entry2
->data
;
986 if (sipe_strequal(group
->name
, gname
)) {
987 in_sipe_groups
= TRUE
;
990 entry2
= entry2
->next
;
992 if(!in_sipe_groups
) {
993 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname
, gname
);
994 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
997 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname
, gname
);
998 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1002 entry
= entry
->next
;
1004 g_slist_free(buddies
);
1008 * A callback for g_hash_table_foreach
1010 static void sipe_buddy_subscribe_cb(char *buddy_name
,
1011 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1012 struct sipe_core_private
*sipe_private
)
1014 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
1015 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1016 guint time_range
= (g_hash_table_size(sipe_private
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1017 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
1019 sipe_schedule_mseconds(sipe_private
,
1021 g_strdup(buddy_name
),
1023 sipe_subscribe_presence_single
,
1025 g_free(action_name
);
1028 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1031 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1032 int len
= msg
->bodylen
;
1034 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1035 const sipe_xml
*item
;
1038 const sipe_xml
*group_node
;
1039 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1043 /* Convert the contact from XML to backend Buddies */
1044 isc
= sipe_xml_parse(msg
->body
, len
);
1049 /* [MS-SIP]: deltaNum MUST be non-zero */
1050 delta
= sipe_xml_int_attribute(isc
, "deltaNum", 0);
1052 sipe_private
->deltanum_contacts
= delta
;
1055 if (sipe_strequal(sipe_xml_name(isc
), "contactList")) {
1058 for (group_node
= sipe_xml_child(isc
, "group"); group_node
; group_node
= sipe_xml_twin(group_node
)) {
1059 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1060 const char *name
= sipe_xml_attribute(group_node
, "name");
1062 if (g_str_has_prefix(name
, "~")) {
1063 name
= _("Other Contacts");
1065 group
->name
= g_strdup(name
);
1066 group
->id
= (int)g_ascii_strtod(sipe_xml_attribute(group_node
, "id"), NULL
);
1068 sipe_group_add(sipe_private
, group
);
1071 // Make sure we have at least one group
1072 if (g_slist_length(sipe_private
->groups
) == 0) {
1073 sipe_group_create(sipe_private
, _("Other Contacts"), NULL
);
1076 /* Parse contacts */
1077 for (item
= sipe_xml_child(isc
, "contact"); item
; item
= sipe_xml_twin(item
)) {
1078 const gchar
*uri
= sipe_xml_attribute(item
, "uri");
1079 const gchar
*name
= sipe_xml_attribute(item
, "name");
1081 struct sipe_buddy
*buddy
= NULL
;
1083 gchar
**item_groups
;
1086 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1087 tmp
= sip_uri_from_name(uri
);
1088 buddy_name
= g_ascii_strdown(tmp
, -1);
1091 /* assign to group Other Contacts if nothing else received */
1092 tmp
= g_strdup(sipe_xml_attribute(item
, "groups"));
1094 struct sipe_group
*group
= sipe_group_find_by_name(sipe_private
, _("Other Contacts"));
1096 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1098 item_groups
= g_strsplit(tmp
, " ", 0);
1101 while (item_groups
[i
]) {
1102 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
, g_ascii_strtod(item_groups
[i
], NULL
));
1104 // If couldn't find the right group for this contact, just put them in the first group we have
1105 if (group
== NULL
&& g_slist_length(sipe_private
->groups
) > 0) {
1106 group
= sipe_private
->groups
->data
;
1109 if (group
!= NULL
) {
1111 sipe_backend_buddy b
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, buddy_name
, group
->name
);
1113 b
= sipe_backend_buddy_add(SIPE_CORE_PUBLIC
, buddy_name
, uri
, group
->name
);
1114 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name
, uri
);
1117 b_alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, b
);
1118 if (sipe_strcase_equal(uri
, b_alias
)) {
1119 if (name
!= NULL
&& strlen(name
) != 0) {
1120 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, b
, name
);
1122 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name
, name
);
1128 buddy
= g_new0(struct sipe_buddy
, 1);
1129 buddy
->name
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1130 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1132 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy
->name
);
1135 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1137 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy
->name
, group
->name
);
1139 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1144 } // while, contact groups
1145 g_strfreev(item_groups
);
1150 sipe_cleanup_local_blist(sipe_private
);
1152 /* Add self-contact if not there yet. 2005 systems. */
1153 /* This will resemble subscription to roaming_self in 2007 systems */
1154 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1155 gchar
*self_uri
= sip_uri_self(sipe_private
);
1156 struct sipe_buddy
*buddy
= g_hash_table_lookup(sipe_private
->buddies
, self_uri
);
1159 buddy
= g_new0(struct sipe_buddy
, 1);
1160 buddy
->name
= g_strdup(self_uri
);
1161 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1168 /* subscribe to buddies */
1169 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1170 if (sip
->batched_support
) {
1171 sipe_subscribe_presence_batched(sipe_private
);
1173 g_hash_table_foreach(sipe_private
->buddies
,
1174 (GHFunc
)sipe_buddy_subscribe_cb
,
1177 sip
->subscribed_buddies
= TRUE
;
1179 /* for 2005 systems schedule contacts' status update
1180 * based on their calendar information
1182 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1183 sipe_ocs2005_schedule_status_update(sipe_private
, time(NULL
));
1189 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1195 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1199 /* [MS-SIP]: deltaNum MUST be non-zero */
1200 delta
= sipe_xml_int_attribute(xml
, "deltaNum", 0);
1202 sipe_private
->deltanum_acl
= delta
;
1208 struct sipe_auth_job
{
1210 struct sipe_core_private
*sipe_private
;
1213 void sipe_core_contact_allow_deny(struct sipe_core_public
*sipe_public
,
1217 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
1220 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who
);
1222 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who
);
1225 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1226 sipe_ocs2007_change_access_level(sipe_private
,
1227 (allow
? -1 : 32000),
1229 sipe_get_no_sip_uri(who
));
1231 sip_soap_ocs2005_setacl(sipe_private
, who
, allow
);
1236 static void sipe_auth_user_cb(gpointer data
)
1238 struct sipe_auth_job
*job
= (struct sipe_auth_job
*) data
;
1241 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
,
1247 static void sipe_deny_user_cb(gpointer data
)
1249 struct sipe_auth_job
*job
= (struct sipe_auth_job
*) data
;
1252 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
,
1259 static void sipe_process_presence_wpending (struct sipe_core_private
*sipe_private
,
1260 struct sipmsg
* msg
)
1263 const sipe_xml
*watcher
;
1264 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1265 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1267 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1269 watchers
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1270 if (!watchers
) return;
1272 for (watcher
= sipe_xml_child(watchers
, "watcher"); watcher
; watcher
= sipe_xml_twin(watcher
)) {
1273 gchar
* remote_user
= g_strdup(sipe_xml_attribute(watcher
, "uri"));
1274 gchar
* alias
= g_strdup(sipe_xml_attribute(watcher
, "displayName"));
1275 gboolean on_list
= g_hash_table_lookup(sipe_private
->buddies
, remote_user
) != NULL
;
1277 // TODO pull out optional displayName to pass as alias
1279 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1280 job
->who
= remote_user
;
1281 job
->sipe_private
= sipe_private
;
1282 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC
,
1293 sipe_xml_free(watchers
);
1297 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
1298 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
1302 GSList
**buddies
= user_data
;
1303 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
1305 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
1306 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
1307 const sipe_xml
*xn_category
;
1310 * automaton: presence is never expected to change
1312 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
1314 for (xn_category
= sipe_xml_child(xml
, "category");
1316 xn_category
= sipe_xml_twin(xn_category
)) {
1317 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
1319 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
1321 char *boolean
= sipe_xml_data(node
);
1322 if (sipe_strequal(boolean
, "true")) {
1323 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
1334 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
1341 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
1346 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
1347 gchar
*action_name
= sipe_utils_presence_key(who
);
1349 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
1352 strstr(ctype
, "multipart") &&
1353 (strstr(ctype
, "application/rlmi+xml") ||
1354 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
1355 GSList
*buddies
= NULL
;
1357 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
1360 sipe_subscribe_presence_batched_schedule(sipe_private
,
1367 sipe_schedule_seconds(sipe_private
,
1371 sipe_subscribe_presence_single
,
1373 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
1375 g_free(action_name
);
1379 * Dispatcher for all incoming subscription information
1380 * whether it comes from NOTIFY, BENOTIFY requests or
1381 * piggy-backed to subscription's OK responce.
1383 * @param request whether initiated from BE/NOTIFY request or OK-response message.
1384 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
1386 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
1388 gboolean request
, gboolean benotify
)
1390 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1391 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
1392 const gchar
*event
= sipmsg_find_header(msg
, "Event");
1393 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
1395 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
1397 /* implicit subscriptions */
1398 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
1399 sipe_process_imdn(sipe_private
, msg
);
1403 /* for one off subscriptions (send with Expire: 0) */
1404 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
1406 sipe_process_provisioning_v2(sipe_private
, msg
);
1408 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
1410 sipe_process_provisioning(sipe_private
, msg
);
1412 else if (sipe_strcase_equal(event
, "presence"))
1414 sipe_process_presence(sipe_private
, msg
);
1416 else if (sipe_strcase_equal(event
, "registration-notify"))
1418 sipe_process_registration_notify(sipe_private
, msg
);
1421 if (!subscription_state
|| strstr(subscription_state
, "active"))
1423 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
1425 sipe_process_roaming_contacts(sipe_private
, msg
);
1427 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
1429 sipe_ocs2007_process_roaming_self(sipe_private
, msg
);
1431 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
1433 sipe_process_roaming_acl(sipe_private
, msg
);
1435 else if (sipe_strcase_equal(event
, "presence.wpending"))
1437 sipe_process_presence_wpending(sipe_private
, msg
);
1439 else if (sipe_strcase_equal(event
, "conference"))
1441 sipe_process_conference(sipe_private
, msg
);
1446 /* The server sends status 'terminated' */
1447 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
1448 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
1449 gchar
*key
= sipe_utils_subscription_key(event
, who
);
1451 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
1454 sipe_subscriptions_remove(sipe_private
, key
);
1458 if (!request
&& event
) {
1459 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
1460 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
1461 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
1464 /* 2 min ahead of expiration */
1465 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
1467 if (sipe_strcase_equal(event
, "presence.wpending") &&
1468 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
1470 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
1471 sipe_schedule_seconds(sipe_private
,
1475 sipe_subscribe_presence_wpending
,
1477 g_free(action_name
);
1479 else if (sipe_strcase_equal(event
, "presence") &&
1480 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
1482 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
1483 gchar
*action_name
= sipe_utils_presence_key(who
);
1485 if (sip
->batched_support
) {
1486 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
1489 sipe_schedule_seconds(sipe_private
,
1493 sipe_subscribe_presence_single
,
1495 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
1497 g_free(action_name
);
1503 /* The client responses on received a NOTIFY message */
1504 if (request
&& !benotify
)
1506 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);