core: process incoming changes for roaming contacts
[siplcs.git] / src / core / sipe-notify.c
blobbfe0d10d069e2137941c626408bfbc618385adde
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011-12 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 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
94 "dlxExternalUrl" : "dlxInternalUrl";
95 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
96 "absExternalServerUrl" : "absInternalServerUrl";
98 g_free(sipe_private->focus_factory_uri);
99 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
100 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
101 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
103 g_free(sipe_private->dlx_uri);
104 sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
105 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s",
106 sipe_private->dlx_uri ? sipe_private->dlx_uri : "");
108 g_free(sipe_private->addressbook_uri);
109 sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str));
110 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s",
111 sipe_private->addressbook_uri ? sipe_private->addressbook_uri : "");
113 #ifdef HAVE_VV
114 g_free(sipe_private->test_call_bot_uri);
115 sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall"));
116 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s",
117 sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : "");
119 g_free(sipe_private->mras_uri);
120 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
121 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
122 sipe_private->mras_uri ? sipe_private->mras_uri : "");
124 if (sipe_private->mras_uri)
125 sipe_media_get_av_edge_credentials(sipe_private);
126 #endif
127 break;
130 sipe_xml_free(xn_provision_group_list);
132 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
133 /* Some buddies might have been added before we received this
134 * provisioning notify with DLX and addressbook URIs. Now we can
135 * trigger an update of their photos. */
136 sipe_buddy_refresh_photos(sipe_private);
140 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
141 const gchar *data, unsigned len)
143 sipe_xml *xn_list;
144 const sipe_xml *xn_resource;
145 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
146 g_free, NULL);
147 GSList *server;
148 gchar *host;
150 xn_list = sipe_xml_parse(data, len);
152 for (xn_resource = sipe_xml_child(xn_list, "resource");
153 xn_resource;
154 xn_resource = sipe_xml_twin(xn_resource) )
156 const char *uri, *state;
157 const sipe_xml *xn_instance;
159 xn_instance = sipe_xml_child(xn_resource, "instance");
160 if (!xn_instance) continue;
162 uri = sipe_xml_attribute(xn_resource, "uri");
163 state = sipe_xml_attribute(xn_instance, "state");
164 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
166 if (strstr(state, "resubscribe")) {
167 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
169 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
170 gchar *user = g_strdup(uri);
171 host = g_strdup(poolFqdn);
172 server = g_hash_table_lookup(servers, host);
173 server = g_slist_append(server, user);
174 g_hash_table_insert(servers, host, server);
175 } else {
176 sipe_subscribe_presence_single(sipe_private,
177 (void *) uri);
182 /* Send out any deferred poolFqdn subscriptions */
183 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
184 g_hash_table_destroy(servers);
186 sipe_xml_free(xn_list);
190 * Update user phone
191 * Suitable for both 2005 and 2007 systems.
193 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
194 * @param phone_type
195 * @param phone may be modified to strip white space
196 * @param phone_display_string may be modified to strip white space
198 static void
199 sipe_update_user_phone(struct sipe_core_private *sipe_private,
200 const gchar *uri,
201 const gchar *phone_type,
202 gchar *phone,
203 gchar *phone_display_string)
205 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
206 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
208 if(!phone || strlen(phone) == 0) return;
210 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
211 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
212 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
213 } else if (sipe_strequal(phone_type, "home")) {
214 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
215 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
216 } else if (sipe_strequal(phone_type, "other")) {
217 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
218 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
219 } else if (sipe_strequal(phone_type, "custom1")) {
220 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
221 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
224 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
225 if (phone_display_string) {
226 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
230 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
231 const gchar *data,
232 unsigned len)
234 char *activity = NULL;
235 const char *epid;
236 const char *status_id = NULL;
237 const char *name;
238 char *uri;
239 char *self_uri = sip_uri_self(sipe_private);
240 int avl;
241 int act;
242 const char *device_name = NULL;
243 const char *cal_start_time = NULL;
244 const char *cal_granularity = NULL;
245 char *cal_free_busy_base64 = NULL;
246 struct sipe_buddy *sbuddy;
247 const sipe_xml *node;
248 sipe_xml *xn_presentity;
249 const sipe_xml *xn_availability;
250 const sipe_xml *xn_activity;
251 const sipe_xml *xn_display_name;
252 const sipe_xml *xn_email;
253 const sipe_xml *xn_phone_number;
254 const sipe_xml *xn_userinfo;
255 const sipe_xml *xn_note;
256 const sipe_xml *xn_oof;
257 const sipe_xml *xn_state;
258 const sipe_xml *xn_contact;
259 char *note;
260 int user_avail;
261 const char *user_avail_nil;
262 int res_avail;
263 time_t user_avail_since = 0;
264 time_t activity_since = 0;
266 /* fix for Reuters environment on Linux */
267 if (data && strstr(data, "encoding=\"utf-16\"")) {
268 char *tmp_data;
269 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
270 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
271 g_free(tmp_data);
272 } else {
273 xn_presentity = sipe_xml_parse(data, len);
276 xn_availability = sipe_xml_child(xn_presentity, "availability");
277 xn_activity = sipe_xml_child(xn_presentity, "activity");
278 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
279 xn_email = sipe_xml_child(xn_presentity, "email");
280 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
281 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
282 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
283 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
284 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
285 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
286 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
287 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
288 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
289 note = xn_note ? sipe_xml_data(xn_note) : NULL;
291 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
292 user_avail = 0;
293 user_avail_since = 0;
296 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
297 uri = sip_uri_from_name(name);
298 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
299 epid = sipe_xml_attribute(xn_availability, "epid");
300 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
302 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
303 activity = g_strdup(sipe_ocs2005_activity_description(act));
304 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
305 if (user_avail > res_avail) {
306 res_avail = user_avail;
307 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
310 if (xn_display_name) {
311 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
312 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
313 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
314 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
315 char *tel_uri = sip_to_tel_uri(phone_number);
317 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
318 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
319 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
320 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
322 g_free(tel_uri);
323 g_free(phone_label);
324 g_free(phone_number);
325 g_free(email);
326 g_free(display_name);
329 if (xn_contact) {
330 /* tel */
331 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
333 /* Ex.: <tel type="work">tel:+3222220000</tel> */
334 const char *phone_type = sipe_xml_attribute(node, "type");
335 char* phone = sipe_xml_data(node);
337 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
339 g_free(phone);
343 /* devicePresence */
344 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
345 const sipe_xml *xn_device_name;
346 const sipe_xml *xn_calendar_info;
347 const sipe_xml *xn_state;
348 char *state;
350 /* deviceName */
351 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
352 xn_device_name = sipe_xml_child(node, "deviceName");
353 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
356 /* calendarInfo */
357 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
358 if (xn_calendar_info) {
359 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
361 if (cal_start_time) {
362 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
363 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
365 if (cal_start_time_t_tmp > cal_start_time_t) {
366 cal_start_time = cal_start_time_tmp;
367 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
368 g_free(cal_free_busy_base64);
369 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
371 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);
373 } else {
374 cal_start_time = cal_start_time_tmp;
375 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
376 g_free(cal_free_busy_base64);
377 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
379 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);
383 /* state */
384 xn_state = sipe_xml_child(node, "states/state");
385 if (xn_state) {
386 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
387 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
389 state = sipe_xml_data(xn_state);
390 if (dev_avail_since > user_avail_since &&
391 dev_avail >= res_avail)
393 const gchar *new_desc;
394 res_avail = dev_avail;
395 if (!is_empty(state)) {
396 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
397 g_free(activity);
398 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
399 } else if (sipe_strequal(state, "presenting")) {
400 g_free(activity);
401 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
402 } else {
403 activity = state;
404 state = NULL;
406 activity_since = dev_avail_since;
408 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
409 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
410 if (new_desc) {
411 g_free(activity);
412 activity = g_strdup(new_desc);
415 g_free(state);
419 /* oof */
420 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
421 g_free(activity);
422 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
423 activity_since = 0;
426 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
427 if (sbuddy)
429 g_free(sbuddy->activity);
430 sbuddy->activity = activity;
431 activity = NULL;
433 sbuddy->activity_since = activity_since;
435 sbuddy->user_avail = user_avail;
436 sbuddy->user_avail_since = user_avail_since;
438 g_free(sbuddy->note);
439 sbuddy->note = NULL;
440 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
442 sbuddy->is_oof_note = (xn_oof != NULL);
444 g_free(sbuddy->device_name);
445 sbuddy->device_name = NULL;
446 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
448 if (!is_empty(cal_free_busy_base64)) {
449 g_free(sbuddy->cal_start_time);
450 sbuddy->cal_start_time = g_strdup(cal_start_time);
452 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
454 g_free(sbuddy->cal_free_busy_base64);
455 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
456 cal_free_busy_base64 = NULL;
458 g_free(sbuddy->cal_free_busy);
459 sbuddy->cal_free_busy = NULL;
462 sbuddy->last_non_cal_status_id = status_id;
463 g_free(sbuddy->last_non_cal_activity);
464 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
466 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
467 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
469 if (sbuddy->is_oof_note)
470 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
471 else
472 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
474 g_free(sipe_private->note);
475 sipe_private->note = g_strdup(sbuddy->note);
477 sipe_private->note_since = time(NULL);
480 sipe_status_set_token(sipe_private,
481 sbuddy->last_non_cal_status_id);
484 g_free(cal_free_busy_base64);
485 g_free(activity);
487 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
488 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
489 sipe_status_token_to_activity(status_id));
491 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
492 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
495 g_free(note);
496 sipe_xml_free(xn_presentity);
497 g_free(uri);
498 g_free(self_uri);
501 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
502 const gchar *data,
503 unsigned len)
505 const char *uri;
506 sipe_xml *xn_categories;
507 const sipe_xml *xn_category;
508 const char *status = NULL;
509 gboolean do_update_status = FALSE;
510 gboolean has_note_cleaned = FALSE;
511 gboolean has_free_busy_cleaned = FALSE;
513 xn_categories = sipe_xml_parse(data, len);
514 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
516 for (xn_category = sipe_xml_child(xn_categories, "category");
517 xn_category ;
518 xn_category = sipe_xml_twin(xn_category) )
520 const sipe_xml *xn_node;
521 const char *tmp;
522 const char *attrVar = sipe_xml_attribute(xn_category, "name");
523 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
524 sipe_utils_str_to_time(tmp) : 0;
526 /* contactCard */
527 if (sipe_strequal(attrVar, "contactCard"))
529 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
531 if (card) {
532 const sipe_xml *node;
533 /* identity - Display Name and email */
534 node = sipe_xml_child(card, "identity");
535 if (node) {
536 char* display_name = sipe_xml_data(
537 sipe_xml_child(node, "name/displayName"));
538 char* email = sipe_xml_data(
539 sipe_xml_child(node, "email"));
541 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
542 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
544 g_free(display_name);
545 g_free(email);
547 /* company */
548 node = sipe_xml_child(card, "company");
549 if (node) {
550 char* company = sipe_xml_data(node);
551 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
552 g_free(company);
554 /* department */
555 node = sipe_xml_child(card, "department");
556 if (node) {
557 char* department = sipe_xml_data(node);
558 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
559 g_free(department);
561 /* title */
562 node = sipe_xml_child(card, "title");
563 if (node) {
564 char* title = sipe_xml_data(node);
565 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
566 g_free(title);
568 /* office */
569 node = sipe_xml_child(card, "office");
570 if (node) {
571 char* office = sipe_xml_data(node);
572 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
573 g_free(office);
575 /* site (url) */
576 node = sipe_xml_child(card, "url");
577 if (node) {
578 char* site = sipe_xml_data(node);
579 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
580 g_free(site);
582 /* phone */
583 for (node = sipe_xml_child(card, "phone");
584 node;
585 node = sipe_xml_twin(node))
587 const char *phone_type = sipe_xml_attribute(node, "type");
588 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
589 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
591 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
593 g_free(phone);
594 g_free(phone_display_string);
596 /* address */
597 for (node = sipe_xml_child(card, "address");
598 node;
599 node = sipe_xml_twin(node))
601 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
602 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
603 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
604 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
605 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
606 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
608 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
609 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
610 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
611 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
612 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
614 g_free(street);
615 g_free(city);
616 g_free(state);
617 g_free(zipcode);
618 g_free(country_code);
620 break;
625 /* note */
626 else if (sipe_strequal(attrVar, "note"))
628 if (uri) {
629 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
631 if (!has_note_cleaned) {
632 has_note_cleaned = TRUE;
634 g_free(sbuddy->note);
635 sbuddy->note = NULL;
636 sbuddy->is_oof_note = FALSE;
637 sbuddy->note_since = publish_time;
639 do_update_status = TRUE;
641 if (sbuddy && (publish_time >= sbuddy->note_since)) {
642 /* clean up in case no 'note' element is supplied
643 * which indicate note removal in client
645 g_free(sbuddy->note);
646 sbuddy->note = NULL;
647 sbuddy->is_oof_note = FALSE;
648 sbuddy->note_since = publish_time;
650 xn_node = sipe_xml_child(xn_category, "note/body");
651 if (xn_node) {
652 char *tmp;
653 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
654 g_free(tmp);
655 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
656 sbuddy->note_since = publish_time;
658 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
659 uri, sbuddy->note ? sbuddy->note : "");
661 /* to trigger UI refresh in case no status info is supplied in this update */
662 do_update_status = TRUE;
666 /* state */
667 else if(sipe_strequal(attrVar, "state"))
669 char *tmp;
670 int availability;
671 const sipe_xml *xn_availability;
672 const sipe_xml *xn_activity;
673 const sipe_xml *xn_meeting_subject;
674 const sipe_xml *xn_meeting_location;
675 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
677 xn_node = sipe_xml_child(xn_category, "state");
678 if (!xn_node) continue;
679 xn_availability = sipe_xml_child(xn_node, "availability");
680 if (!xn_availability) continue;
681 xn_activity = sipe_xml_child(xn_node, "activity");
682 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
683 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
685 tmp = sipe_xml_data(xn_availability);
686 availability = atoi(tmp);
687 g_free(tmp);
689 /* activity, meeting_subject, meeting_location */
690 if (sbuddy) {
691 const gchar *tmp;
693 /* activity */
694 g_free(sbuddy->activity);
695 sbuddy->activity = NULL;
696 if (xn_activity) {
697 const char *token = sipe_xml_attribute(xn_activity, "token");
698 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
700 /* from token */
701 if (!is_empty(token)) {
702 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
704 /* from custom element */
705 if (xn_custom) {
706 char *custom = sipe_xml_data(xn_custom);
708 if (!is_empty(custom)) {
709 g_free(sbuddy->activity);
710 sbuddy->activity = custom;
711 custom = NULL;
713 g_free(custom);
716 /* meeting_subject */
717 g_free(sbuddy->meeting_subject);
718 sbuddy->meeting_subject = NULL;
719 if (xn_meeting_subject) {
720 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
722 if (!is_empty(meeting_subject)) {
723 sbuddy->meeting_subject = meeting_subject;
724 meeting_subject = NULL;
726 g_free(meeting_subject);
728 /* meeting_location */
729 g_free(sbuddy->meeting_location);
730 sbuddy->meeting_location = NULL;
731 if (xn_meeting_location) {
732 char *meeting_location = sipe_xml_data(xn_meeting_location);
734 if (!is_empty(meeting_location)) {
735 sbuddy->meeting_location = meeting_location;
736 meeting_location = NULL;
738 g_free(meeting_location);
741 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
742 tmp = sipe_ocs2007_legacy_activity_description(availability);
743 if (sbuddy->activity && tmp) {
744 gchar *tmp2 = sbuddy->activity;
746 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, tmp);
747 g_free(tmp2);
748 } else if (tmp) {
749 sbuddy->activity = g_strdup(tmp);
753 do_update_status = TRUE;
755 /* calendarData */
756 else if(sipe_strequal(attrVar, "calendarData"))
758 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
759 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
760 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
762 if (sbuddy && xn_free_busy) {
763 if (!has_free_busy_cleaned) {
764 has_free_busy_cleaned = TRUE;
766 g_free(sbuddy->cal_start_time);
767 sbuddy->cal_start_time = NULL;
769 g_free(sbuddy->cal_free_busy_base64);
770 sbuddy->cal_free_busy_base64 = NULL;
772 g_free(sbuddy->cal_free_busy);
773 sbuddy->cal_free_busy = NULL;
775 sbuddy->cal_free_busy_published = publish_time;
778 if (publish_time >= sbuddy->cal_free_busy_published) {
779 g_free(sbuddy->cal_start_time);
780 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
782 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
783 15 : 0;
785 g_free(sbuddy->cal_free_busy_base64);
786 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
788 g_free(sbuddy->cal_free_busy);
789 sbuddy->cal_free_busy = NULL;
791 sbuddy->cal_free_busy_published = publish_time;
793 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);
797 if (sbuddy && xn_working_hours) {
798 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
803 if (do_update_status) {
804 guint activity;
806 if (status) {
807 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
808 activity = sipe_status_token_to_activity(status);
809 } else {
810 /* no status category in this update,
811 using contact's current status */
812 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
813 uri);
816 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity);
819 sipe_xml_free(xn_categories);
822 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
823 const gchar *uri,
824 const gchar *activity,
825 gboolean is_online)
827 if (is_online) {
828 const gchar *status_id = NULL;
829 if (activity) {
830 if (sipe_strequal(activity,
831 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
832 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
833 } else if (sipe_strequal(activity,
834 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
835 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
839 if (!status_id) {
840 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
843 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
844 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
845 sipe_status_token_to_activity(status_id));
846 } else {
847 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
848 SIPE_ACTIVITY_OFFLINE);
852 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
853 const gchar *data,
854 unsigned len)
856 gchar *uri;
857 gchar *getbasic;
858 gchar *activity = NULL;
859 sipe_xml *pidf;
860 const sipe_xml *basicstatus = NULL, *tuple, *status;
861 gboolean isonline = FALSE;
862 const sipe_xml *display_name_node;
864 pidf = sipe_xml_parse(data, len);
865 if (!pidf) {
866 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
867 return;
870 if ((tuple = sipe_xml_child(pidf, "tuple")))
872 if ((status = sipe_xml_child(tuple, "status"))) {
873 basicstatus = sipe_xml_child(status, "basic");
877 if (!basicstatus) {
878 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
879 sipe_xml_free(pidf);
880 return;
883 getbasic = sipe_xml_data(basicstatus);
884 if (!getbasic) {
885 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
886 sipe_xml_free(pidf);
887 return;
890 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
891 if (strstr(getbasic, "open")) {
892 isonline = TRUE;
894 g_free(getbasic);
896 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
898 display_name_node = sipe_xml_child(pidf, "display-name");
899 if (display_name_node) {
900 char * display_name = sipe_xml_data(display_name_node);
902 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
903 g_free(display_name);
906 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
907 if ((status = sipe_xml_child(tuple, "status"))) {
908 if ((basicstatus = sipe_xml_child(status, "activities"))) {
909 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
910 activity = sipe_xml_data(basicstatus);
911 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
917 sipe_buddy_status_from_activity(sipe_private,
918 uri,
919 activity,
920 isonline);
922 g_free(activity);
923 g_free(uri);
924 sipe_xml_free(pidf);
927 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
928 const GSList *fields,
929 const gchar *body,
930 gsize length)
932 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
934 if (strstr(type,"application/rlmi+xml")) {
935 process_incoming_notify_rlmi_resub(user_data, body, length);
936 } else if (strstr(type, "text/xml+msrtc.pidf")) {
937 process_incoming_notify_msrtc(user_data, body, length);
938 } else {
939 process_incoming_notify_rlmi(user_data, body, length);
943 static void sipe_process_presence(struct sipe_core_private *sipe_private,
944 struct sipmsg *msg)
946 const char *ctype = sipmsg_find_header(msg, "Content-Type");
948 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
950 if (ctype &&
951 (strstr(ctype, "application/rlmi+xml") ||
952 strstr(ctype, "application/msrtc-event-categories+xml")))
954 if (strstr(ctype, "multipart"))
956 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
958 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
960 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
962 else if(strstr(ctype, "application/rlmi+xml"))
964 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
967 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
969 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
971 else
973 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
978 * Fires on deregistration event initiated by server.
979 * [MS-SIPREGE] SIP extension.
981 * OCS2007 Example
983 * Content-Type: text/registration-event
984 * subscription-state: terminated;expires=0
985 * ms-diagnostics-public: 4141;reason="User disabled"
987 * deregistered;event=rejected
989 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
990 struct sipmsg *msg)
992 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
993 gchar *event = NULL;
994 gchar *reason = NULL;
995 gchar *warning;
997 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
999 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1000 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1001 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1002 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1003 } else {
1004 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1005 return;
1008 reason = sipmsg_get_ms_diagnostics_reason(msg);
1009 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1010 if (!reason) { // for LCS2005
1011 if (event && sipe_strcase_equal(event, "unregistered")) {
1012 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1013 reason = g_strdup(_("you are already signed in at another location"));
1014 } else if (event && sipe_strcase_equal(event, "rejected")) {
1015 reason = g_strdup(_("user disabled")); // [MS-OCER]
1016 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1017 reason = g_strdup(_("user moved")); // [MS-OCER]
1020 g_free(event);
1021 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1022 g_free(reason);
1024 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1025 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1026 warning);
1027 g_free(warning);
1032 * Removes entries from local buddy list
1033 * that does not correspond ones in the roaming contact list.
1035 static void sipe_cleanup_local_blist(struct sipe_core_private *sipe_private)
1037 GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC,
1038 NULL, NULL);
1039 GSList *entry = buddies;
1040 struct sipe_buddy *buddy;
1041 sipe_backend_buddy b;
1042 gchar *bname;
1043 gchar *gname;
1045 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies));
1046 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private->buddies));
1047 while (entry) {
1048 b = entry->data;
1049 gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC, b);
1050 bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
1051 buddy = g_hash_table_lookup(sipe_private->buddies, bname);
1052 if(buddy) {
1053 gboolean in_sipe_groups = FALSE;
1054 GSList *entry2 = buddy->groups;
1055 while (entry2) {
1056 struct sipe_group *group = entry2->data;
1057 if (sipe_strequal(group->name, gname)) {
1058 in_sipe_groups = TRUE;
1059 break;
1061 entry2 = entry2->next;
1063 if(!in_sipe_groups) {
1064 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname, gname);
1065 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1067 } else {
1068 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname, gname);
1069 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1071 g_free(bname);
1072 g_free(gname);
1073 entry = entry->next;
1075 g_slist_free(buddies);
1079 * A callback for g_hash_table_foreach
1081 static void sipe_buddy_subscribe_cb(char *buddy_name,
1082 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1083 struct sipe_core_private *sipe_private)
1085 guint time_range = (g_hash_table_size(sipe_private->buddies) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1088 * g_hash_table_size() can never return 0, otherwise this function
1089 * wouldn't be called :-) But to keep Coverity happy...
1091 if (time_range) {
1092 gchar *action_name = sipe_utils_presence_key(buddy_name);
1093 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
1095 sipe_schedule_mseconds(sipe_private,
1096 action_name,
1097 g_strdup(buddy_name),
1098 timeout,
1099 sipe_subscribe_presence_single,
1100 g_free);
1101 g_free(action_name);
1105 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1106 struct sipmsg *msg)
1108 int len = msg->bodylen;
1110 const gchar *tmp = sipmsg_find_header(msg, "Event");
1111 const sipe_xml *item;
1112 sipe_xml *isc;
1113 guint delta;
1114 const sipe_xml *group_node;
1116 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1117 return FALSE;
1120 /* Convert the contact from XML to backend Buddies */
1121 isc = sipe_xml_parse(msg->body, len);
1122 if (!isc) {
1123 return FALSE;
1126 /* [MS-SIP]: deltaNum MUST be non-zero */
1127 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1128 if (delta) {
1129 sipe_private->deltanum_contacts = delta;
1132 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1134 /* Start processing contact list */
1135 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1137 /* Parse groups */
1138 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node)) {
1139 struct sipe_group * group = g_new0(struct sipe_group, 1);
1140 const char *name = sipe_xml_attribute(group_node, "name");
1142 if (g_str_has_prefix(name, "~")) {
1143 name = _("Other Contacts");
1145 group->name = g_strdup(name);
1146 group->id = (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"), NULL);
1148 sipe_group_add(sipe_private, group);
1151 // Make sure we have at least one group
1152 if (g_slist_length(sipe_private->groups) == 0) {
1153 sipe_group_create(sipe_private, _("Other Contacts"), NULL);
1156 /* Parse contacts */
1157 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1158 const gchar *uri = sipe_xml_attribute(item, "uri");
1159 const gchar *name = sipe_xml_attribute(item, "name");
1160 gchar *buddy_name;
1161 struct sipe_buddy *buddy = NULL;
1162 gchar *tmp;
1163 gchar **item_groups;
1164 int i = 0;
1166 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1167 tmp = sip_uri_from_name(uri);
1168 buddy_name = g_ascii_strdown(tmp, -1);
1169 g_free(tmp);
1171 /* assign to group Other Contacts if nothing else received */
1172 tmp = g_strdup(sipe_xml_attribute(item, "groups"));
1173 if(is_empty(tmp)) {
1174 struct sipe_group *group = sipe_group_find_by_name(sipe_private, _("Other Contacts"));
1175 g_free(tmp);
1176 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1178 item_groups = g_strsplit(tmp, " ", 0);
1179 g_free(tmp);
1181 while (item_groups[i]) {
1182 struct sipe_group *group = sipe_group_find_by_id(sipe_private, g_ascii_strtod(item_groups[i], NULL));
1184 // If couldn't find the right group for this contact, just put them in the first group we have
1185 if (group == NULL && g_slist_length(sipe_private->groups) > 0) {
1186 group = sipe_private->groups->data;
1189 if (group != NULL) {
1190 gchar *b_alias;
1191 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, buddy_name, group->name);
1192 if (!b){
1193 b = sipe_backend_buddy_add(SIPE_CORE_PUBLIC, buddy_name, uri, group->name);
1194 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name, uri);
1197 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b);
1198 if (sipe_strcase_equal(uri, b_alias)) {
1199 if (name != NULL && strlen(name) != 0) {
1200 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, b, name);
1202 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name, name);
1205 g_free(b_alias);
1207 if (!buddy) {
1208 buddy = sipe_buddy_add(sipe_private, buddy_name);
1211 buddy->groups = slist_insert_unique_sorted(buddy->groups, group, (GCompareFunc)sipe_group_compare);
1213 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy->name, group->name);
1214 } else {
1215 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1216 name);
1219 i++;
1220 } // while, contact groups
1221 g_strfreev(item_groups);
1222 g_free(buddy_name);
1224 } // for, contacts
1226 sipe_cleanup_local_blist(sipe_private);
1228 /* Add self-contact if not there yet. 2005 systems. */
1229 /* This will resemble subscription to roaming_self in 2007 systems */
1230 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1231 gchar *self_uri = sip_uri_self(sipe_private);
1232 sipe_buddy_add(sipe_private, self_uri);
1233 g_free(self_uri);
1236 /* Finished processing contact list */
1237 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1239 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1241 /* @TODO: Parse new groups
1243 * <addedGroup id="3" name="testnewgroup" externalURI="" />
1245 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1246 const char *name = sipe_xml_attribute(group_node, "name");
1247 SIPE_DEBUG_INFO("Add new group '%s' - NOT IMPLEMENTED", name);
1250 /* Parse modified groups */
1251 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1252 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1253 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1254 NULL));
1255 if (group) {
1256 const char *name = sipe_xml_attribute(group_node, "name");
1258 if (g_str_has_prefix(name, "~")) {
1259 name = _("Other Contacts");
1262 if (!(is_empty(name) ||
1263 sipe_strequal(group->name, name)) &&
1264 sipe_group_rename(sipe_private,
1265 group,
1266 name))
1267 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1271 /* @TODO: Parse deleted groups
1273 * <deletedGroup id="2" />
1275 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1276 const char *id = sipe_xml_attribute(group_node, "id");
1277 SIPE_DEBUG_INFO("Delete group ID %s - NOT IMPLEMENTED", id);
1280 /* @TODO: Parse new buddies
1282 * <addedContact uri="sip:test1user@domain.com" name="Test User" groups="1" subscribed="true" externalURI="" />
1284 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1285 const gchar *uri = sipe_xml_attribute(item, "uri");
1286 SIPE_DEBUG_INFO("Add new buddy %s - NOT IMPLEMENTED", uri);
1289 /* Parse modified contacts */
1290 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1291 const gchar *uri = sipe_xml_attribute(item, "uri");
1292 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies,
1293 uri);
1295 if (buddy) {
1296 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1297 uri,
1298 NULL);
1300 if (b) {
1301 const gchar *name = sipe_xml_attribute(item, "name");
1302 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1304 gchar **item_groups;
1305 int i = 0;
1306 GSList *found = NULL;
1307 GSList *entry;
1309 /* new alias? */
1310 if (!(is_empty(name) ||
1311 sipe_strequal(b_alias, name))) {
1312 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1314 name);
1315 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", b_alias, name);
1317 g_free(b_alias);
1319 item_groups = g_strsplit(sipe_xml_attribute(item,
1320 "groups"),
1321 " ", 0);
1322 /* added to groups? */
1323 if (item_groups) {
1324 while (item_groups[i]) {
1325 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1326 g_ascii_strtod(item_groups[i],
1327 NULL));
1329 /* ignore unkown groups */
1330 if (group) {
1331 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1332 uri,
1333 group->name);
1335 /* add group to found list */
1336 found = g_slist_prepend(found, group);
1338 /* buddy NOT in this group? */
1339 if (!oldb) {
1340 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1342 SIPE_DEBUG_INFO("Adding buddy %s (alias %s) to new group %s", uri, b_alias, group->name);
1343 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1344 uri,
1345 b_alias,
1346 group->name);
1347 g_free(b_alias);
1349 buddy->groups = slist_insert_unique_sorted(buddy->groups,
1350 group,
1351 (GCompareFunc) sipe_group_compare);
1355 /* next group */
1356 i++;
1358 g_strfreev(item_groups);
1361 /* removed from groups? */
1362 entry = buddy->groups;
1363 while (entry) {
1364 GSList *remove_link = entry;
1365 struct sipe_group *group = remove_link->data;
1367 /* next buddy group */
1368 entry = entry->next;
1370 /* old group NOT found in new list? */
1371 if (g_slist_find(found, group) == NULL) {
1372 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1373 uri,
1374 group->name);
1375 SIPE_DEBUG_INFO("Removing buddy %s from group %s", uri, group->name);
1376 /* this should never be NULL */
1377 if (oldb)
1378 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1379 oldb);
1380 buddy->groups = g_slist_remove_link(buddy->groups,
1381 remove_link);
1384 g_slist_free(found);
1389 /* Parse deleted contacts */
1390 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1391 const gchar *uri = sipe_xml_attribute(item, "uri");
1392 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies,
1393 uri);
1395 if (buddy) {
1396 GSList *entry = buddy->groups;
1398 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1399 while (entry) {
1400 struct sipe_group *group = entry->data;
1401 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1402 uri,
1403 group->name);
1404 /* this should never be NULL */
1405 if (oldb)
1406 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1407 oldb);
1409 /* next buddy group */
1410 entry = entry->next;
1412 sipe_buddy_remove(sipe_private, buddy);
1417 sipe_xml_free(isc);
1419 /* subscribe to buddies */
1420 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) {
1421 /* do it once, then count Expire field to schedule resubscribe */
1422 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
1423 sipe_subscribe_presence_batched(sipe_private);
1424 } else {
1425 g_hash_table_foreach(sipe_private->buddies,
1426 (GHFunc)sipe_buddy_subscribe_cb,
1427 sipe_private);
1429 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES);
1431 /* for 2005 systems schedule contacts' status update
1432 * based on their calendar information
1434 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1435 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1438 return 0;
1441 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1442 struct sipmsg *msg)
1444 guint delta;
1445 sipe_xml *xml;
1447 xml = sipe_xml_parse(msg->body, msg->bodylen);
1448 if (!xml)
1449 return;
1451 /* [MS-SIP]: deltaNum MUST be non-zero */
1452 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1453 if (delta) {
1454 sipe_private->deltanum_acl = delta;
1457 sipe_xml_free(xml);
1460 struct sipe_auth_job {
1461 gchar *who;
1462 struct sipe_core_private *sipe_private;
1465 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1466 const gchar* who,
1467 gboolean allow)
1469 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1471 if (allow) {
1472 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1473 } else {
1474 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1477 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1478 sipe_ocs2007_change_access_level(sipe_private,
1479 (allow ? -1 : 32000),
1480 "user",
1481 sipe_get_no_sip_uri(who));
1482 } else {
1483 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1488 static void sipe_auth_user_cb(gpointer data)
1490 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1491 if (!job) return;
1493 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1494 job->who,
1495 TRUE);
1496 g_free(job);
1499 static void sipe_deny_user_cb(gpointer data)
1501 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1502 if (!job) return;
1504 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1505 job->who,
1506 FALSE);
1507 g_free(job);
1510 /* OCS2005- */
1511 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1512 struct sipmsg * msg)
1514 sipe_xml *watchers;
1515 const sipe_xml *watcher;
1516 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1517 if (msg->response != 0 && msg->response != 200) return;
1519 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1521 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1522 if (!watchers) return;
1524 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1525 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1526 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1527 gboolean on_list = g_hash_table_lookup(sipe_private->buddies, remote_user) != NULL;
1529 // TODO pull out optional displayName to pass as alias
1530 if (remote_user) {
1531 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1532 job->who = remote_user;
1533 job->sipe_private = sipe_private;
1534 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1535 remote_user,
1536 alias,
1537 on_list,
1538 sipe_auth_user_cb,
1539 sipe_deny_user_cb,
1540 (gpointer)job);
1545 sipe_xml_free(watchers);
1546 return;
1549 static void sipe_presence_timeout_mime_cb(gpointer user_data,
1550 SIPE_UNUSED_PARAMETER const GSList *fields,
1551 const gchar *body,
1552 gsize length)
1554 GSList **buddies = user_data;
1555 sipe_xml *xml = sipe_xml_parse(body, length);
1557 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
1558 const gchar *uri = sipe_xml_attribute(xml, "uri");
1559 const sipe_xml *xn_category;
1562 * automaton: presence is never expected to change
1564 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
1566 for (xn_category = sipe_xml_child(xml, "category");
1567 xn_category;
1568 xn_category = sipe_xml_twin(xn_category)) {
1569 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
1570 "contactCard")) {
1571 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
1572 if (node) {
1573 char *boolean = sipe_xml_data(node);
1574 if (sipe_strequal(boolean, "true")) {
1575 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
1576 uri);
1577 uri = NULL;
1579 g_free(boolean);
1581 break;
1585 if (uri) {
1586 *buddies = g_slist_append(*buddies, sip_uri(uri));
1590 sipe_xml_free(xml);
1593 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
1594 struct sipmsg *msg,
1595 const gchar *who,
1596 int timeout)
1598 const char *ctype = sipmsg_find_header(msg, "Content-Type");
1599 gchar *action_name = sipe_utils_presence_key(who);
1601 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
1603 if (ctype &&
1604 strstr(ctype, "multipart") &&
1605 (strstr(ctype, "application/rlmi+xml") ||
1606 strstr(ctype, "application/msrtc-event-categories+xml"))) {
1607 GSList *buddies = NULL;
1609 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
1611 if (buddies)
1612 sipe_subscribe_presence_batched_schedule(sipe_private,
1613 action_name,
1614 who,
1615 buddies,
1616 timeout);
1618 } else {
1619 sipe_schedule_seconds(sipe_private,
1620 action_name,
1621 g_strdup(who),
1622 timeout,
1623 sipe_subscribe_presence_single,
1624 g_free);
1625 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who, timeout);
1627 g_free(action_name);
1631 * Dispatcher for all incoming subscription information
1632 * whether it comes from NOTIFY, BENOTIFY requests or
1633 * piggy-backed to subscription's OK responce.
1635 * @param request whether initiated from BE/NOTIFY request or OK-response message.
1636 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
1638 void process_incoming_notify(struct sipe_core_private *sipe_private,
1639 struct sipmsg *msg,
1640 gboolean request, gboolean benotify)
1642 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1643 const gchar *event = sipmsg_find_header(msg, "Event");
1644 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1646 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1648 /* implicit subscriptions */
1649 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1650 sipe_process_imdn(sipe_private, msg);
1653 if (event) {
1654 /* for one off subscriptions (send with Expire: 0) */
1655 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2"))
1657 sipe_process_provisioning_v2(sipe_private, msg);
1659 else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning"))
1661 sipe_process_provisioning(sipe_private, msg);
1663 else if (sipe_strcase_equal(event, "presence"))
1665 sipe_process_presence(sipe_private, msg);
1667 else if (sipe_strcase_equal(event, "registration-notify"))
1669 sipe_process_registration_notify(sipe_private, msg);
1672 if (!subscription_state || strstr(subscription_state, "active"))
1674 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts"))
1676 sipe_process_roaming_contacts(sipe_private, msg);
1678 else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self"))
1680 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1682 else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL"))
1684 sipe_process_roaming_acl(sipe_private, msg);
1686 else if (sipe_strcase_equal(event, "presence.wpending"))
1688 sipe_process_presence_wpending(sipe_private, msg);
1690 else if (sipe_strcase_equal(event, "conference"))
1692 sipe_process_conference(sipe_private, msg);
1697 /* The server sends status 'terminated' */
1698 if (subscription_state && strstr(subscription_state, "terminated") ) {
1699 gchar *who = parse_from(sipmsg_find_header(msg, request ? "From" : "To"));
1700 gchar *key = sipe_utils_subscription_key(event, who);
1702 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who);
1703 g_free(who);
1705 sipe_subscriptions_remove(sipe_private, key);
1706 g_free(key);
1709 if (!request && event) {
1710 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
1711 int timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
1712 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout);
1714 if (timeout) {
1715 /* 2 min ahead of expiration */
1716 timeout = (timeout - 120) > 120 ? (timeout - 120) : timeout;
1718 if (sipe_strcase_equal(event, "presence.wpending") &&
1719 g_slist_find_custom(sipe_private->allowed_events, "presence.wpending", (GCompareFunc)g_ascii_strcasecmp))
1721 gchar *action_name = g_strdup_printf("<%s>", "presence.wpending");
1722 sipe_schedule_seconds(sipe_private,
1723 action_name,
1724 NULL,
1725 timeout,
1726 sipe_subscribe_presence_wpending,
1727 NULL);
1728 g_free(action_name);
1730 else if (sipe_strcase_equal(event, "presence") &&
1731 g_slist_find_custom(sipe_private->allowed_events, "presence", (GCompareFunc)g_ascii_strcasecmp))
1733 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
1734 gchar *action_name = sipe_utils_presence_key(who);
1736 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
1737 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
1739 else {
1740 sipe_schedule_seconds(sipe_private,
1741 action_name,
1742 g_strdup(who),
1743 timeout,
1744 sipe_subscribe_presence_single,
1745 g_free);
1746 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who, timeout);
1748 g_free(action_name);
1749 g_free(who);
1754 /* The client responses on received a NOTIFY message */
1755 if (request && !benotify)
1757 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1762 Local Variables:
1763 mode: c
1764 c-file-style: "bsd"
1765 indent-tabs-mode: t
1766 tab-width: 8
1767 End: