c5b06254f89d259d87c6da9cef3b9ef895d0fc1b
[siplcs.git] / src / core / sipe-notify.c
blobc5b06254f89d259d87c6da9cef3b9ef895d0fc1b
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Process incoming SIP NOTIFY/BENOTIFY messages
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 "sipe-backend.h"
42 #include "sipe-buddy.h"
43 #include "sipe-cal.h"
44 #include "sipe-conf.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
47 #include "sipe-group.h"
48 #include "sipe-media.h"
49 #include "sipe-mime.h"
50 #include "sipe-nls.h"
51 #include "sipe-notify.h"
52 #include "sipe-ocs2005.h"
53 #include "sipe-ocs2007.h"
54 #include "sipe-schedule.h"
55 #include "sipe-status.h"
56 #include "sipe-subscriptions.h"
57 #include "sipe-utils.h"
58 #include "sipe-xml.h"
60 /* OCS2005 */
61 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
62 struct sipmsg *msg)
64 sipe_xml *xn_provision;
65 const sipe_xml *node;
67 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
68 if ((node = sipe_xml_child(xn_provision, "user"))) {
69 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
70 if ((node = sipe_xml_child(node, "line"))) {
71 const gchar *line_uri = sipe_xml_attribute(node, "uri");
72 const gchar *server = sipe_xml_attribute(node, "server");
73 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
74 sip_csta_open(sipe_private, line_uri, server);
77 sipe_xml_free(xn_provision);
80 /* OCS2007+ */
81 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
82 struct sipmsg *msg)
84 sipe_xml *xn_provision_group_list;
85 const sipe_xml *node;
87 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
89 /* provisionGroup */
90 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup"); node; node = sipe_xml_twin(node)) {
91 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node, "name"))) {
92 const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
93 "dlxExternalUrl" : "dlxInternalUrl";
94 const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
95 "absExternalServerUrl" : "absInternalServerUrl";
97 g_free(sipe_private->focus_factory_uri);
98 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
99 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
100 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
102 g_free(sipe_private->dlx_uri);
103 sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
104 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->dlx_uri=%s",
105 sipe_private->dlx_uri ? sipe_private->dlx_uri : "");
107 g_free(sipe_private->addressbook_uri);
108 sipe_private->addressbook_uri = sipe_xml_data(sipe_xml_child(node, addressbook_uri_str));
109 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->addressbook_uri=%s",
110 sipe_private->addressbook_uri ? sipe_private->addressbook_uri : "");
112 #ifdef HAVE_VV
113 g_free(sipe_private->test_call_bot_uri);
114 sipe_private->test_call_bot_uri = sipe_xml_data(sipe_xml_child(node, "botSipUriForTestCall"));
115 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->test_call_bot_uri=%s",
116 sipe_private->test_call_bot_uri ? sipe_private->test_call_bot_uri : "");
118 g_free(sipe_private->mras_uri);
119 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
120 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
121 sipe_private->mras_uri ? sipe_private->mras_uri : "");
123 if (sipe_private->mras_uri)
124 sipe_media_get_av_edge_credentials(sipe_private);
125 #endif
126 break;
129 sipe_xml_free(xn_provision_group_list);
131 if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
132 /* Some buddies might have been added before we received this
133 * provisioning notify with DLX and addressbook URIs. Now we can
134 * trigger an update of their photos. */
135 sipe_buddy_refresh_photos(sipe_private);
139 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
140 const gchar *data, unsigned len)
142 sipe_xml *xn_list;
143 const sipe_xml *xn_resource;
144 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
145 g_free, NULL);
147 xn_list = sipe_xml_parse(data, len);
149 for (xn_resource = sipe_xml_child(xn_list, "resource");
150 xn_resource;
151 xn_resource = sipe_xml_twin(xn_resource) )
153 const char *uri, *state;
154 const sipe_xml *xn_instance;
156 xn_instance = sipe_xml_child(xn_resource, "instance");
157 if (!xn_instance) continue;
159 uri = sipe_xml_attribute(xn_resource, "uri");
160 state = sipe_xml_attribute(xn_instance, "state");
161 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
163 if (strstr(state, "resubscribe")) {
164 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
166 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
167 gchar *user = g_strdup(uri);
168 gchar *host = g_strdup(poolFqdn);
169 GSList *server = g_hash_table_lookup(servers,
170 host);
171 server = g_slist_append(server, user);
172 g_hash_table_insert(servers, host, server);
173 } else {
174 sipe_subscribe_presence_single(sipe_private,
175 uri,
176 uri);
181 /* Send out any deferred poolFqdn subscriptions */
182 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
183 g_hash_table_destroy(servers);
185 sipe_xml_free(xn_list);
189 * Update user phone
190 * Suitable for both 2005 and 2007 systems.
192 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
193 * @param phone_type
194 * @param phone may be modified to strip white space
195 * @param phone_display_string may be modified to strip white space
197 static void
198 sipe_update_user_phone(struct sipe_core_private *sipe_private,
199 const gchar *uri,
200 const gchar *phone_type,
201 gchar *phone,
202 gchar *phone_display_string)
204 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
205 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
207 if(!phone || strlen(phone) == 0) return;
209 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
210 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
211 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
212 } else if (sipe_strequal(phone_type, "home")) {
213 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
214 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
215 } else if (sipe_strequal(phone_type, "other")) {
216 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
217 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
218 } else if (sipe_strequal(phone_type, "custom1")) {
219 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
220 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
223 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
224 if (phone_display_string) {
225 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
229 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
230 const gchar *data,
231 unsigned len)
233 char *activity = NULL;
234 const char *epid;
235 const char *status_id = NULL;
236 const char *name;
237 char *uri;
238 char *self_uri = sip_uri_self(sipe_private);
239 int avl;
240 int act;
241 const char *device_name = NULL;
242 const char *cal_start_time = NULL;
243 const char *cal_granularity = NULL;
244 char *cal_free_busy_base64 = NULL;
245 struct sipe_buddy *sbuddy;
246 const sipe_xml *node;
247 sipe_xml *xn_presentity;
248 const sipe_xml *xn_availability;
249 const sipe_xml *xn_activity;
250 const sipe_xml *xn_display_name;
251 const sipe_xml *xn_email;
252 const sipe_xml *xn_phone_number;
253 const sipe_xml *xn_userinfo;
254 const sipe_xml *xn_note;
255 const sipe_xml *xn_oof;
256 const sipe_xml *xn_state;
257 const sipe_xml *xn_contact;
258 char *note;
259 int user_avail;
260 const char *user_avail_nil;
261 int res_avail;
262 time_t user_avail_since = 0;
263 time_t activity_since = 0;
265 /* fix for Reuters environment on Linux */
266 if (data && strstr(data, "encoding=\"utf-16\"")) {
267 char *tmp_data;
268 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
269 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
270 g_free(tmp_data);
271 } else {
272 xn_presentity = sipe_xml_parse(data, len);
275 xn_availability = sipe_xml_child(xn_presentity, "availability");
276 xn_activity = sipe_xml_child(xn_presentity, "activity");
277 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
278 xn_email = sipe_xml_child(xn_presentity, "email");
279 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
280 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
281 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
282 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
283 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
284 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
285 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
286 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
287 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
288 note = xn_note ? sipe_xml_data(xn_note) : NULL;
290 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
291 user_avail = 0;
292 user_avail_since = 0;
295 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
296 uri = sip_uri_from_name(name);
297 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
298 epid = sipe_xml_attribute(xn_availability, "epid");
299 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
301 status_id = sipe_ocs2005_status_from_activity_availability(act, avl);
302 activity = g_strdup(sipe_ocs2005_activity_description(act));
303 res_avail = sipe_ocs2007_availability_from_status(status_id, NULL);
304 if (user_avail > res_avail) {
305 res_avail = user_avail;
306 status_id = sipe_ocs2007_status_from_legacy_availability(user_avail, NULL);
309 if (xn_display_name) {
310 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
311 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
312 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
313 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
314 char *tel_uri = sip_to_tel_uri(phone_number);
316 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
317 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
318 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
319 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
321 g_free(tel_uri);
322 g_free(phone_label);
323 g_free(phone_number);
324 g_free(email);
325 g_free(display_name);
328 if (xn_contact) {
329 /* tel */
330 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
332 /* Ex.: <tel type="work">tel:+3222220000</tel> */
333 const char *phone_type = sipe_xml_attribute(node, "type");
334 char* phone = sipe_xml_data(node);
336 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
338 g_free(phone);
342 if (xn_display_name || xn_contact)
343 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
345 /* devicePresence */
346 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
347 const sipe_xml *xn_device_name;
348 const sipe_xml *xn_calendar_info;
349 const sipe_xml *xn_state;
350 char *state;
352 /* deviceName */
353 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
354 xn_device_name = sipe_xml_child(node, "deviceName");
355 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
358 /* calendarInfo */
359 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
360 if (xn_calendar_info) {
361 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
363 if (cal_start_time) {
364 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
365 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
367 if (cal_start_time_t_tmp > cal_start_time_t) {
368 cal_start_time = cal_start_time_tmp;
369 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
370 g_free(cal_free_busy_base64);
371 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
373 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);
375 } else {
376 cal_start_time = cal_start_time_tmp;
377 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
378 g_free(cal_free_busy_base64);
379 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
381 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);
385 /* state */
386 xn_state = sipe_xml_child(node, "states/state");
387 if (xn_state) {
388 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
389 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
391 state = sipe_xml_data(xn_state);
392 if (dev_avail_since > user_avail_since &&
393 dev_avail >= res_avail)
395 const gchar *new_desc;
396 res_avail = dev_avail;
397 if (!is_empty(state)) {
398 if (sipe_strequal(state, sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
399 g_free(activity);
400 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE));
401 } else if (sipe_strequal(state, "presenting")) {
402 g_free(activity);
403 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF));
404 } else {
405 activity = state;
406 state = NULL;
408 activity_since = dev_avail_since;
410 status_id = sipe_ocs2007_status_from_legacy_availability(res_avail, NULL);
411 new_desc = sipe_ocs2007_legacy_activity_description(res_avail);
412 if (new_desc) {
413 g_free(activity);
414 activity = g_strdup(new_desc);
417 g_free(state);
421 /* oof */
422 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
423 g_free(activity);
424 activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
425 activity_since = 0;
428 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
429 if (sbuddy)
431 g_free(sbuddy->activity);
432 sbuddy->activity = activity;
433 activity = NULL;
435 sbuddy->activity_since = activity_since;
437 sbuddy->user_avail = user_avail;
438 sbuddy->user_avail_since = user_avail_since;
440 g_free(sbuddy->note);
441 sbuddy->note = NULL;
442 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
444 sbuddy->is_oof_note = (xn_oof != NULL);
446 g_free(sbuddy->device_name);
447 sbuddy->device_name = NULL;
448 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
450 if (!is_empty(cal_free_busy_base64)) {
451 g_free(sbuddy->cal_start_time);
452 sbuddy->cal_start_time = g_strdup(cal_start_time);
454 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
456 g_free(sbuddy->cal_free_busy_base64);
457 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
458 cal_free_busy_base64 = NULL;
460 g_free(sbuddy->cal_free_busy);
461 sbuddy->cal_free_busy = NULL;
464 sbuddy->last_non_cal_status_id = status_id;
465 g_free(sbuddy->last_non_cal_activity);
466 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
468 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
469 if (!sipe_strequal(sbuddy->note, sipe_private->note)) /* not same */
471 if (sbuddy->is_oof_note)
472 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
473 else
474 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
476 g_free(sipe_private->note);
477 sipe_private->note = g_strdup(sbuddy->note);
479 sipe_private->note_since = time(NULL);
482 sipe_status_set_token(sipe_private,
483 sbuddy->last_non_cal_status_id);
486 g_free(cal_free_busy_base64);
487 g_free(activity);
489 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
490 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
491 sipe_status_token_to_activity(status_id));
493 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
494 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
497 g_free(note);
498 sipe_xml_free(xn_presentity);
499 g_free(uri);
500 g_free(self_uri);
503 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
504 const gchar *data,
505 unsigned len)
507 const char *uri;
508 struct sipe_buddy *sbuddy = NULL;
509 sipe_xml *xn_categories;
510 const sipe_xml *xn_category;
511 const char *status = NULL;
512 gboolean do_update_status = FALSE;
513 gboolean has_note_cleaned = FALSE;
514 gboolean has_free_busy_cleaned = FALSE;
516 xn_categories = sipe_xml_parse(data, len);
517 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
518 if (uri) {
519 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
522 if (!sbuddy) {
523 /* Got presence of a buddy not in our contact list, ignore. */
524 sipe_xml_free(xn_categories);
525 return;
528 for (xn_category = sipe_xml_child(xn_categories, "category");
529 xn_category ;
530 xn_category = sipe_xml_twin(xn_category) )
532 const sipe_xml *xn_node;
533 const char *tmp;
534 const char *attrVar = sipe_xml_attribute(xn_category, "name");
535 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
536 sipe_utils_str_to_time(tmp) : 0;
538 /* contactCard */
539 if (sipe_strequal(attrVar, "contactCard"))
541 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
543 if (card) {
544 const sipe_xml *node;
545 /* identity - Display Name and email */
546 node = sipe_xml_child(card, "identity");
547 if (node) {
548 char* display_name = sipe_xml_data(
549 sipe_xml_child(node, "name/displayName"));
550 char* email = sipe_xml_data(
551 sipe_xml_child(node, "email"));
553 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
554 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
556 g_free(display_name);
557 g_free(email);
559 /* company */
560 node = sipe_xml_child(card, "company");
561 if (node) {
562 char* company = sipe_xml_data(node);
563 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
564 g_free(company);
566 /* department */
567 node = sipe_xml_child(card, "department");
568 if (node) {
569 char* department = sipe_xml_data(node);
570 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
571 g_free(department);
573 /* title */
574 node = sipe_xml_child(card, "title");
575 if (node) {
576 char* title = sipe_xml_data(node);
577 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
578 g_free(title);
580 /* office */
581 node = sipe_xml_child(card, "office");
582 if (node) {
583 char* office = sipe_xml_data(node);
584 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
585 g_free(office);
587 /* site (url) */
588 node = sipe_xml_child(card, "url");
589 if (node) {
590 char* site = sipe_xml_data(node);
591 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
592 g_free(site);
594 /* phone */
595 for (node = sipe_xml_child(card, "phone");
596 node;
597 node = sipe_xml_twin(node))
599 const char *phone_type = sipe_xml_attribute(node, "type");
600 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
601 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
603 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
605 g_free(phone);
606 g_free(phone_display_string);
608 /* address */
609 for (node = sipe_xml_child(card, "address");
610 node;
611 node = sipe_xml_twin(node))
613 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
614 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
615 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
616 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
617 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
618 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
620 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
621 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
622 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
623 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
624 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
626 g_free(street);
627 g_free(city);
628 g_free(state);
629 g_free(zipcode);
630 g_free(country_code);
632 break;
637 /* note */
638 else if (sipe_strequal(attrVar, "note"))
640 if (!has_note_cleaned) {
641 has_note_cleaned = TRUE;
643 g_free(sbuddy->note);
644 sbuddy->note = NULL;
645 sbuddy->is_oof_note = FALSE;
646 sbuddy->note_since = publish_time;
648 do_update_status = TRUE;
650 if (publish_time >= sbuddy->note_since) {
651 /* clean up in case no 'note' element is supplied
652 * which indicate note removal in client
654 g_free(sbuddy->note);
655 sbuddy->note = NULL;
656 sbuddy->is_oof_note = FALSE;
657 sbuddy->note_since = publish_time;
659 xn_node = sipe_xml_child(xn_category, "note/body");
660 if (xn_node) {
661 char *tmp;
662 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
663 g_free(tmp);
664 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
665 sbuddy->note_since = publish_time;
667 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
668 uri, sbuddy->note ? sbuddy->note : "");
670 /* to trigger UI refresh in case no status info is supplied in this update */
671 do_update_status = TRUE;
674 /* state */
675 else if(sipe_strequal(attrVar, "state"))
677 char *tmp;
678 int availability;
679 const sipe_xml *xn_availability;
680 const sipe_xml *xn_activity;
681 const sipe_xml *xn_meeting_subject;
682 const sipe_xml *xn_meeting_location;
683 const gchar *legacy_activity;
685 xn_node = sipe_xml_child(xn_category, "state");
686 if (!xn_node) continue;
687 xn_availability = sipe_xml_child(xn_node, "availability");
688 if (!xn_availability) continue;
689 xn_activity = sipe_xml_child(xn_node, "activity");
690 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
691 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
693 tmp = sipe_xml_data(xn_availability);
694 availability = atoi(tmp);
695 g_free(tmp);
697 /* activity */
698 g_free(sbuddy->activity);
699 sbuddy->activity = NULL;
700 if (xn_activity) {
701 const char *token = sipe_xml_attribute(xn_activity, "token");
702 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
704 /* from token */
705 if (!is_empty(token)) {
706 sbuddy->activity = g_strdup(sipe_core_activity_description(sipe_status_token_to_activity(token)));
708 /* from custom element */
709 if (xn_custom) {
710 char *custom = sipe_xml_data(xn_custom);
712 if (!is_empty(custom)) {
713 g_free(sbuddy->activity);
714 sbuddy->activity = custom;
715 custom = NULL;
717 g_free(custom);
720 /* meeting_subject */
721 g_free(sbuddy->meeting_subject);
722 sbuddy->meeting_subject = NULL;
723 if (xn_meeting_subject) {
724 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
726 if (!is_empty(meeting_subject)) {
727 sbuddy->meeting_subject = meeting_subject;
728 meeting_subject = NULL;
730 g_free(meeting_subject);
732 /* meeting_location */
733 g_free(sbuddy->meeting_location);
734 sbuddy->meeting_location = NULL;
735 if (xn_meeting_location) {
736 char *meeting_location = sipe_xml_data(xn_meeting_location);
738 if (!is_empty(meeting_location)) {
739 sbuddy->meeting_location = meeting_location;
740 meeting_location = NULL;
742 g_free(meeting_location);
745 status = sipe_ocs2007_status_from_legacy_availability(availability, NULL);
746 legacy_activity = sipe_ocs2007_legacy_activity_description(availability);
747 if (sbuddy->activity && legacy_activity) {
748 gchar *tmp2 = sbuddy->activity;
750 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, legacy_activity);
751 g_free(tmp2);
752 } else if (legacy_activity) {
753 sbuddy->activity = g_strdup(legacy_activity);
756 do_update_status = TRUE;
758 /* calendarData */
759 else if(sipe_strequal(attrVar, "calendarData"))
761 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
762 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
764 if (xn_free_busy) {
765 if (!has_free_busy_cleaned) {
766 has_free_busy_cleaned = TRUE;
768 g_free(sbuddy->cal_start_time);
769 sbuddy->cal_start_time = NULL;
771 g_free(sbuddy->cal_free_busy_base64);
772 sbuddy->cal_free_busy_base64 = NULL;
774 g_free(sbuddy->cal_free_busy);
775 sbuddy->cal_free_busy = NULL;
777 sbuddy->cal_free_busy_published = publish_time;
780 if (publish_time >= sbuddy->cal_free_busy_published) {
781 g_free(sbuddy->cal_start_time);
782 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
784 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
785 15 : 0;
787 g_free(sbuddy->cal_free_busy_base64);
788 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
790 g_free(sbuddy->cal_free_busy);
791 sbuddy->cal_free_busy = NULL;
793 sbuddy->cal_free_busy_published = publish_time;
795 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);
799 if (xn_working_hours) {
800 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
805 if (do_update_status) {
806 guint activity;
808 if (status) {
809 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
810 activity = sipe_status_token_to_activity(status);
811 } else {
812 /* no status category in this update,
813 using contact's current status */
814 activity = sipe_backend_buddy_get_status(SIPE_CORE_PUBLIC,
815 uri);
818 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, activity);
821 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
823 sipe_xml_free(xn_categories);
826 static void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
827 const gchar *uri,
828 const gchar *activity,
829 gboolean is_online)
831 if (is_online) {
832 const gchar *status_id = NULL;
833 if (activity) {
834 if (sipe_strequal(activity,
835 sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
836 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
837 } else if (sipe_strequal(activity,
838 sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
839 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
843 if (!status_id) {
844 status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
847 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
848 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
849 sipe_status_token_to_activity(status_id));
850 } else {
851 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri,
852 SIPE_ACTIVITY_OFFLINE);
856 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
857 const gchar *data,
858 unsigned len)
860 gchar *uri;
861 gchar *getbasic;
862 gchar *activity = NULL;
863 sipe_xml *pidf;
864 const sipe_xml *basicstatus = NULL, *tuple, *status;
865 gboolean isonline = FALSE;
866 const sipe_xml *display_name_node;
868 pidf = sipe_xml_parse(data, len);
869 if (!pidf) {
870 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
871 return;
874 if ((tuple = sipe_xml_child(pidf, "tuple")))
876 if ((status = sipe_xml_child(tuple, "status"))) {
877 basicstatus = sipe_xml_child(status, "basic");
881 if (!basicstatus) {
882 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
883 sipe_xml_free(pidf);
884 return;
887 getbasic = sipe_xml_data(basicstatus);
888 if (!getbasic) {
889 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
890 sipe_xml_free(pidf);
891 return;
894 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
895 if (strstr(getbasic, "open")) {
896 isonline = TRUE;
898 g_free(getbasic);
900 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
902 display_name_node = sipe_xml_child(pidf, "display-name");
903 if (display_name_node) {
904 char * display_name = sipe_xml_data(display_name_node);
906 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
907 g_free(display_name);
909 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
912 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
913 if ((status = sipe_xml_child(tuple, "status"))) {
914 if ((basicstatus = sipe_xml_child(status, "activities"))) {
915 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
916 activity = sipe_xml_data(basicstatus);
917 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
923 sipe_buddy_status_from_activity(sipe_private,
924 uri,
925 activity,
926 isonline);
928 g_free(activity);
929 g_free(uri);
930 sipe_xml_free(pidf);
933 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
934 const GSList *fields,
935 const gchar *body,
936 gsize length)
938 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
940 if (strstr(type,"application/rlmi+xml")) {
941 process_incoming_notify_rlmi_resub(user_data, body, length);
942 } else if (strstr(type, "text/xml+msrtc.pidf")) {
943 process_incoming_notify_msrtc(user_data, body, length);
944 } else {
945 process_incoming_notify_rlmi(user_data, body, length);
949 static void sipe_process_presence(struct sipe_core_private *sipe_private,
950 struct sipmsg *msg)
952 const char *ctype = sipmsg_find_header(msg, "Content-Type");
954 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
956 if (ctype &&
957 (strstr(ctype, "application/rlmi+xml") ||
958 strstr(ctype, "application/msrtc-event-categories+xml")))
960 if (strstr(ctype, "multipart"))
962 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
964 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
966 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
968 else if(strstr(ctype, "application/rlmi+xml"))
970 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
973 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
975 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
977 else
979 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
984 * Fires on deregistration event initiated by server.
985 * [MS-SIPREGE] SIP extension.
987 * OCS2007 Example
989 * Content-Type: text/registration-event
990 * subscription-state: terminated;expires=0
991 * ms-diagnostics-public: 4141;reason="User disabled"
993 * deregistered;event=rejected
995 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
996 struct sipmsg *msg)
998 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
999 gchar *event = NULL;
1000 gchar *reason = NULL;
1001 gchar *warning;
1003 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1005 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
1006 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
1007 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1008 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
1009 } else {
1010 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1011 return;
1014 reason = sipmsg_get_ms_diagnostics_reason(msg);
1015 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1016 if (!reason) { // for LCS2005
1017 if (event && sipe_strcase_equal(event, "unregistered")) {
1018 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1019 reason = g_strdup(_("you are already signed in at another location"));
1020 } else if (event && sipe_strcase_equal(event, "rejected")) {
1021 reason = g_strdup(_("user disabled")); // [MS-OCER]
1022 } else if (event && sipe_strcase_equal(event, "deactivated")) {
1023 reason = g_strdup(_("user moved")); // [MS-OCER]
1026 g_free(event);
1027 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
1028 g_free(reason);
1030 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1031 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1032 warning);
1033 g_free(warning);
1038 * Removes entries from local buddy list
1039 * that does not correspond ones in the roaming contact list.
1041 static void sipe_cleanup_local_blist(struct sipe_core_private *sipe_private)
1043 GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC,
1044 NULL, NULL);
1045 GSList *entry = buddies;
1046 struct sipe_buddy *buddy;
1047 sipe_backend_buddy b;
1048 gchar *bname;
1049 gchar *gname;
1051 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies));
1052 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private->buddies));
1053 while (entry) {
1054 b = entry->data;
1055 gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC, b);
1056 bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
1057 buddy = g_hash_table_lookup(sipe_private->buddies, bname);
1058 if(buddy) {
1059 gboolean in_sipe_groups = FALSE;
1060 GSList *entry2 = buddy->groups;
1061 while (entry2) {
1062 struct sipe_group *group = entry2->data;
1063 if (sipe_strequal(group->name, gname)) {
1064 in_sipe_groups = TRUE;
1065 break;
1067 entry2 = entry2->next;
1069 if(!in_sipe_groups) {
1070 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname, gname);
1071 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1073 } else {
1074 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname, gname);
1075 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1077 g_free(bname);
1078 g_free(gname);
1079 entry = entry->next;
1081 g_slist_free(buddies);
1085 * A callback for g_hash_table_foreach
1087 static void sipe_buddy_subscribe_cb(char *buddy_name,
1088 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1089 struct sipe_core_private *sipe_private)
1091 guint time_range = (g_hash_table_size(sipe_private->buddies) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1094 * g_hash_table_size() can never return 0, otherwise this function
1095 * wouldn't be called :-) But to keep Coverity happy...
1097 if (time_range) {
1098 gchar *action_name = sipe_utils_presence_key(buddy_name);
1099 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
1101 sipe_schedule_mseconds(sipe_private,
1102 action_name,
1103 g_strdup(buddy_name),
1104 timeout,
1105 sipe_subscribe_presence_single_cb,
1106 g_free);
1107 g_free(action_name);
1111 /* Replace "~" with localized version of "Other Contacts" */
1112 static const gchar *get_group_name(const sipe_xml *node)
1114 const gchar *name = sipe_xml_attribute(node, "name");
1115 return(g_str_has_prefix(name, "~") ? _("Other Contacts") : name);
1118 static void add_new_group(struct sipe_core_private *sipe_private,
1119 const sipe_xml *node)
1121 struct sipe_group *group = g_new0(struct sipe_group, 1);
1123 group->name = g_strdup(get_group_name(node));
1124 group->id = (int)g_ascii_strtod(sipe_xml_attribute(node, "id"), NULL);
1126 sipe_group_add(sipe_private, group);
1129 static void add_new_buddy(struct sipe_core_private *sipe_private,
1130 const sipe_xml *node,
1131 const gchar *uri,
1132 const gchar *alias)
1134 const gchar *name = sipe_xml_attribute(node, "name");
1135 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1136 gchar *normalized_uri = g_ascii_strdown(uri, -1);
1137 struct sipe_buddy *buddy = NULL;
1138 gchar *tmp;
1139 gchar **item_groups;
1140 int i = 0;
1142 /* assign to group Other Contacts if nothing else received */
1143 tmp = g_strdup(sipe_xml_attribute(node, "groups"));
1144 if (is_empty(tmp)) {
1145 struct sipe_group *group = sipe_group_find_by_name(sipe_private,
1146 _("Other Contacts"));
1147 g_free(tmp);
1148 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1150 item_groups = g_strsplit(tmp, " ", 0);
1151 g_free(tmp);
1153 while (item_groups[i]) {
1154 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1155 g_ascii_strtod(item_groups[i],
1156 NULL));
1158 /* If couldn't find the right group for this contact, */
1159 /* then just put it in the first group we have */
1160 if ((group == NULL) &&
1161 (g_slist_length(sipe_private->groups) > 0))
1162 group = sipe_private->groups->data;
1164 if (group) {
1165 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1166 normalized_uri,
1167 group->name);
1168 gchar *b_alias;
1170 if (!b) {
1171 b = sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1172 normalized_uri,
1173 alias,
1174 group->name);
1175 SIPE_DEBUG_INFO("Created new buddy %s with alias %s",
1176 normalized_uri, alias);
1179 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b);
1180 if (sipe_strcase_equal(alias, b_alias) &&
1181 !is_empty(name)) {
1182 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1184 name);
1185 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1186 normalized_uri, group->name, b_alias, name);
1188 g_free(b_alias);
1190 if (!buddy)
1191 buddy = sipe_buddy_add(sipe_private,
1192 normalized_uri);
1194 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
1195 group,
1196 (GCompareFunc)sipe_group_compare,
1197 NULL);
1199 SIPE_DEBUG_INFO("Added buddy %s to group %s",
1200 buddy->name, group->name);
1201 } else {
1202 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1203 name);
1206 i++;
1209 g_strfreev(item_groups);
1210 g_free(normalized_uri);
1213 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1214 struct sipmsg *msg)
1216 int len = msg->bodylen;
1218 const gchar *tmp = sipmsg_find_header(msg, "Event");
1219 const sipe_xml *item;
1220 sipe_xml *isc;
1221 guint delta;
1222 const sipe_xml *group_node;
1224 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1225 return FALSE;
1228 /* Convert the contact from XML to backend Buddies */
1229 isc = sipe_xml_parse(msg->body, len);
1230 if (!isc) {
1231 return FALSE;
1234 /* [MS-SIP]: deltaNum MUST be non-zero */
1235 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1236 if (delta) {
1237 sipe_private->deltanum_contacts = delta;
1240 /* Process whole buddy list (only sent once?) */
1241 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1243 /* Start processing contact list */
1244 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
1246 /* Parse groups */
1247 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node))
1248 add_new_group(sipe_private, group_node);
1250 /* Make sure we have at least one group */
1251 if (g_slist_length(sipe_private->groups) == 0) {
1252 sipe_group_create(sipe_private, _("Other Contacts"), NULL);
1255 /* Parse contacts */
1256 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1257 const gchar *name = sipe_xml_attribute(item, "uri");
1258 gchar *uri = sip_uri_from_name(name);
1259 add_new_buddy(sipe_private, item, uri, name);
1260 g_free(uri);
1263 sipe_cleanup_local_blist(sipe_private);
1265 /* Add self-contact if not there yet. 2005 systems. */
1266 /* This will resemble subscription to roaming_self in 2007 systems */
1267 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1268 gchar *self_uri = sip_uri_self(sipe_private);
1269 sipe_buddy_add(sipe_private, self_uri);
1270 g_free(self_uri);
1273 /* Finished processing contact list */
1274 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
1276 /* Process buddy list updates */
1277 } else if (sipe_strequal(sipe_xml_name(isc), "contactDelta")) {
1279 /* Process new groups */
1280 for (group_node = sipe_xml_child(isc, "addedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1281 add_new_group(sipe_private, group_node);
1283 /* Process modified groups */
1284 for (group_node = sipe_xml_child(isc, "modifiedGroup"); group_node; group_node = sipe_xml_twin(group_node)) {
1285 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1286 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1287 NULL));
1288 if (group) {
1289 const gchar *name = get_group_name(group_node);
1291 if (!(is_empty(name) ||
1292 sipe_strequal(group->name, name)) &&
1293 sipe_group_rename(sipe_private,
1294 group,
1295 name))
1296 SIPE_DEBUG_INFO("Replaced group %d name with %s", group->id, name);
1300 /* Process new buddies */
1301 for (item = sipe_xml_child(isc, "addedContact"); item; item = sipe_xml_twin(item)) {
1302 const gchar *uri = sipe_xml_attribute(item, "uri");
1303 const gchar *name = sipe_get_no_sip_uri(uri);
1304 add_new_buddy(sipe_private, item, uri, name);
1307 /* Process modified buddies */
1308 for (item = sipe_xml_child(isc, "modifiedContact"); item; item = sipe_xml_twin(item)) {
1309 const gchar *uri = sipe_xml_attribute(item, "uri");
1310 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies,
1311 uri);
1313 if (buddy) {
1314 gchar **item_groups = g_strsplit(sipe_xml_attribute(item,
1315 "groups"),
1316 " ", 0);
1318 /* this should be defined. Otherwise we would get "deletedContact" */
1319 if (item_groups) {
1320 const gchar *name = sipe_xml_attribute(item, "name");
1321 gboolean empty_name = is_empty(name);
1322 GSList *found = NULL;
1323 GSList *entry;
1324 int i = 0;
1326 while (item_groups[i]) {
1327 struct sipe_group *group = sipe_group_find_by_id(sipe_private,
1328 g_ascii_strtod(item_groups[i],
1329 NULL));
1330 /* ignore unkown groups */
1331 if (group) {
1332 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1333 uri,
1334 group->name);
1336 /* add group to found list */
1337 found = g_slist_prepend(found, group);
1339 if (b) {
1340 /* new alias? */
1341 gchar *b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC,
1344 if (!(empty_name ||
1345 sipe_strequal(b_alias, name))) {
1346 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC,
1348 name);
1349 SIPE_DEBUG_INFO("Replaced for buddy %s in group '%s' old alias '%s' with '%s'",
1350 uri, group->name, b_alias, name);
1352 g_free(b_alias);
1354 } else {
1355 const gchar *alias = empty_name ? uri : name;
1356 /* buddy was not in this group */
1357 sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
1358 uri,
1359 alias,
1360 group->name);
1361 buddy->groups = sipe_utils_slist_insert_unique_sorted(buddy->groups,
1362 group,
1363 (GCompareFunc) sipe_group_compare,
1364 NULL);
1365 SIPE_DEBUG_INFO("Added buddy %s (alias '%s' to group '%s'",
1366 uri, alias, group->name);
1370 /* next group */
1371 i++;
1373 g_strfreev(item_groups);
1375 /* removed from groups? */
1376 entry = buddy->groups;
1377 while (entry) {
1378 GSList *remove_link = entry;
1379 struct sipe_group *group = remove_link->data;
1381 /* next buddy group */
1382 entry = entry->next;
1384 /* old group NOT found in new list? */
1385 if (g_slist_find(found, group) == NULL) {
1386 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1387 uri,
1388 group->name);
1389 SIPE_DEBUG_INFO("Removing buddy %s from group '%s'",
1390 uri, group->name);
1391 /* this should never be NULL */
1392 if (oldb)
1393 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1394 oldb);
1395 buddy->groups = g_slist_remove_link(buddy->groups,
1396 remove_link);
1399 g_slist_free(found);
1404 /* Process deleted buddies */
1405 for (item = sipe_xml_child(isc, "deletedContact"); item; item = sipe_xml_twin(item)) {
1406 const gchar *uri = sipe_xml_attribute(item, "uri");
1407 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies,
1408 uri);
1410 if (buddy) {
1411 GSList *entry = buddy->groups;
1413 SIPE_DEBUG_INFO("Removing buddy %s", uri);
1414 while (entry) {
1415 struct sipe_group *group = entry->data;
1416 sipe_backend_buddy oldb = sipe_backend_buddy_find(SIPE_CORE_PUBLIC,
1417 uri,
1418 group->name);
1419 /* this should never be NULL */
1420 if (oldb)
1421 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC,
1422 oldb);
1424 /* next buddy group */
1425 entry = entry->next;
1427 sipe_buddy_remove(sipe_private, buddy);
1431 /* Process deleted groups
1433 * NOTE: all buddies will already have been removed from the
1434 * group prior to this. The log shows that OCS actually
1435 * sends two separate updates when you delete a group:
1437 * - first one with "modifiedContact" removing buddies
1438 * from the group, leaving it empty, and
1440 * - then one with "deletedGroup" removing the group
1442 for (group_node = sipe_xml_child(isc, "deletedGroup"); group_node; group_node = sipe_xml_twin(group_node))
1443 sipe_group_remove(sipe_private,
1444 sipe_group_find_by_id(sipe_private,
1445 (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"),
1446 NULL)));
1449 sipe_xml_free(isc);
1451 /* subscribe to buddies */
1452 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) {
1453 /* do it once, then count Expire field to schedule resubscribe */
1454 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
1455 sipe_subscribe_presence_batched(sipe_private);
1456 } else {
1457 g_hash_table_foreach(sipe_private->buddies,
1458 (GHFunc)sipe_buddy_subscribe_cb,
1459 sipe_private);
1461 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES);
1463 /* for 2005 systems schedule contacts' status update
1464 * based on their calendar information
1466 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1467 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1470 return 0;
1473 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1474 struct sipmsg *msg)
1476 guint delta;
1477 sipe_xml *xml;
1479 xml = sipe_xml_parse(msg->body, msg->bodylen);
1480 if (!xml)
1481 return;
1483 /* [MS-SIP]: deltaNum MUST be non-zero */
1484 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1485 if (delta) {
1486 sipe_private->deltanum_acl = delta;
1489 sipe_xml_free(xml);
1492 struct sipe_auth_job {
1493 gchar *who;
1494 struct sipe_core_private *sipe_private;
1497 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1498 const gchar* who,
1499 gboolean allow)
1501 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1503 if (allow) {
1504 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1505 } else {
1506 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1509 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1510 sipe_ocs2007_change_access_level(sipe_private,
1511 (allow ? -1 : 32000),
1512 "user",
1513 sipe_get_no_sip_uri(who));
1514 } else {
1515 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1520 static void sipe_auth_user_cb(gpointer data)
1522 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1523 if (!job) return;
1525 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1526 job->who,
1527 TRUE);
1528 g_free(job);
1531 static void sipe_deny_user_cb(gpointer data)
1533 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1534 if (!job) return;
1536 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1537 job->who,
1538 FALSE);
1539 g_free(job);
1542 /* OCS2005- */
1543 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1544 struct sipmsg * msg)
1546 sipe_xml *watchers;
1547 const sipe_xml *watcher;
1548 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1549 if (msg->response != 0 && msg->response != 200) return;
1551 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1553 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1554 if (!watchers) return;
1556 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1557 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1558 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1559 gboolean on_list = g_hash_table_lookup(sipe_private->buddies, remote_user) != NULL;
1561 // TODO pull out optional displayName to pass as alias
1562 if (remote_user) {
1563 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1564 job->who = remote_user;
1565 job->sipe_private = sipe_private;
1566 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1567 remote_user,
1568 alias,
1569 on_list,
1570 sipe_auth_user_cb,
1571 sipe_deny_user_cb,
1572 (gpointer)job);
1577 sipe_xml_free(watchers);
1578 return;
1582 * Dispatcher for all incoming subscription information
1583 * whether it comes from NOTIFY, BENOTIFY requests or
1584 * piggy-backed to subscription's OK responce.
1586 void process_incoming_notify(struct sipe_core_private *sipe_private,
1587 struct sipmsg *msg)
1589 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1590 const gchar *event = sipmsg_find_header(msg, "Event");
1591 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1593 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1595 /* implicit subscriptions */
1596 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1597 sipe_process_imdn(sipe_private, msg);
1599 /* event subscriptions */
1600 } else if (event) {
1602 /* One-off subscriptions - sent with "Expires: 0" */
1603 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2")) {
1604 sipe_process_provisioning_v2(sipe_private, msg);
1605 } else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning")) {
1606 sipe_process_provisioning(sipe_private, msg);
1607 } else if (sipe_strcase_equal(event, "presence")) {
1608 sipe_process_presence(sipe_private, msg);
1609 } else if (sipe_strcase_equal(event, "registration-notify")) {
1610 sipe_process_registration_notify(sipe_private, msg);
1612 /* Subscriptions with timeout */
1613 } else if (!subscription_state || strstr(subscription_state, "active")) {
1614 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
1615 sipe_process_roaming_contacts(sipe_private, msg);
1616 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self")) {
1617 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1618 } else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL")) {
1619 sipe_process_roaming_acl(sipe_private, msg);
1620 } else if (sipe_strcase_equal(event, "presence.wpending")) {
1621 sipe_process_presence_wpending(sipe_private, msg);
1622 } else if (sipe_strcase_equal(event, "conference")) {
1623 sipe_process_conference(sipe_private, msg);
1630 Local Variables:
1631 mode: c
1632 c-file-style: "bsd"
1633 indent-tabs-mode: t
1634 tab-width: 8
1635 End: