core cleanup: separate code for sipe_backend_account_status_and_note()
[siplcs.git] / src / core / sipe-notify.c
blob74d445a2825cf9df7b603eb242e902c7706bd2dd
1 /**
2 * @file sipe-notify.c
4 * pidgin-sipe
6 * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Process incoming SIP NOTIFY/BENOTIFY messages
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include "sipe-common.h"
38 #include "http-conn.h" /* sipe-cal.h requires this */
39 #include "sipmsg.h"
40 #include "sip-csta.h"
41 #include "sip-soap.h"
42 #include "sip-transport.h"
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
45 #include "sipe-cal.h"
46 #include "sipe-conf.h"
47 #include "sipe-core.h"
48 #include "sipe-core-private.h"
49 #include "sipe-group.h"
50 #include "sipe-media.h"
51 #include "sipe-mime.h"
52 #include "sipe-nls.h"
53 #include "sipe-notify.h"
54 #include "sipe-ocs2005.h"
55 #include "sipe-ocs2007.h"
56 #include "sipe-schedule.h"
57 #include "sipe-subscriptions.h"
58 #include "sipe-utils.h"
59 #include "sipe-xml.h"
60 #define _SIPE_NEED_ACTIVITIES
61 #include "sipe.h"
63 /* OCS2005 */
64 static void sipe_process_provisioning(struct sipe_core_private *sipe_private,
65 struct sipmsg *msg)
67 sipe_xml *xn_provision;
68 const sipe_xml *node;
70 xn_provision = sipe_xml_parse(msg->body, msg->bodylen);
71 if ((node = sipe_xml_child(xn_provision, "user"))) {
72 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node, "uri"));
73 if ((node = sipe_xml_child(node, "line"))) {
74 const gchar *line_uri = sipe_xml_attribute(node, "uri");
75 const gchar *server = sipe_xml_attribute(node, "server");
76 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri, server);
77 sip_csta_open(sipe_private, line_uri, server);
80 sipe_xml_free(xn_provision);
83 /* OCS2007+ */
84 static void sipe_process_provisioning_v2(struct sipe_core_private *sipe_private,
85 struct sipmsg *msg)
87 sipe_xml *xn_provision_group_list;
88 const sipe_xml *node;
90 xn_provision_group_list = sipe_xml_parse(msg->body, msg->bodylen);
92 /* provisionGroup */
93 for (node = sipe_xml_child(xn_provision_group_list, "provisionGroup"); node; node = sipe_xml_twin(node)) {
94 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node, "name"))) {
95 g_free(sipe_private->focus_factory_uri);
96 sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
97 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
98 sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
100 #ifdef HAVE_VV
101 g_free(sipe_private->mras_uri);
102 sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
103 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
104 sipe_private->mras_uri ? sipe_private->mras_uri : "");
106 if (sipe_private->mras_uri)
107 sipe_media_get_av_edge_credentials(sipe_private);
108 #endif
109 break;
112 sipe_xml_free(xn_provision_group_list);
115 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
116 const gchar *data, unsigned len)
118 sipe_xml *xn_list;
119 const sipe_xml *xn_resource;
120 GHashTable *servers = g_hash_table_new_full(g_str_hash, g_str_equal,
121 g_free, NULL);
122 GSList *server;
123 gchar *host;
125 xn_list = sipe_xml_parse(data, len);
127 for (xn_resource = sipe_xml_child(xn_list, "resource");
128 xn_resource;
129 xn_resource = sipe_xml_twin(xn_resource) )
131 const char *uri, *state;
132 const sipe_xml *xn_instance;
134 xn_instance = sipe_xml_child(xn_resource, "instance");
135 if (!xn_instance) continue;
137 uri = sipe_xml_attribute(xn_resource, "uri");
138 state = sipe_xml_attribute(xn_instance, "state");
139 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri, state);
141 if (strstr(state, "resubscribe")) {
142 const char *poolFqdn = sipe_xml_attribute(xn_instance, "poolFqdn");
144 if (poolFqdn) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
145 gchar *user = g_strdup(uri);
146 host = g_strdup(poolFqdn);
147 server = g_hash_table_lookup(servers, host);
148 server = g_slist_append(server, user);
149 g_hash_table_insert(servers, host, server);
150 } else {
151 sipe_subscribe_presence_single(sipe_private,
152 (void *) uri);
157 /* Send out any deferred poolFqdn subscriptions */
158 g_hash_table_foreach(servers, (GHFunc) sipe_subscribe_poolfqdn_resource_uri, sipe_private);
159 g_hash_table_destroy(servers);
161 sipe_xml_free(xn_list);
165 * Update user phone
166 * Suitable for both 2005 and 2007 systems.
168 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
169 * @param phone_type
170 * @param phone may be modified to strip white space
171 * @param phone_display_string may be modified to strip white space
173 static void
174 sipe_update_user_phone(struct sipe_core_private *sipe_private,
175 const gchar *uri,
176 const gchar *phone_type,
177 gchar *phone,
178 gchar *phone_display_string)
180 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
181 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
183 if(!phone || strlen(phone) == 0) return;
185 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
186 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
187 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
188 } else if (sipe_strequal(phone_type, "home")) {
189 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
190 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
191 } else if (sipe_strequal(phone_type, "other")) {
192 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
193 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
194 } else if (sipe_strequal(phone_type, "custom1")) {
195 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
196 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
199 sipe_buddy_update_property(sipe_private, uri, phone_node, phone);
200 if (phone_display_string) {
201 sipe_buddy_update_property(sipe_private, uri, phone_display_node, phone_display_string);
205 static void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
206 const gchar *data,
207 unsigned len)
209 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
210 char *activity = NULL;
211 const char *epid;
212 const char *status_id = NULL;
213 const char *name;
214 char *uri;
215 char *self_uri = sip_uri_self(sipe_private);
216 int avl;
217 int act;
218 const char *device_name = NULL;
219 const char *cal_start_time = NULL;
220 const char *cal_granularity = NULL;
221 char *cal_free_busy_base64 = NULL;
222 struct sipe_buddy *sbuddy;
223 const sipe_xml *node;
224 sipe_xml *xn_presentity;
225 const sipe_xml *xn_availability;
226 const sipe_xml *xn_activity;
227 const sipe_xml *xn_display_name;
228 const sipe_xml *xn_email;
229 const sipe_xml *xn_phone_number;
230 const sipe_xml *xn_userinfo;
231 const sipe_xml *xn_note;
232 const sipe_xml *xn_oof;
233 const sipe_xml *xn_state;
234 const sipe_xml *xn_contact;
235 char *note;
236 int user_avail;
237 const char *user_avail_nil;
238 int res_avail;
239 time_t user_avail_since = 0;
240 time_t activity_since = 0;
242 /* fix for Reuters environment on Linux */
243 if (data && strstr(data, "encoding=\"utf-16\"")) {
244 char *tmp_data;
245 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
246 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
247 g_free(tmp_data);
248 } else {
249 xn_presentity = sipe_xml_parse(data, len);
252 xn_availability = sipe_xml_child(xn_presentity, "availability");
253 xn_activity = sipe_xml_child(xn_presentity, "activity");
254 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
255 xn_email = sipe_xml_child(xn_presentity, "email");
256 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
257 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
258 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
259 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
260 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
261 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
262 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
263 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
264 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
265 note = xn_note ? sipe_xml_data(xn_note) : NULL;
267 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
268 user_avail = 0;
269 user_avail_since = 0;
272 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
273 uri = sip_uri_from_name(name);
274 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
275 epid = sipe_xml_attribute(xn_availability, "epid");
276 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
278 status_id = sipe_get_status_by_act_avail_2005(act, avl, &activity);
279 res_avail = sipe_get_availability_by_status(status_id, NULL);
280 if (user_avail > res_avail) {
281 res_avail = user_avail;
282 status_id = sipe_get_status_by_availability(user_avail, NULL);
285 if (xn_display_name) {
286 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
287 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
288 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
289 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
290 char *tel_uri = sip_to_tel_uri(phone_number);
292 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
293 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
294 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
295 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
297 g_free(tel_uri);
298 g_free(phone_label);
299 g_free(phone_number);
300 g_free(email);
301 g_free(display_name);
304 if (xn_contact) {
305 /* tel */
306 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
308 /* Ex.: <tel type="work">tel:+3222220000</tel> */
309 const char *phone_type = sipe_xml_attribute(node, "type");
310 char* phone = sipe_xml_data(node);
312 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
314 g_free(phone);
318 /* devicePresence */
319 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
320 const sipe_xml *xn_device_name;
321 const sipe_xml *xn_calendar_info;
322 const sipe_xml *xn_state;
323 char *state;
325 /* deviceName */
326 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
327 xn_device_name = sipe_xml_child(node, "deviceName");
328 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
331 /* calendarInfo */
332 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
333 if (xn_calendar_info) {
334 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
336 if (cal_start_time) {
337 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
338 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
340 if (cal_start_time_t_tmp > cal_start_time_t) {
341 cal_start_time = cal_start_time_tmp;
342 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
343 g_free(cal_free_busy_base64);
344 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
346 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time, cal_granularity, cal_free_busy_base64);
348 } else {
349 cal_start_time = cal_start_time_tmp;
350 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
351 g_free(cal_free_busy_base64);
352 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
354 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time, cal_granularity, cal_free_busy_base64);
358 /* state */
359 xn_state = sipe_xml_child(node, "states/state");
360 if (xn_state) {
361 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
362 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
364 state = sipe_xml_data(xn_state);
365 if (dev_avail_since > user_avail_since &&
366 dev_avail >= res_avail)
368 res_avail = dev_avail;
369 if (!is_empty(state))
371 if (sipe_strequal(state, sipe_activity_to_token(SIPE_ACTIVITY_ON_PHONE))) {
372 g_free(activity);
373 activity = g_strdup(sipe_activity_description(SIPE_ACTIVITY_ON_PHONE));
374 } else if (sipe_strequal(state, "presenting")) {
375 g_free(activity);
376 activity = g_strdup(sipe_activity_description(SIPE_ACTIVITY_IN_CONF));
377 } else {
378 activity = state;
379 state = NULL;
381 activity_since = dev_avail_since;
383 status_id = sipe_get_status_by_availability(res_avail, &activity);
385 g_free(state);
389 /* oof */
390 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
391 g_free(activity);
392 activity = g_strdup(sipe_activity_description(SIPE_ACTIVITY_OOF));
393 activity_since = 0;
396 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
397 if (sbuddy)
399 g_free(sbuddy->activity);
400 sbuddy->activity = activity;
401 activity = NULL;
403 sbuddy->activity_since = activity_since;
405 sbuddy->user_avail = user_avail;
406 sbuddy->user_avail_since = user_avail_since;
408 g_free(sbuddy->note);
409 sbuddy->note = NULL;
410 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
412 sbuddy->is_oof_note = (xn_oof != NULL);
414 g_free(sbuddy->device_name);
415 sbuddy->device_name = NULL;
416 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
418 if (!is_empty(cal_free_busy_base64)) {
419 g_free(sbuddy->cal_start_time);
420 sbuddy->cal_start_time = g_strdup(cal_start_time);
422 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
424 g_free(sbuddy->cal_free_busy_base64);
425 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
426 cal_free_busy_base64 = NULL;
428 g_free(sbuddy->cal_free_busy);
429 sbuddy->cal_free_busy = NULL;
432 sbuddy->last_non_cal_status_id = status_id;
433 g_free(sbuddy->last_non_cal_activity);
434 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
436 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
437 if (!sipe_strequal(sbuddy->note, sip->note)) /* not same */
439 sip->is_oof_note = sbuddy->is_oof_note;
441 g_free(sip->note);
442 sip->note = g_strdup(sbuddy->note);
444 sip->note_since = time(NULL);
447 g_free(sip->status);
448 sip->status = g_strdup(sbuddy->last_non_cal_status_id);
451 g_free(cal_free_busy_base64);
452 g_free(activity);
454 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
455 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status_id);
457 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
458 sipe_ocs2005_user_info_has_updated(sipe_private, xn_userinfo);
461 g_free(note);
462 sipe_xml_free(xn_presentity);
463 g_free(uri);
464 g_free(self_uri);
467 static void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
468 const gchar *data,
469 unsigned len)
471 const char *uri;
472 sipe_xml *xn_categories;
473 const sipe_xml *xn_category;
474 const char *status = NULL;
475 gboolean do_update_status = FALSE;
476 gboolean has_note_cleaned = FALSE;
477 gboolean has_free_busy_cleaned = FALSE;
479 xn_categories = sipe_xml_parse(data, len);
480 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
482 for (xn_category = sipe_xml_child(xn_categories, "category");
483 xn_category ;
484 xn_category = sipe_xml_twin(xn_category) )
486 const sipe_xml *xn_node;
487 const char *tmp;
488 const char *attrVar = sipe_xml_attribute(xn_category, "name");
489 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
490 sipe_utils_str_to_time(tmp) : 0;
492 /* contactCard */
493 if (sipe_strequal(attrVar, "contactCard"))
495 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
497 if (card) {
498 const sipe_xml *node;
499 /* identity - Display Name and email */
500 node = sipe_xml_child(card, "identity");
501 if (node) {
502 char* display_name = sipe_xml_data(
503 sipe_xml_child(node, "name/displayName"));
504 char* email = sipe_xml_data(
505 sipe_xml_child(node, "email"));
507 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
508 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
510 g_free(display_name);
511 g_free(email);
513 /* company */
514 node = sipe_xml_child(card, "company");
515 if (node) {
516 char* company = sipe_xml_data(node);
517 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
518 g_free(company);
520 /* department */
521 node = sipe_xml_child(card, "department");
522 if (node) {
523 char* department = sipe_xml_data(node);
524 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
525 g_free(department);
527 /* title */
528 node = sipe_xml_child(card, "title");
529 if (node) {
530 char* title = sipe_xml_data(node);
531 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
532 g_free(title);
534 /* office */
535 node = sipe_xml_child(card, "office");
536 if (node) {
537 char* office = sipe_xml_data(node);
538 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
539 g_free(office);
541 /* site (url) */
542 node = sipe_xml_child(card, "url");
543 if (node) {
544 char* site = sipe_xml_data(node);
545 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
546 g_free(site);
548 /* phone */
549 for (node = sipe_xml_child(card, "phone");
550 node;
551 node = sipe_xml_twin(node))
553 const char *phone_type = sipe_xml_attribute(node, "type");
554 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
555 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
557 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
559 g_free(phone);
560 g_free(phone_display_string);
562 /* address */
563 for (node = sipe_xml_child(card, "address");
564 node;
565 node = sipe_xml_twin(node))
567 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
568 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
569 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
570 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
571 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
572 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
574 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
575 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
576 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
577 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
578 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
580 g_free(street);
581 g_free(city);
582 g_free(state);
583 g_free(zipcode);
584 g_free(country_code);
586 break;
591 /* note */
592 else if (sipe_strequal(attrVar, "note"))
594 if (uri) {
595 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
597 if (!has_note_cleaned) {
598 has_note_cleaned = TRUE;
600 g_free(sbuddy->note);
601 sbuddy->note = NULL;
602 sbuddy->is_oof_note = FALSE;
603 sbuddy->note_since = publish_time;
605 do_update_status = TRUE;
607 if (sbuddy && (publish_time >= sbuddy->note_since)) {
608 /* clean up in case no 'note' element is supplied
609 * which indicate note removal in client
611 g_free(sbuddy->note);
612 sbuddy->note = NULL;
613 sbuddy->is_oof_note = FALSE;
614 sbuddy->note_since = publish_time;
616 xn_node = sipe_xml_child(xn_category, "note/body");
617 if (xn_node) {
618 char *tmp;
619 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
620 g_free(tmp);
621 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
622 sbuddy->note_since = publish_time;
624 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
625 uri, sbuddy->note ? sbuddy->note : "");
627 /* to trigger UI refresh in case no status info is supplied in this update */
628 do_update_status = TRUE;
632 /* state */
633 else if(sipe_strequal(attrVar, "state"))
635 char *tmp;
636 int availability;
637 const sipe_xml *xn_availability;
638 const sipe_xml *xn_activity;
639 const sipe_xml *xn_meeting_subject;
640 const sipe_xml *xn_meeting_location;
641 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
643 xn_node = sipe_xml_child(xn_category, "state");
644 if (!xn_node) continue;
645 xn_availability = sipe_xml_child(xn_node, "availability");
646 if (!xn_availability) continue;
647 xn_activity = sipe_xml_child(xn_node, "activity");
648 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
649 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
651 tmp = sipe_xml_data(xn_availability);
652 availability = atoi(tmp);
653 g_free(tmp);
655 /* activity, meeting_subject, meeting_location */
656 if (sbuddy) {
657 char *tmp = NULL;
659 /* activity */
660 g_free(sbuddy->activity);
661 sbuddy->activity = NULL;
662 if (xn_activity) {
663 const char *token = sipe_xml_attribute(xn_activity, "token");
664 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
666 /* from token */
667 if (!is_empty(token)) {
668 sbuddy->activity = g_strdup(sipe_activity_description_from_token(token));
670 /* from custom element */
671 if (xn_custom) {
672 char *custom = sipe_xml_data(xn_custom);
674 if (!is_empty(custom)) {
675 sbuddy->activity = custom;
676 custom = NULL;
678 g_free(custom);
681 /* meeting_subject */
682 g_free(sbuddy->meeting_subject);
683 sbuddy->meeting_subject = NULL;
684 if (xn_meeting_subject) {
685 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
687 if (!is_empty(meeting_subject)) {
688 sbuddy->meeting_subject = meeting_subject;
689 meeting_subject = NULL;
691 g_free(meeting_subject);
693 /* meeting_location */
694 g_free(sbuddy->meeting_location);
695 sbuddy->meeting_location = NULL;
696 if (xn_meeting_location) {
697 char *meeting_location = sipe_xml_data(xn_meeting_location);
699 if (!is_empty(meeting_location)) {
700 sbuddy->meeting_location = meeting_location;
701 meeting_location = NULL;
703 g_free(meeting_location);
706 status = sipe_get_status_by_availability(availability, &tmp);
707 if (sbuddy->activity && tmp) {
708 char *tmp2 = sbuddy->activity;
710 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, tmp);
711 g_free(tmp);
712 g_free(tmp2);
713 } else if (tmp) {
714 sbuddy->activity = tmp;
718 do_update_status = TRUE;
720 /* calendarData */
721 else if(sipe_strequal(attrVar, "calendarData"))
723 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
724 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
725 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
727 if (sbuddy && xn_free_busy) {
728 if (!has_free_busy_cleaned) {
729 has_free_busy_cleaned = TRUE;
731 g_free(sbuddy->cal_start_time);
732 sbuddy->cal_start_time = NULL;
734 g_free(sbuddy->cal_free_busy_base64);
735 sbuddy->cal_free_busy_base64 = NULL;
737 g_free(sbuddy->cal_free_busy);
738 sbuddy->cal_free_busy = NULL;
740 sbuddy->cal_free_busy_published = publish_time;
743 if (publish_time >= sbuddy->cal_free_busy_published) {
744 g_free(sbuddy->cal_start_time);
745 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
747 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
748 15 : 0;
750 g_free(sbuddy->cal_free_busy_base64);
751 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
753 g_free(sbuddy->cal_free_busy);
754 sbuddy->cal_free_busy = NULL;
756 sbuddy->cal_free_busy_published = publish_time;
758 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: startTime=%s granularity=%d cal_free_busy_base64=\n%s", sbuddy->cal_start_time, sbuddy->cal_granularity, sbuddy->cal_free_busy_base64);
762 if (sbuddy && xn_working_hours) {
763 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
768 if (do_update_status) {
769 if (!status) { /* no status category in this update, using contact's current status */
770 status = sipe_get_buddy_status(sipe_private,
771 uri);
774 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
775 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status);
778 sipe_xml_free(xn_categories);
781 static void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
782 const gchar *data,
783 unsigned len)
785 gchar *uri;
786 gchar *getbasic;
787 gchar *activity = NULL;
788 sipe_xml *pidf;
789 const sipe_xml *basicstatus = NULL, *tuple, *status;
790 gboolean isonline = FALSE;
791 const sipe_xml *display_name_node;
793 pidf = sipe_xml_parse(data, len);
794 if (!pidf) {
795 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
796 return;
799 if ((tuple = sipe_xml_child(pidf, "tuple")))
801 if ((status = sipe_xml_child(tuple, "status"))) {
802 basicstatus = sipe_xml_child(status, "basic");
806 if (!basicstatus) {
807 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
808 sipe_xml_free(pidf);
809 return;
812 getbasic = sipe_xml_data(basicstatus);
813 if (!getbasic) {
814 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
815 sipe_xml_free(pidf);
816 return;
819 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
820 if (strstr(getbasic, "open")) {
821 isonline = TRUE;
823 g_free(getbasic);
825 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
827 display_name_node = sipe_xml_child(pidf, "display-name");
828 if (display_name_node) {
829 char * display_name = sipe_xml_data(display_name_node);
831 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
832 g_free(display_name);
835 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
836 if ((status = sipe_xml_child(tuple, "status"))) {
837 if ((basicstatus = sipe_xml_child(status, "activities"))) {
838 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
839 activity = sipe_xml_data(basicstatus);
840 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
846 sipe_buddy_status_from_activity(sipe_private,
847 uri,
848 activity,
849 isonline);
851 g_free(activity);
852 g_free(uri);
853 sipe_xml_free(pidf);
856 static void sipe_presence_mime_cb(gpointer user_data, /* sipe_core_private */
857 const GSList *fields,
858 const gchar *body,
859 gsize length)
861 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
863 if (strstr(type,"application/rlmi+xml")) {
864 process_incoming_notify_rlmi_resub(user_data, body, length);
865 } else if (strstr(type, "text/xml+msrtc.pidf")) {
866 process_incoming_notify_msrtc(user_data, body, length);
867 } else {
868 process_incoming_notify_rlmi(user_data, body, length);
872 static void sipe_process_presence(struct sipe_core_private *sipe_private,
873 struct sipmsg *msg)
875 const char *ctype = sipmsg_find_header(msg, "Content-Type");
877 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype ? ctype : "");
879 if (ctype &&
880 (strstr(ctype, "application/rlmi+xml") ||
881 strstr(ctype, "application/msrtc-event-categories+xml")))
883 if (strstr(ctype, "multipart"))
885 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_mime_cb, sipe_private);
887 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
889 process_incoming_notify_rlmi(sipe_private, msg->body, msg->bodylen);
891 else if(strstr(ctype, "application/rlmi+xml"))
893 process_incoming_notify_rlmi_resub(sipe_private, msg->body, msg->bodylen);
896 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
898 process_incoming_notify_msrtc(sipe_private, msg->body, msg->bodylen);
900 else
902 process_incoming_notify_pidf(sipe_private, msg->body, msg->bodylen);
907 * Fires on deregistration event initiated by server.
908 * [MS-SIPREGE] SIP extension.
910 * OCS2007 Example
912 * Content-Type: text/registration-event
913 * subscription-state: terminated;expires=0
914 * ms-diagnostics-public: 4141;reason="User disabled"
916 * deregistered;event=rejected
918 static void sipe_process_registration_notify(struct sipe_core_private *sipe_private,
919 struct sipmsg *msg)
921 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
922 gchar *event = NULL;
923 gchar *reason = NULL;
924 gchar *warning;
926 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
928 if (!g_ascii_strncasecmp(contenttype, "text/registration-event", 23)) {
929 event = sipmsg_find_part_of_header(msg->body, "event=", NULL, NULL);
930 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
931 event = event ? event : sipmsg_find_part_of_header(msg->body, "event=", ";", NULL);
932 } else {
933 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
934 return;
937 reason = sipmsg_get_ms_diagnostics_reason(msg);
938 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
939 if (!reason) { // for LCS2005
940 if (event && sipe_strcase_equal(event, "unregistered")) {
941 //reason = g_strdup(_("User logged out")); // [MS-OCER]
942 reason = g_strdup(_("you are already signed in at another location"));
943 } else if (event && sipe_strcase_equal(event, "rejected")) {
944 reason = g_strdup(_("user disabled")); // [MS-OCER]
945 } else if (event && sipe_strcase_equal(event, "deactivated")) {
946 reason = g_strdup(_("user moved")); // [MS-OCER]
949 g_free(event);
950 warning = g_strdup_printf(_("You have been rejected by the server: %s"), reason ? reason : _("no reason given"));
951 g_free(reason);
953 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
954 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
955 warning);
956 g_free(warning);
961 * Removes entries from local buddy list
962 * that does not correspond ones in the roaming contact list.
964 static void sipe_cleanup_local_blist(struct sipe_core_private *sipe_private)
966 GSList *buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC,
967 NULL, NULL);
968 GSList *entry = buddies;
969 struct sipe_buddy *buddy;
970 sipe_backend_buddy b;
971 gchar *bname;
972 gchar *gname;
974 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies));
975 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private->buddies));
976 while (entry) {
977 b = entry->data;
978 gname = sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC, b);
979 bname = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
980 buddy = g_hash_table_lookup(sipe_private->buddies, bname);
981 if(buddy) {
982 gboolean in_sipe_groups = FALSE;
983 GSList *entry2 = buddy->groups;
984 while (entry2) {
985 struct sipe_group *group = entry2->data;
986 if (sipe_strequal(group->name, gname)) {
987 in_sipe_groups = TRUE;
988 break;
990 entry2 = entry2->next;
992 if(!in_sipe_groups) {
993 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname, gname);
994 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
996 } else {
997 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname, gname);
998 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC, b);
1000 g_free(bname);
1001 g_free(gname);
1002 entry = entry->next;
1004 g_slist_free(buddies);
1008 * A callback for g_hash_table_foreach
1010 static void sipe_buddy_subscribe_cb(char *buddy_name,
1011 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1012 struct sipe_core_private *sipe_private)
1014 gchar *action_name = sipe_utils_presence_key(buddy_name);
1015 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1016 guint time_range = (g_hash_table_size(sipe_private->buddies) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1017 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
1019 sipe_schedule_mseconds(sipe_private,
1020 action_name,
1021 g_strdup(buddy_name),
1022 timeout,
1023 sipe_subscribe_presence_single,
1024 g_free);
1025 g_free(action_name);
1028 static gboolean sipe_process_roaming_contacts(struct sipe_core_private *sipe_private,
1029 struct sipmsg *msg)
1031 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1032 int len = msg->bodylen;
1034 const gchar *tmp = sipmsg_find_header(msg, "Event");
1035 const sipe_xml *item;
1036 sipe_xml *isc;
1037 guint delta;
1038 const sipe_xml *group_node;
1039 if (!g_str_has_prefix(tmp, "vnd-microsoft-roaming-contacts")) {
1040 return FALSE;
1043 /* Convert the contact from XML to backend Buddies */
1044 isc = sipe_xml_parse(msg->body, len);
1045 if (!isc) {
1046 return FALSE;
1049 /* [MS-SIP]: deltaNum MUST be non-zero */
1050 delta = sipe_xml_int_attribute(isc, "deltaNum", 0);
1051 if (delta) {
1052 sipe_private->deltanum_contacts = delta;
1055 if (sipe_strequal(sipe_xml_name(isc), "contactList")) {
1057 /* Parse groups */
1058 for (group_node = sipe_xml_child(isc, "group"); group_node; group_node = sipe_xml_twin(group_node)) {
1059 struct sipe_group * group = g_new0(struct sipe_group, 1);
1060 const char *name = sipe_xml_attribute(group_node, "name");
1062 if (g_str_has_prefix(name, "~")) {
1063 name = _("Other Contacts");
1065 group->name = g_strdup(name);
1066 group->id = (int)g_ascii_strtod(sipe_xml_attribute(group_node, "id"), NULL);
1068 sipe_group_add(sipe_private, group);
1071 // Make sure we have at least one group
1072 if (g_slist_length(sipe_private->groups) == 0) {
1073 sipe_group_create(sipe_private, _("Other Contacts"), NULL);
1076 /* Parse contacts */
1077 for (item = sipe_xml_child(isc, "contact"); item; item = sipe_xml_twin(item)) {
1078 const gchar *uri = sipe_xml_attribute(item, "uri");
1079 const gchar *name = sipe_xml_attribute(item, "name");
1080 gchar *buddy_name;
1081 struct sipe_buddy *buddy = NULL;
1082 gchar *tmp;
1083 gchar **item_groups;
1084 int i = 0;
1086 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1087 tmp = sip_uri_from_name(uri);
1088 buddy_name = g_ascii_strdown(tmp, -1);
1089 g_free(tmp);
1091 /* assign to group Other Contacts if nothing else received */
1092 tmp = g_strdup(sipe_xml_attribute(item, "groups"));
1093 if(is_empty(tmp)) {
1094 struct sipe_group *group = sipe_group_find_by_name(sipe_private, _("Other Contacts"));
1095 g_free(tmp);
1096 tmp = group ? g_strdup_printf("%d", group->id) : g_strdup("1");
1098 item_groups = g_strsplit(tmp, " ", 0);
1099 g_free(tmp);
1101 while (item_groups[i]) {
1102 struct sipe_group *group = sipe_group_find_by_id(sipe_private, g_ascii_strtod(item_groups[i], NULL));
1104 // If couldn't find the right group for this contact, just put them in the first group we have
1105 if (group == NULL && g_slist_length(sipe_private->groups) > 0) {
1106 group = sipe_private->groups->data;
1109 if (group != NULL) {
1110 gchar *b_alias;
1111 sipe_backend_buddy b = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, buddy_name, group->name);
1112 if (!b){
1113 b = sipe_backend_buddy_add(SIPE_CORE_PUBLIC, buddy_name, uri, group->name);
1114 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name, uri);
1117 b_alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, b);
1118 if (sipe_strcase_equal(uri, b_alias)) {
1119 if (name != NULL && strlen(name) != 0) {
1120 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, b, name);
1122 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name, name);
1125 g_free(b_alias);
1127 if (!buddy) {
1128 buddy = g_new0(struct sipe_buddy, 1);
1129 buddy->name = sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC, b);
1130 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
1132 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy->name);
1135 buddy->groups = slist_insert_unique_sorted(buddy->groups, group, (GCompareFunc)sipe_group_compare);
1137 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy->name, group->name);
1138 } else {
1139 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1140 name);
1143 i++;
1144 } // while, contact groups
1145 g_strfreev(item_groups);
1146 g_free(buddy_name);
1148 } // for, contacts
1150 sipe_cleanup_local_blist(sipe_private);
1152 /* Add self-contact if not there yet. 2005 systems. */
1153 /* This will resemble subscription to roaming_self in 2007 systems */
1154 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1155 gchar *self_uri = sip_uri_self(sipe_private);
1156 struct sipe_buddy *buddy = g_hash_table_lookup(sipe_private->buddies, self_uri);
1158 if (!buddy) {
1159 buddy = g_new0(struct sipe_buddy, 1);
1160 buddy->name = g_strdup(self_uri);
1161 g_hash_table_insert(sipe_private->buddies, buddy->name, buddy);
1163 g_free(self_uri);
1166 sipe_xml_free(isc);
1168 /* subscribe to buddies */
1169 if (!sip->subscribed_buddies) { //do it once, then count Expire field to schedule resubscribe.
1170 if (sip->batched_support) {
1171 sipe_subscribe_presence_batched(sipe_private);
1172 } else {
1173 g_hash_table_foreach(sipe_private->buddies,
1174 (GHFunc)sipe_buddy_subscribe_cb,
1175 sipe_private);
1177 sip->subscribed_buddies = TRUE;
1179 /* for 2005 systems schedule contacts' status update
1180 * based on their calendar information
1182 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1183 sipe_ocs2005_schedule_status_update(sipe_private, time(NULL));
1186 return 0;
1189 static void sipe_process_roaming_acl(struct sipe_core_private *sipe_private,
1190 struct sipmsg *msg)
1192 guint delta;
1193 sipe_xml *xml;
1195 xml = sipe_xml_parse(msg->body, msg->bodylen);
1196 if (!xml)
1197 return;
1199 /* [MS-SIP]: deltaNum MUST be non-zero */
1200 delta = sipe_xml_int_attribute(xml, "deltaNum", 0);
1201 if (delta) {
1202 sipe_private->deltanum_acl = delta;
1205 sipe_xml_free(xml);
1208 struct sipe_auth_job {
1209 gchar *who;
1210 struct sipe_core_private *sipe_private;
1213 void sipe_core_contact_allow_deny(struct sipe_core_public *sipe_public,
1214 const gchar* who,
1215 gboolean allow)
1217 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1219 if (allow) {
1220 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: authorizing contact %s", who);
1221 } else {
1222 SIPE_DEBUG_INFO("sipe_core_contact_allow_deny: blocking contact %s", who);
1225 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1226 sipe_ocs2007_change_access_level(sipe_private,
1227 (allow ? -1 : 32000),
1228 "user",
1229 sipe_get_no_sip_uri(who));
1230 } else {
1231 sip_soap_ocs2005_setacl(sipe_private, who, allow);
1236 static void sipe_auth_user_cb(gpointer data)
1238 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1239 if (!job) return;
1241 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1242 job->who,
1243 TRUE);
1244 g_free(job);
1247 static void sipe_deny_user_cb(gpointer data)
1249 struct sipe_auth_job *job = (struct sipe_auth_job *) data;
1250 if (!job) return;
1252 sipe_core_contact_allow_deny((struct sipe_core_public *)job->sipe_private,
1253 job->who,
1254 FALSE);
1255 g_free(job);
1258 /* OCS2005- */
1259 static void sipe_process_presence_wpending (struct sipe_core_private *sipe_private,
1260 struct sipmsg * msg)
1262 sipe_xml *watchers;
1263 const sipe_xml *watcher;
1264 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1265 if (msg->response != 0 && msg->response != 200) return;
1267 if (msg->bodylen == 0 || msg->body == NULL || sipe_strequal(sipmsg_find_header(msg, "Event"), "msrtc.wpending")) return;
1269 watchers = sipe_xml_parse(msg->body, msg->bodylen);
1270 if (!watchers) return;
1272 for (watcher = sipe_xml_child(watchers, "watcher"); watcher; watcher = sipe_xml_twin(watcher)) {
1273 gchar * remote_user = g_strdup(sipe_xml_attribute(watcher, "uri"));
1274 gchar * alias = g_strdup(sipe_xml_attribute(watcher, "displayName"));
1275 gboolean on_list = g_hash_table_lookup(sipe_private->buddies, remote_user) != NULL;
1277 // TODO pull out optional displayName to pass as alias
1278 if (remote_user) {
1279 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1280 job->who = remote_user;
1281 job->sipe_private = sipe_private;
1282 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC,
1283 remote_user,
1284 alias,
1285 on_list,
1286 sipe_auth_user_cb,
1287 sipe_deny_user_cb,
1288 (gpointer)job);
1293 sipe_xml_free(watchers);
1294 return;
1297 static void sipe_presence_timeout_mime_cb(gpointer user_data,
1298 SIPE_UNUSED_PARAMETER const GSList *fields,
1299 const gchar *body,
1300 gsize length)
1302 GSList **buddies = user_data;
1303 sipe_xml *xml = sipe_xml_parse(body, length);
1305 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
1306 const gchar *uri = sipe_xml_attribute(xml, "uri");
1307 const sipe_xml *xn_category;
1310 * automaton: presence is never expected to change
1312 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
1314 for (xn_category = sipe_xml_child(xml, "category");
1315 xn_category;
1316 xn_category = sipe_xml_twin(xn_category)) {
1317 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
1318 "contactCard")) {
1319 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
1320 if (node) {
1321 char *boolean = sipe_xml_data(node);
1322 if (sipe_strequal(boolean, "true")) {
1323 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
1324 uri);
1325 uri = NULL;
1327 g_free(boolean);
1329 break;
1333 if (uri) {
1334 *buddies = g_slist_append(*buddies, sip_uri(uri));
1338 sipe_xml_free(xml);
1341 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
1342 struct sipmsg *msg,
1343 const gchar *who,
1344 int timeout)
1346 const char *ctype = sipmsg_find_header(msg, "Content-Type");
1347 gchar *action_name = sipe_utils_presence_key(who);
1349 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
1351 if (ctype &&
1352 strstr(ctype, "multipart") &&
1353 (strstr(ctype, "application/rlmi+xml") ||
1354 strstr(ctype, "application/msrtc-event-categories+xml"))) {
1355 GSList *buddies = NULL;
1357 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
1359 if (buddies)
1360 sipe_subscribe_presence_batched_schedule(sipe_private,
1361 action_name,
1362 who,
1363 buddies,
1364 timeout);
1366 } else {
1367 sipe_schedule_seconds(sipe_private,
1368 action_name,
1369 g_strdup(who),
1370 timeout,
1371 sipe_subscribe_presence_single,
1372 g_free);
1373 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who, timeout);
1375 g_free(action_name);
1379 * Dispatcher for all incoming subscription information
1380 * whether it comes from NOTIFY, BENOTIFY requests or
1381 * piggy-backed to subscription's OK responce.
1383 * @param request whether initiated from BE/NOTIFY request or OK-response message.
1384 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
1386 void process_incoming_notify(struct sipe_core_private *sipe_private,
1387 struct sipmsg *msg,
1388 gboolean request, gboolean benotify)
1390 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1391 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
1392 const gchar *event = sipmsg_find_header(msg, "Event");
1393 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
1395 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state ? subscription_state : "");
1397 /* implicit subscriptions */
1398 if (content_type && g_str_has_prefix(content_type, "application/ms-imdn+xml")) {
1399 sipe_process_imdn(sipe_private, msg);
1402 if (event) {
1403 /* for one off subscriptions (send with Expire: 0) */
1404 if (sipe_strcase_equal(event, "vnd-microsoft-provisioning-v2"))
1406 sipe_process_provisioning_v2(sipe_private, msg);
1408 else if (sipe_strcase_equal(event, "vnd-microsoft-provisioning"))
1410 sipe_process_provisioning(sipe_private, msg);
1412 else if (sipe_strcase_equal(event, "presence"))
1414 sipe_process_presence(sipe_private, msg);
1416 else if (sipe_strcase_equal(event, "registration-notify"))
1418 sipe_process_registration_notify(sipe_private, msg);
1421 if (!subscription_state || strstr(subscription_state, "active"))
1423 if (sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts"))
1425 sipe_process_roaming_contacts(sipe_private, msg);
1427 else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-self"))
1429 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1431 else if (sipe_strcase_equal(event, "vnd-microsoft-roaming-ACL"))
1433 sipe_process_roaming_acl(sipe_private, msg);
1435 else if (sipe_strcase_equal(event, "presence.wpending"))
1437 sipe_process_presence_wpending(sipe_private, msg);
1439 else if (sipe_strcase_equal(event, "conference"))
1441 sipe_process_conference(sipe_private, msg);
1446 /* The server sends status 'terminated' */
1447 if (subscription_state && strstr(subscription_state, "terminated") ) {
1448 gchar *who = parse_from(sipmsg_find_header(msg, request ? "From" : "To"));
1449 gchar *key = sipe_utils_subscription_key(event, who);
1451 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who);
1452 g_free(who);
1454 sipe_subscriptions_remove(sipe_private, key);
1455 g_free(key);
1458 if (!request && event) {
1459 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
1460 int timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
1461 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout);
1463 if (timeout) {
1464 /* 2 min ahead of expiration */
1465 timeout = (timeout - 120) > 120 ? (timeout - 120) : timeout;
1467 if (sipe_strcase_equal(event, "presence.wpending") &&
1468 g_slist_find_custom(sip->allow_events, "presence.wpending", (GCompareFunc)g_ascii_strcasecmp))
1470 gchar *action_name = g_strdup_printf("<%s>", "presence.wpending");
1471 sipe_schedule_seconds(sipe_private,
1472 action_name,
1473 NULL,
1474 timeout,
1475 sipe_subscribe_presence_wpending,
1476 NULL);
1477 g_free(action_name);
1479 else if (sipe_strcase_equal(event, "presence") &&
1480 g_slist_find_custom(sip->allow_events, "presence", (GCompareFunc)g_ascii_strcasecmp))
1482 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
1483 gchar *action_name = sipe_utils_presence_key(who);
1485 if (sip->batched_support) {
1486 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
1488 else {
1489 sipe_schedule_seconds(sipe_private,
1490 action_name,
1491 g_strdup(who),
1492 timeout,
1493 sipe_subscribe_presence_single,
1494 g_free);
1495 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who, timeout);
1497 g_free(action_name);
1498 g_free(who);
1503 /* The client responses on received a NOTIFY message */
1504 if (request && !benotify)
1506 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1511 Local Variables:
1512 mode: c
1513 c-file-style: "bsd"
1514 indent-tabs-mode: t
1515 tab-width: 8
1516 End: