calendar: clean up API to remove dependency
[siplcs.git] / src / core / sipe-notify.c
blobc4a3ddf495c2ecb9e7b6d0d2cab2f3cceccbdfea
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
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
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include "sipe-common.h"
38 #include "sipmsg.h"
39 #include "sip-csta.h"
40 #include "sip-soap.h"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-buddy.h"
44 #include "sipe-cal.h"
45 #include "sipe-conf.h"
46 #include "sipe-core.h"
47 #include "sipe-core-private.h"
48 #include "sipe-group.h"
49 #include "sipe-media.h"
50 #include "sipe-mime.h"
51 #include "sipe-nls.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"
58 #include "sipe-utils.h"
59 #include "sipe-xml.h"
61 /* OCS2005 */
62 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
63 struct sipmsg *msg)
65 sipe_xml *xn_provision;
66 const sipe_xml *node;
68 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
69 if ((node = sipe_xml_child(xn_provision, "user"))) {
70 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
71 if ((node = sipe_xml_child(node, "line"))) {
72 const gchar *line_uri = sipe_xml_attribute(node, "uri");
73 const gchar *server = sipe_xml_attribute(node, "server");
74 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
75 sip_csta_open(sipe_private, line_uri, server);
78 sipe_xml_free(xn_provision);
81 /* OCS2007+ */
82 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
83 struct sipmsg *msg)
85 sipe_xml *xn_provision_group_list;
86 const sipe_xml *node;
88 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
90 /* provisionGroup */
91 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup"); node; node = sipe_xml_twin(node)) {
92 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node, "name"))) {
93 g_free(sipe_private->focus_factory_uri);
94 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
95 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
96 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
98 #ifdef HAVE_VV
99 g_free(sipe_private->mras_uri);
100 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
101 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
102 sipe_private->mras_uri ? sipe_private->mras_uri : "");
104 if (sipe_private->mras_uri)
105 sipe_media_get_av_edge_credentials(sipe_private);
106 #endif
107 break;
110 sipe_xml_free(xn_provision_group_list);
113 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
114 const gchar *data, unsigned len)
116 sipe_xml *xn_list;
117 const sipe_xml *xn_resource;
118 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
119 g_free, NULL);
120 GSList *server;
121 gchar *host;
123 xn_list = sipe_xml_parse(data, len);
125 for (xn_resource = sipe_xml_child(xn_list, "resource");
126 xn_resource;
127 xn_resource = sipe_xml_twin(xn_resource) )
129 const char *uri, *state;
130 const sipe_xml *xn_instance;
132 xn_instance = sipe_xml_child(xn_resource, "instance");
133 if (!xn_instance) continue;
135 uri = sipe_xml_attribute(xn_resource, "uri");
136 state = sipe_xml_attribute(xn_instance, "state");
137 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
139 if (strstr(state, "resubscribe")) {
140 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
142 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
143 gchar *user = g_strdup(uri);
144 host = g_strdup(poolFqdn);
145 server = g_hash_table_lookup(servers, host);
146 server = g_slist_append(server, user);
147 g_hash_table_insert(servers, host, server);
148 } else {
149 sipe_subscribe_presence_single(sipe_private,
150 (void *) uri);
155 /* Send out any deferred poolFqdn subscriptions */
156 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
157 g_hash_table_destroy(servers);
159 sipe_xml_free(xn_list);
163 * Update user phone
164 * Suitable for both 2005 and 2007 systems.
166 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
167 * @param phone_type
168 * @param phone may be modified to strip white space
169 * @param phone_display_string may be modified to strip white space
171 static void
172 sipe_update_user_phone(struct sipe_core_private *sipe_private,
173 const gchar *uri,
174 const gchar *phone_type,
175 gchar *phone,
176 gchar *phone_display_string)
178 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
179 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
181 if(!phone || strlen(phone) == 0) return;
183 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
184 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
185 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
186 } else if (sipe_strequal(phone_type, "home")) {
187 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
188 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
189 } else if (sipe_strequal(phone_type, "other")) {
190 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
191 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
192 } else if (sipe_strequal(phone_type, "custom1")) {
193 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
194 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
197 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
198 if (phone_display_string) {
199 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
203 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
204 const gchar *data,
205 unsigned len)
207 char *activity = NULL;
208 const char *epid;
209 const char *status_id = NULL;
210 const char *name;
211 char *uri;
212 char *self_uri = sip_uri_self(sipe_private);
213 int avl;
214 int act;
215 const char *device_name = NULL;
216 const char *cal_start_time = NULL;
217 const char *cal_granularity = NULL;
218 char *cal_free_busy_base64 = NULL;
219 struct sipe_buddy *sbuddy;
220 const sipe_xml *node;
221 sipe_xml *xn_presentity;
222 const sipe_xml *xn_availability;
223 const sipe_xml *xn_activity;
224 const sipe_xml *xn_display_name;
225 const sipe_xml *xn_email;
226 const sipe_xml *xn_phone_number;
227 const sipe_xml *xn_userinfo;
228 const sipe_xml *xn_note;
229 const sipe_xml *xn_oof;
230 const sipe_xml *xn_state;
231 const sipe_xml *xn_contact;
232 char *note;
233 int user_avail;
234 const char *user_avail_nil;
235 int res_avail;
236 time_t user_avail_since = 0;
237 time_t activity_since = 0;
239 /* fix for Reuters environment on Linux */
240 if (data && strstr(data, "encoding=\"utf-16\"")) {
241 char *tmp_data;
242 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
243 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
244 g_free(tmp_data);
245 } else {
246 xn_presentity = sipe_xml_parse(data, len);
249 xn_availability = sipe_xml_child(xn_presentity, "availability");
250 xn_activity = sipe_xml_child(xn_presentity, "activity");
251 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
252 xn_email = sipe_xml_child(xn_presentity, "email");
253 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
254 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
255 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
256 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
257 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
258 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
259 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
260 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
261 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
262 note = xn_note ? sipe_xml_data(xn_note) : NULL;
264 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
265 user_avail = 0;
266 user_avail_since = 0;
269 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
270 uri = sip_uri_from_name(name);
271 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
272 epid = sipe_xml_attribute(xn_availability, "epid");
273 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
275 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
276 activity = g_strdup(sipe_ocs2005_activity_description(act));
277 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
278 if (user_avail > res_avail) {
279 res_avail = user_avail;
280 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail);
283 if (xn_display_name) {
284 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
285 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
286 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
287 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
288 char *tel_uri = sip_to_tel_uri(phone_number);
290 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
291 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
292 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
293 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
295 g_free(tel_uri);
296 g_free(phone_label);
297 g_free(phone_number);
298 g_free(email);
299 g_free(display_name);
302 if (xn_contact) {
303 /* tel */
304 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
306 /* Ex.: <tel type="work">tel:+3222220000</tel> */
307 const char *phone_type = sipe_xml_attribute(node, "type");
308 char* phone = sipe_xml_data(node);
310 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
312 g_free(phone);
316 /* devicePresence */
317 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
318 const sipe_xml *xn_device_name;
319 const sipe_xml *xn_calendar_info;
320 const sipe_xml *xn_state;
321 char *state;
323 /* deviceName */
324 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
325 xn_device_name = sipe_xml_child(node, "deviceName");
326 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
329 /* calendarInfo */
330 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
331 if (xn_calendar_info) {
332 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
334 if (cal_start_time) {
335 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
336 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
338 if (cal_start_time_t_tmp > cal_start_time_t) {
339 cal_start_time = cal_start_time_tmp;
340 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
341 g_free(cal_free_busy_base64);
342 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
344 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);
346 } else {
347 cal_start_time = cal_start_time_tmp;
348 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
349 g_free(cal_free_busy_base64);
350 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
352 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);
356 /* state */
357 xn_state = sipe_xml_child(node, "states/state");
358 if (xn_state) {
359 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
360 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
362 state = sipe_xml_data(xn_state);
363 if (dev_avail_since > user_avail_since &&
364 dev_avail >= res_avail)
366 const gchar *new_desc;
367 res_avail = dev_avail;
368 if (!is_empty(state)) {
369 if (sipe_strequal(state, sipe_backend_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
370 g_free(activity);
371 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
372 } else if (sipe_strequal(state, "presenting")) {
373 g_free(activity);
374 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
375 } else {
376 activity = state;
377 state = NULL;
379 activity_since = dev_avail_since;
381 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail);
382 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
383 if (new_desc) {
384 g_free(activity);
385 activity = g_strdup(new_desc);
388 g_free(state);
392 /* oof */
393 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
394 g_free(activity);
395 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
396 activity_since = 0;
399 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
400 if (sbuddy)
402 g_free(sbuddy->activity);
403 sbuddy->activity = activity;
404 activity = NULL;
406 sbuddy->activity_since = activity_since;
408 sbuddy->user_avail = user_avail;
409 sbuddy->user_avail_since = user_avail_since;
411 g_free(sbuddy->note);
412 sbuddy->note = NULL;
413 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
415 sbuddy->is_oof_note = (xn_oof != NULL);
417 g_free(sbuddy->device_name);
418 sbuddy->device_name = NULL;
419 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
421 if (!is_empty(cal_free_busy_base64)) {
422 g_free(sbuddy->cal_start_time);
423 sbuddy->cal_start_time = g_strdup(cal_start_time);
425 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
427 g_free(sbuddy->cal_free_busy_base64);
428 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
429 cal_free_busy_base64 = NULL;
431 g_free(sbuddy->cal_free_busy);
432 sbuddy->cal_free_busy = NULL;
435 sbuddy->last_non_cal_status_id = status_id;
436 g_free(sbuddy->last_non_cal_activity);
437 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
439 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
440 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
442 if (sbuddy->is_oof_note)
443 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
444 else
445 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
447 g_free(sipe_private->note);
448 sipe_private->note = g_strdup(sbuddy->note);
450 sipe_private->note_since = time(NULL);
453 sipe_status_set_token(sipe_private,
454 sbuddy->last_non_cal_status_id);
457 g_free(cal_free_busy_base64);
458 g_free(activity);
460 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
461 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status_id);
463 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
464 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
467 g_free(note);
468 sipe_xml_free(xn_presentity);
469 g_free(uri);
470 g_free(self_uri);
473 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
474 const gchar *data,
475 unsigned len)
477 const char *uri;
478 sipe_xml *xn_categories;
479 const sipe_xml *xn_category;
480 const char *status = NULL;
481 gboolean do_update_status = FALSE;
482 gboolean has_note_cleaned = FALSE;
483 gboolean has_free_busy_cleaned = FALSE;
485 xn_categories = sipe_xml_parse(data, len);
486 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
488 for (xn_category = sipe_xml_child(xn_categories, "category");
489 xn_category ;
490 xn_category = sipe_xml_twin(xn_category) )
492 const sipe_xml *xn_node;
493 const char *tmp;
494 const char *attrVar = sipe_xml_attribute(xn_category, "name");
495 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
496 sipe_utils_str_to_time(tmp) : 0;
498 /* contactCard */
499 if (sipe_strequal(attrVar, "contactCard"))
501 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
503 if (card) {
504 const sipe_xml *node;
505 /* identity - Display Name and email */
506 node = sipe_xml_child(card, "identity");
507 if (node) {
508 char* display_name = sipe_xml_data(
509 sipe_xml_child(node, "name/displayName"));
510 char* email = sipe_xml_data(
511 sipe_xml_child(node, "email"));
513 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
514 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
516 g_free(display_name);
517 g_free(email);
519 /* company */
520 node = sipe_xml_child(card, "company");
521 if (node) {
522 char* company = sipe_xml_data(node);
523 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
524 g_free(company);
526 /* department */
527 node = sipe_xml_child(card, "department");
528 if (node) {
529 char* department = sipe_xml_data(node);
530 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
531 g_free(department);
533 /* title */
534 node = sipe_xml_child(card, "title");
535 if (node) {
536 char* title = sipe_xml_data(node);
537 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
538 g_free(title);
540 /* office */
541 node = sipe_xml_child(card, "office");
542 if (node) {
543 char* office = sipe_xml_data(node);
544 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
545 g_free(office);
547 /* site (url) */
548 node = sipe_xml_child(card, "url");
549 if (node) {
550 char* site = sipe_xml_data(node);
551 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
552 g_free(site);
554 /* phone */
555 for (node = sipe_xml_child(card, "phone");
556 node;
557 node = sipe_xml_twin(node))
559 const char *phone_type = sipe_xml_attribute(node, "type");
560 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
561 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
563 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
565 g_free(phone);
566 g_free(phone_display_string);
568 /* address */
569 for (node = sipe_xml_child(card, "address");
570 node;
571 node = sipe_xml_twin(node))
573 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
574 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
575 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
576 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
577 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
578 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
580 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
581 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
582 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
583 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
584 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
586 g_free(street);
587 g_free(city);
588 g_free(state);
589 g_free(zipcode);
590 g_free(country_code);
592 break;
597 /* note */
598 else if (sipe_strequal(attrVar, "note"))
600 if (uri) {
601 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
603 if (!has_note_cleaned) {
604 has_note_cleaned = TRUE;
606 g_free(sbuddy->note);
607 sbuddy->note = NULL;
608 sbuddy->is_oof_note = FALSE;
609 sbuddy->note_since = publish_time;
611 do_update_status = TRUE;
613 if (sbuddy && (publish_time >= sbuddy->note_since)) {
614 /* clean up in case no 'note' element is supplied
615 * which indicate note removal in client
617 g_free(sbuddy->note);
618 sbuddy->note = NULL;
619 sbuddy->is_oof_note = FALSE;
620 sbuddy->note_since = publish_time;
622 xn_node = sipe_xml_child(xn_category, "note/body");
623 if (xn_node) {
624 char *tmp;
625 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
626 g_free(tmp);
627 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
628 sbuddy->note_since = publish_time;
630 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
631 uri, sbuddy->note ? sbuddy->note : "");
633 /* to trigger UI refresh in case no status info is supplied in this update */
634 do_update_status = TRUE;
638 /* state */
639 else if(sipe_strequal(attrVar, "state"))
641 char *tmp;
642 int availability;
643 const sipe_xml *xn_availability;
644 const sipe_xml *xn_activity;
645 const sipe_xml *xn_meeting_subject;
646 const sipe_xml *xn_meeting_location;
647 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
649 xn_node = sipe_xml_child(xn_category, "state");
650 if (!xn_node) continue;
651 xn_availability = sipe_xml_child(xn_node, "availability");
652 if (!xn_availability) continue;
653 xn_activity = sipe_xml_child(xn_node, "activity");
654 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
655 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
657 tmp = sipe_xml_data(xn_availability);
658 availability = atoi(tmp);
659 g_free(tmp);
661 /* activity, meeting_subject, meeting_location */
662 if (sbuddy) {
663 const gchar *tmp;
665 /* activity */
666 g_free(sbuddy->activity);
667 sbuddy->activity = NULL;
668 if (xn_activity) {
669 const char *token = sipe_xml_attribute(xn_activity, "token");
670 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
672 /* from token */
673 if (!is_empty(token)) {
674 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_backend_token_to_activity(token)));
676 /* from custom element */
677 if (xn_custom) {
678 char *custom = sipe_xml_data(xn_custom);
680 if (!is_empty(custom)) {
681 g_free(sbuddy->activity);
682 sbuddy->activity = custom;
683 custom = NULL;
685 g_free(custom);
688 /* meeting_subject */
689 g_free(sbuddy->meeting_subject);
690 sbuddy->meeting_subject = NULL;
691 if (xn_meeting_subject) {
692 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
694 if (!is_empty(meeting_subject)) {
695 sbuddy->meeting_subject = meeting_subject;
696 meeting_subject = NULL;
698 g_free(meeting_subject);
700 /* meeting_location */
701 g_free(sbuddy->meeting_location);
702 sbuddy->meeting_location = NULL;
703 if (xn_meeting_location) {
704 char *meeting_location = sipe_xml_data(xn_meeting_location);
706 if (!is_empty(meeting_location)) {
707 sbuddy->meeting_location = meeting_location;
708 meeting_location = NULL;
710 g_free(meeting_location);
713 status = sipe_ocs2007_status_from_legacy_availability(availability);
714 tmp = sipe_ocs2007_legacy_activity_description(availability);
715 if (sbuddy->activity && tmp) {
716 gchar *tmp2 = sbuddy->activity;
718 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, tmp);
719 g_free(tmp2);
720 } else if (tmp) {
721 sbuddy->activity = g_strdup(tmp);
725 do_update_status = TRUE;
727 /* calendarData */
728 else if(sipe_strequal(attrVar, "calendarData"))
730 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
731 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
732 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
734 if (sbuddy && xn_free_busy) {
735 if (!has_free_busy_cleaned) {
736 has_free_busy_cleaned = TRUE;
738 g_free(sbuddy->cal_start_time);
739 sbuddy->cal_start_time = NULL;
741 g_free(sbuddy->cal_free_busy_base64);
742 sbuddy->cal_free_busy_base64 = NULL;
744 g_free(sbuddy->cal_free_busy);
745 sbuddy->cal_free_busy = NULL;
747 sbuddy->cal_free_busy_published = publish_time;
750 if (publish_time >= sbuddy->cal_free_busy_published) {
751 g_free(sbuddy->cal_start_time);
752 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
754 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
755 15 : 0;
757 g_free(sbuddy->cal_free_busy_base64);
758 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
760 g_free(sbuddy->cal_free_busy);
761 sbuddy->cal_free_busy = NULL;
763 sbuddy->cal_free_busy_published = publish_time;
765 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);
769 if (sbuddy && xn_working_hours) {
770 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
775 if (do_update_status) {
776 if (!status) {
777 /* no status category in this update,
778 using contact's current status */
779 status = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
780 uri);
783 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
784 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status);
787 sipe_xml_free(xn_categories);
790 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
791 const gchar *uri,
792 const gchar *activity,
793 gboolean is_online)
795 if (is_online) {
796 const gchar *status_id = NULL;
797 if (activity) {
798 if (sipe_strequal(activity,
799 sipe_backend_activity_to_token(SIPE_ACTIVITY_BUSY))) {
800 status_id = sipe_backend_activity_to_token(SIPE_ACTIVITY_BUSY);
801 } else if (sipe_strequal(activity,
802 sipe_backend_activity_to_token(SIPE_ACTIVITY_AWAY))) {
803 status_id = sipe_backend_activity_to_token(SIPE_ACTIVITY_AWAY);
807 if (!status_id) {
808 status_id = sipe_backend_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
811 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
812 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status_id);
813 } else {
814 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
815 sipe_backend_activity_to_token(SIPE_ACTIVITY_OFFLINE));
819 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
820 const gchar *data,
821 unsigned len)
823 gchar *uri;
824 gchar *getbasic;
825 gchar *activity = NULL;
826 sipe_xml *pidf;
827 const sipe_xml *basicstatus = NULL, *tuple, *status;
828 gboolean isonline = FALSE;
829 const sipe_xml *display_name_node;
831 pidf = sipe_xml_parse(data, len);
832 if (!pidf) {
833 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
834 return;
837 if ((tuple = sipe_xml_child(pidf, "tuple")))
839 if ((status = sipe_xml_child(tuple, "status"))) {
840 basicstatus = sipe_xml_child(status, "basic");
844 if (!basicstatus) {
845 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
846 sipe_xml_free(pidf);
847 return;
850 getbasic = sipe_xml_data(basicstatus);
851 if (!getbasic) {
852 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
853 sipe_xml_free(pidf);
854 return;
857 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
858 if (strstr(getbasic, "open")) {
859 isonline = TRUE;
861 g_free(getbasic);
863 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
865 display_name_node = sipe_xml_child(pidf, "display-name");
866 if (display_name_node) {
867 char * display_name = sipe_xml_data(display_name_node);
869 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
870 g_free(display_name);
873 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
874 if ((status = sipe_xml_child(tuple, "status"))) {
875 if ((basicstatus = sipe_xml_child(status, "activities"))) {
876 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
877 activity = sipe_xml_data(basicstatus);
878 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
884 sipe_buddy_status_from_activity(sipe_private,
885 uri,
886 activity,
887 isonline);
889 g_free(activity);
890 g_free(uri);
891 sipe_xml_free(pidf);
894 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
895 const GSList *fields,
896 const gchar *body,
897 gsize length)
899 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
901 if (strstr(type,"application/rlmi+xml")) {
902 process_incoming_notify_rlmi_resub(user_data, body, length);
903 } else if (strstr(type, "text/xml+msrtc.pidf")) {
904 process_incoming_notify_msrtc(user_data, body, length);
905 } else {
906 process_incoming_notify_rlmi(user_data, body, length);
910 static void sipe_process_presence(struct sipe_core_private *sipe_private,
911 struct sipmsg *msg)
913 const char *ctype = sipmsg_find_header(msg, "Content-Type");
915 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
917 if (ctype &&
918 (strstr(ctype, "application/rlmi+xml") ||
919 strstr(ctype, "application/msrtc-event-categories+xml")))
921 if (strstr(ctype, "multipart"))
923 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
925 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
927 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
929 else if(strstr(ctype, "application/rlmi+xml"))
931 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
934 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
936 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
938 else
940 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
945 * Fires on deregistration event initiated by server.
946 * [MS-SIPREGE] SIP extension.
948 * OCS2007 Example
950 * Content-Type: text/registration-event
951 * subscription-state: terminated;expires=0
952 * ms-diagnostics-public: 4141;reason="User disabled"
954 * deregistered;event=rejected
956 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
957 struct sipmsg *msg)
959 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
960 gchar *event = NULL;
961 gchar *reason = NULL;
962 gchar *warning;
964 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
966 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
967 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
968 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
969 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
970 } else {
971 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
972 return;
975 reason = sipmsg_get_ms_diagnostics_reason(msg);
976 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
977 if (!reason) { // for LCS2005
978 if (event && sipe_strcase_equal(event, "unregistered")) {
979 //reason = g_strdup(_("User logged out")); // [MS-OCER]
980 reason = g_strdup(_("you are already signed in at another location"));
981 } else if (event && sipe_strcase_equal(event, "rejected")) {
982 reason = g_strdup(_("user disabled")); // [MS-OCER]
983 } else if (event && sipe_strcase_equal(event, "deactivated")) {
984 reason = g_strdup(_("user moved")); // [MS-OCER]
987 g_free(event);
988 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
989 g_free(reason);
991 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
992 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
993 warning);
994 g_free(warning);
999 * Removes entries from local buddy list
1000 * that does not correspond ones in the roaming contact list.
1002 static void sipe_cleanup_local_blist(struct sipe_core_private *sipe_private)
1004 GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC,
1005 NULL, NULL);
1006 GSList *entry = buddies;
1007 struct sipe_buddy *buddy;
1008 sipe_backend_buddy b;
1009 gchar *bname;
1010 gchar *gname;
1012 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies));
1013 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private->buddies));
1014 while (entry) {
1015 b = entry->data;
1016 gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC, b);
1017 bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
1018 buddy = g_hash_table_lookup(sipe_private->buddies, bname);
1019 if(buddy) {
1020 gboolean in_sipe_groups = FALSE;
1021 GSList *entry2 = buddy->groups;
1022 while (entry2) {
1023 struct sipe_group *group = entry2->data;
1024 if (sipe_strequal(group->name, gname)) {
1025 in_sipe_groups = TRUE;
1026 break;
1028 entry2 = entry2->next;
1030 if(!in_sipe_groups) {
1031 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname, gname);
1032 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1034 } else {
1035 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname, gname);
1036 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1038 g_free(bname);
1039 g_free(gname);
1040 entry = entry->next;
1042 g_slist_free(buddies);
1046 * A callback for g_hash_table_foreach
1048 static void sipe_buddy_subscribe_cb(char *buddy_name,
1049 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1050 struct sipe_core_private *sipe_private)
1052 gchar *action_name = sipe_utils_presence_key(buddy_name);
1053 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1054 guint time_range = (g_hash_table_size(sipe_private->buddies) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1055 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
1057 sipe_schedule_mseconds(sipe_private,
1058 action_name,
1059 g_strdup(buddy_name),
1060 timeout,
1061 sipe_subscribe_presence_single,
1062 g_free);
1063 g_free(action_name);
1066 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1067 struct sipmsg *msg)
1069 int len = msg->bodylen;
1071 const gchar *tmp = sipmsg_find_header(msg, "Event");
1072 const sipe_xml *item;
1073 sipe_xml *isc;
1074 guint delta;
1075 const sipe_xml *group_node;
1076 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1077 return FALSE;
1080 /* Convert the contact from XML to backend Buddies */
1081 isc = sipe_xml_parse(msg->body, len);
1082 if (!isc) {
1083 return FALSE;
1086 /* [MS-SIP]: deltaNum MUST be non-zero */
1087 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1088 if (delta) {
1089 sipe_private->deltanum_contacts = delta;
1092 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1094 /* Parse groups */
1095 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node)) {
1096 struct sipe_group * group = g_new0(struct sipe_group, 1);
1097 const char *name = sipe_xml_attribute(group_node, "name");
1099 if (g_str_has_prefix(name, "~")) {
1100 name = _("Other Contacts");
1102 group->name = g_strdup(name);
1103 group->id = (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"), NULL);
1105 sipe_group_add(sipe_private, group);
1108 // Make sure we have at least one group
1109 if (g_slist_length(sipe_private->groups) == 0) {
1110 sipe_group_create(sipe_private, _("Other Contacts"), NULL);
1113 /* Parse contacts */
1114 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1115 const gchar *uri = sipe_xml_attribute(item, "uri");
1116 const gchar *name = sipe_xml_attribute(item, "name");
1117 gchar *buddy_name;
1118 struct sipe_buddy *buddy = NULL;
1119 gchar *tmp;
1120 gchar **item_groups;
1121 int i = 0;
1123 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1124 tmp = sip_uri_from_name(uri);
1125 buddy_name = g_ascii_strdown(tmp, -1);
1126 g_free(tmp);
1128 /* assign to group Other Contacts if nothing else received */
1129 tmp = g_strdup(sipe_xml_attribute(item, "groups"));
1130 if(is_empty(tmp)) {
1131 struct sipe_group *group = sipe_group_find_by_name(sipe_private, _("Other Contacts"));
1132 g_free(tmp);
1133 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1135 item_groups = g_strsplit(tmp, " ", 0);
1136 g_free(tmp);
1138 while (item_groups[i]) {
1139 struct sipe_group *group = sipe_group_find_by_id(sipe_private, g_ascii_strtod(item_groups[i], NULL));
1141 // If couldn't find the right group for this contact, just put them in the first group we have
1142 if (group == NULL && g_slist_length(sipe_private->groups) > 0) {
1143 group = sipe_private->groups->data;
1146 if (group != NULL) {
1147 gchar *b_alias;
1148 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, buddy_name, group->name);
1149 if (!b){
1150 b = sipe_backend_buddy_add(SIPE_CORE_PUBLIC, buddy_name, uri, group->name);
1151 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name, uri);
1154 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b);
1155 if (sipe_strcase_equal(uri, b_alias)) {
1156 if (name != NULL && strlen(name) != 0) {
1157 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, b, name);
1159 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name, name);
1162 g_free(b_alias);
1164 if (!buddy) {
1165 buddy = g_new0(struct sipe_buddy, 1);
1166 buddy->name = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
1167 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
1169 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy->name);
1172 buddy->groups = slist_insert_unique_sorted(buddy->groups, group, (GCompareFunc)sipe_group_compare);
1174 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy->name, group->name);
1175 } else {
1176 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1177 name);
1180 i++;
1181 } // while, contact groups
1182 g_strfreev(item_groups);
1183 g_free(buddy_name);
1185 } // for, contacts
1187 sipe_cleanup_local_blist(sipe_private);
1189 /* Add self-contact if not there yet. 2005 systems. */
1190 /* This will resemble subscription to roaming_self in 2007 systems */
1191 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1192 gchar *self_uri = sip_uri_self(sipe_private);
1193 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies, self_uri);
1195 if (!buddy) {
1196 buddy = g_new0(struct sipe_buddy, 1);
1197 buddy->name = g_strdup(self_uri);
1198 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
1200 g_free(self_uri);
1203 sipe_xml_free(isc);
1205 /* subscribe to buddies */
1206 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) {
1207 /* do it once, then count Expire field to schedule resubscribe */
1208 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
1209 sipe_subscribe_presence_batched(sipe_private);
1210 } else {
1211 g_hash_table_foreach(sipe_private->buddies,
1212 (GHFunc)sipe_buddy_subscribe_cb,
1213 sipe_private);
1215 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES);
1217 /* for 2005 systems schedule contacts' status update
1218 * based on their calendar information
1220 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1221 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1224 return 0;
1227 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1228 struct sipmsg *msg)
1230 guint delta;
1231 sipe_xml *xml;
1233 xml = sipe_xml_parse(msg->body, msg->bodylen);
1234 if (!xml)
1235 return;
1237 /* [MS-SIP]: deltaNum MUST be non-zero */
1238 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1239 if (delta) {
1240 sipe_private->deltanum_acl = delta;
1243 sipe_xml_free(xml);
1246 struct sipe_auth_job {
1247 gchar *who;
1248 struct sipe_core_private *sipe_private;
1251 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1252 const gchar* who,
1253 gboolean allow)
1255 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1257 if (allow) {
1258 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1259 } else {
1260 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1263 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1264 sipe_ocs2007_change_access_level(sipe_private,
1265 (allow ? -1 : 32000),
1266 "user",
1267 sipe_get_no_sip_uri(who));
1268 } else {
1269 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1274 static void sipe_auth_user_cb(gpointer data)
1276 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1277 if (!job) return;
1279 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1280 job->who,
1281 TRUE);
1282 g_free(job);
1285 static void sipe_deny_user_cb(gpointer data)
1287 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1288 if (!job) return;
1290 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1291 job->who,
1292 FALSE);
1293 g_free(job);
1296 /* OCS2005- */
1297 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1298 struct sipmsg * msg)
1300 sipe_xml *watchers;
1301 const sipe_xml *watcher;
1302 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1303 if (msg->response != 0 && msg->response != 200) return;
1305 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1307 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1308 if (!watchers) return;
1310 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1311 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1312 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1313 gboolean on_list = g_hash_table_lookup(sipe_private->buddies, remote_user) != NULL;
1315 // TODO pull out optional displayName to pass as alias
1316 if (remote_user) {
1317 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1318 job->who = remote_user;
1319 job->sipe_private = sipe_private;
1320 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1321 remote_user,
1322 alias,
1323 on_list,
1324 sipe_auth_user_cb,
1325 sipe_deny_user_cb,
1326 (gpointer)job);
1331 sipe_xml_free(watchers);
1332 return;
1335 static void sipe_presence_timeout_mime_cb(gpointer user_data,
1336 SIPE_UNUSED_PARAMETER const GSList *fields,
1337 const gchar *body,
1338 gsize length)
1340 GSList **buddies = user_data;
1341 sipe_xml *xml = sipe_xml_parse(body, length);
1343 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
1344 const gchar *uri = sipe_xml_attribute(xml, "uri");
1345 const sipe_xml *xn_category;
1348 * automaton: presence is never expected to change
1350 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
1352 for (xn_category = sipe_xml_child(xml, "category");
1353 xn_category;
1354 xn_category = sipe_xml_twin(xn_category)) {
1355 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
1356 "contactCard")) {
1357 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
1358 if (node) {
1359 char *boolean = sipe_xml_data(node);
1360 if (sipe_strequal(boolean, "true")) {
1361 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
1362 uri);
1363 uri = NULL;
1365 g_free(boolean);
1367 break;
1371 if (uri) {
1372 *buddies = g_slist_append(*buddies, sip_uri(uri));
1376 sipe_xml_free(xml);
1379 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
1380 struct sipmsg *msg,
1381 const gchar *who,
1382 int timeout)
1384 const char *ctype = sipmsg_find_header(msg, "Content-Type");
1385 gchar *action_name = sipe_utils_presence_key(who);
1387 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
1389 if (ctype &&
1390 strstr(ctype, "multipart") &&
1391 (strstr(ctype, "application/rlmi+xml") ||
1392 strstr(ctype, "application/msrtc-event-categories+xml"))) {
1393 GSList *buddies = NULL;
1395 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
1397 if (buddies)
1398 sipe_subscribe_presence_batched_schedule(sipe_private,
1399 action_name,
1400 who,
1401 buddies,
1402 timeout);
1404 } else {
1405 sipe_schedule_seconds(sipe_private,
1406 action_name,
1407 g_strdup(who),
1408 timeout,
1409 sipe_subscribe_presence_single,
1410 g_free);
1411 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who, timeout);
1413 g_free(action_name);
1417 * Dispatcher for all incoming subscription information
1418 * whether it comes from NOTIFY, BENOTIFY requests or
1419 * piggy-backed to subscription's OK responce.
1421 * @param request whether initiated from BE/NOTIFY request or OK-response message.
1422 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
1424 void process_incoming_notify(struct sipe_core_private *sipe_private,
1425 struct sipmsg *msg,
1426 gboolean request, gboolean benotify)
1428 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1429 const gchar *event = sipmsg_find_header(msg, "Event");
1430 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1432 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1434 /* implicit subscriptions */
1435 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1436 sipe_process_imdn(sipe_private, msg);
1439 if (event) {
1440 /* for one off subscriptions (send with Expire: 0) */
1441 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2"))
1443 sipe_process_provisioning_v2(sipe_private, msg);
1445 else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning"))
1447 sipe_process_provisioning(sipe_private, msg);
1449 else if (sipe_strcase_equal(event, "presence"))
1451 sipe_process_presence(sipe_private, msg);
1453 else if (sipe_strcase_equal(event, "registration-notify"))
1455 sipe_process_registration_notify(sipe_private, msg);
1458 if (!subscription_state || strstr(subscription_state, "active"))
1460 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts"))
1462 sipe_process_roaming_contacts(sipe_private, msg);
1464 else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self"))
1466 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1468 else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL"))
1470 sipe_process_roaming_acl(sipe_private, msg);
1472 else if (sipe_strcase_equal(event, "presence.wpending"))
1474 sipe_process_presence_wpending(sipe_private, msg);
1476 else if (sipe_strcase_equal(event, "conference"))
1478 sipe_process_conference(sipe_private, msg);
1483 /* The server sends status 'terminated' */
1484 if (subscription_state && strstr(subscription_state, "terminated") ) {
1485 gchar *who = parse_from(sipmsg_find_header(msg, request ? "From" : "To"));
1486 gchar *key = sipe_utils_subscription_key(event, who);
1488 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who);
1489 g_free(who);
1491 sipe_subscriptions_remove(sipe_private, key);
1492 g_free(key);
1495 if (!request && event) {
1496 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
1497 int timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
1498 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout);
1500 if (timeout) {
1501 /* 2 min ahead of expiration */
1502 timeout = (timeout - 120) > 120 ? (timeout - 120) : timeout;
1504 if (sipe_strcase_equal(event, "presence.wpending") &&
1505 g_slist_find_custom(sipe_private->allowed_events, "presence.wpending", (GCompareFunc)g_ascii_strcasecmp))
1507 gchar *action_name = g_strdup_printf("<%s>", "presence.wpending");
1508 sipe_schedule_seconds(sipe_private,
1509 action_name,
1510 NULL,
1511 timeout,
1512 sipe_subscribe_presence_wpending,
1513 NULL);
1514 g_free(action_name);
1516 else if (sipe_strcase_equal(event, "presence") &&
1517 g_slist_find_custom(sipe_private->allowed_events, "presence", (GCompareFunc)g_ascii_strcasecmp))
1519 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
1520 gchar *action_name = sipe_utils_presence_key(who);
1522 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
1523 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
1525 else {
1526 sipe_schedule_seconds(sipe_private,
1527 action_name,
1528 g_strdup(who),
1529 timeout,
1530 sipe_subscribe_presence_single,
1531 g_free);
1532 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who, timeout);
1534 g_free(action_name);
1535 g_free(who);
1540 /* The client responses on received a NOTIFY message */
1541 if (request && !benotify)
1543 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1548 Local Variables:
1549 mode: c
1550 c-file-style: "bsd"
1551 indent-tabs-mode: t
1552 tab-width: 8
1553 End: