i18n: update bug tracker URL in script
[siplcs.git] / src / core / sipe-ocs2007.c
blob1568e6965a92c3c382bd2dbdf6ce6e8998851e9d
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * OCS2007+ specific code
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
36 #include <glib.h>
38 #include "sipe-common.h"
39 #include "sipmsg.h"
40 #include "sip-csta.h"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-buddy.h"
44 #include "sipe-cal.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
47 #include "sipe-ews.h"
48 #include "sipe-groupchat.h"
49 #include "sipe-media.h"
50 #include "sipe-nls.h"
51 #include "sipe-ocs2007.h"
52 #include "sipe-schedule.h"
53 #include "sipe-status.h"
54 #include "sipe-utils.h"
55 #include "sipe-xml.h"
57 /** MS-PRES publication */
58 struct sipe_publication {
59 gchar *category;
60 guint instance;
61 guint container;
62 guint version;
63 /** for 'state' category */
64 int availability;
65 /** for 'state:calendarState' category */
66 char *cal_event_hash;
67 /** for 'note' category */
68 gchar *note;
69 /** for 'calendarData' category; 300(Team) container */
70 char *working_hours_xml_str;
71 char *fb_start_str;
72 char *free_busy_base64;
75 /**
76 * 2007-style Activity and Availability.
78 * [MS-PRES] 3.7.5.5
80 * Conversion of legacyInterop availability ranges and activity tokens into
81 * SIPE activity tokens. The descriptions of availability ranges are defined at:
83 * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
85 * The values define the starting point of a range.
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000
94 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
96 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
97 const gchar *activity)
99 guint type;
101 if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
102 type = SIPE_ACTIVITY_OFFLINE;
103 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
104 type = SIPE_ACTIVITY_AVAILABLE;
105 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
106 type = SIPE_ACTIVITY_INACTIVE;
107 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
108 type = sipe_status_token_to_activity(activity);
109 if ((type != SIPE_ACTIVITY_ON_PHONE) &&
110 (type != SIPE_ACTIVITY_IN_CONF))
111 type = SIPE_ACTIVITY_BUSY;
112 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
113 type = SIPE_ACTIVITY_BUSYIDLE;
114 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
115 type = SIPE_ACTIVITY_DND;
116 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
117 type = SIPE_ACTIVITY_BRB;
118 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
119 type = SIPE_ACTIVITY_AWAY;
120 } else {
121 type = SIPE_ACTIVITY_OFFLINE;
124 return sipe_status_activity_to_token(type);
127 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
129 if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
130 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
131 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
132 } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
133 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
134 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
135 } else {
136 return(NULL);
141 * @param sipe_status_id (in)
142 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
143 * requests this token]
145 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
146 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
147 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
148 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
149 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
150 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
151 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
152 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
153 const gchar **activity_token)
155 guint availability;
156 guint activity;
158 if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
159 availability = SIPE_OCS2007_AVAILABILITY_AWAY;
160 activity = SIPE_ACTIVITY_AWAY;
161 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
162 availability = SIPE_OCS2007_AVAILABILITY_BRB;
163 activity = SIPE_ACTIVITY_BRB;
164 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
165 availability = SIPE_OCS2007_AVAILABILITY_DND;
166 activity = SIPE_ACTIVITY_DND;
167 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
168 availability = SIPE_OCS2007_AVAILABILITY_BUSY;
169 activity = SIPE_ACTIVITY_BUSY;
170 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
171 availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
172 activity = SIPE_ACTIVITY_ONLINE;
173 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
174 availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
175 activity = SIPE_ACTIVITY_UNSET;
176 } else {
177 /* Offline or invisible */
178 availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
179 activity = SIPE_ACTIVITY_OFFLINE;
182 if (activity_token) {
183 *activity_token = sipe_status_activity_to_token(activity);
186 return(availability);
189 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
191 return(SIPE_OCS2007_AVAILABILITY_BUSY >=
192 sipe_ocs2007_availability_from_status(status_id, NULL));
196 gboolean sipe_ocs2007_availability_is_away(guint availability)
198 return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
201 static void send_presence_publish(struct sipe_core_private *sipe_private,
202 const char *publications);
204 static void free_publication(struct sipe_publication *publication)
206 g_free(publication->category);
207 g_free(publication->cal_event_hash);
208 g_free(publication->note);
210 g_free(publication->working_hours_xml_str);
211 g_free(publication->fb_start_str);
212 g_free(publication->free_busy_base64);
214 g_free(publication);
217 struct hash_table_delete_payload {
218 GHashTable *hash_table;
219 guint container;
222 static void sipe_remove_category_container_publications_cb(const gchar *name,
223 struct sipe_publication *publication,
224 struct hash_table_delete_payload *payload)
226 if (publication->container == payload->container) {
227 g_hash_table_remove(payload->hash_table, name);
231 static void sipe_remove_category_container_publications(GHashTable *our_publications,
232 const gchar *category,
233 guint container)
235 struct hash_table_delete_payload payload;
236 payload.hash_table = g_hash_table_lookup(our_publications, category);
238 if (!payload.hash_table) return;
240 payload.container = container;
241 g_hash_table_foreach(payload.hash_table,
242 (GHFunc)sipe_remove_category_container_publications_cb,
243 &payload);
246 /** MS-PRES container */
247 struct sipe_container {
248 guint id;
249 guint version;
250 GSList *members;
253 /** MS-PRES container member */
254 struct sipe_container_member {
255 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
256 gchar *type;
257 gchar *value;
260 static const guint containers[] = {32000, 400, 300, 200, 100};
261 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
263 static void free_container_member(struct sipe_container_member *member)
265 if (!member) return;
267 g_free(member->type);
268 g_free(member->value);
269 g_free(member);
272 static void sipe_ocs2007_free_container(struct sipe_container *container)
274 GSList *entry;
276 if (!container) return;
278 entry = container->members;
279 while (entry) {
280 void *data = entry->data;
281 entry = g_slist_remove(entry, data);
282 free_container_member((struct sipe_container_member *)data);
284 g_free(container);
287 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
289 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
290 GSList *entry = sipe_private->blist_menu_containers;
291 while (entry) {
292 sipe_ocs2007_free_container(entry->data);
293 entry = entry->next;
295 g_slist_free(sipe_private->blist_menu_containers);
296 sipe_private->blist_menu_containers = NULL;
299 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
300 struct sipe_container *container)
302 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
303 container);
306 static struct sipe_container *create_container(guint index,
307 const gchar *member_type,
308 const gchar *member_value,
309 gboolean is_group)
311 struct sipe_container *container = g_new0(struct sipe_container, 1);
312 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
314 container->id = is_group ? (guint) -1 : containers[index];
315 container->members = g_slist_append(container->members, member);
316 member->type = g_strdup(member_type);
317 member->value = g_strdup(member_value);
319 return(container);
322 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
324 if (sipe_private->containers) {
325 GSList *entry = sipe_private->containers;
326 while (entry) {
327 sipe_ocs2007_free_container((struct sipe_container *)entry->data);
328 entry = entry->next;
331 g_slist_free(sipe_private->containers);
335 * Finds locally stored MS-PRES container member
337 static struct sipe_container_member *
338 sipe_find_container_member(struct sipe_container *container,
339 const gchar *type,
340 const gchar *value)
342 struct sipe_container_member *member;
343 GSList *entry;
345 if (container == NULL || type == NULL) {
346 return NULL;
349 entry = container->members;
350 while (entry) {
351 member = entry->data;
352 if (sipe_strcase_equal(member->type, type) &&
353 sipe_strcase_equal(member->value, value))
355 return member;
357 entry = entry->next;
359 return NULL;
363 * Finds locally stored MS-PRES container by id
365 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
366 guint id)
368 GSList *entry = sipe_private->containers;
369 while (entry) {
370 struct sipe_container *container = entry->data;
371 if (id == container->id) {
372 return container;
374 entry = entry->next;
376 return NULL;
379 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
380 const gchar *type,
381 const gchar *value)
383 unsigned int i = 0;
384 const gchar *value_mod = value;
386 if (!type) return -1;
388 if (sipe_strequal("user", type)) {
389 value_mod = sipe_get_no_sip_uri(value);
392 for (i = 0; i < CONTAINERS_LEN; i++) {
393 struct sipe_container_member *member;
394 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
395 if (!container) continue;
397 member = sipe_find_container_member(container, type, value_mod);
398 if (member) return containers[i];
401 return -1;
405 * Returns pointer to domain part in provided Email URL
407 * @param email an email URL. Example: first.last@hq.company.com
408 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
410 * Doesn't allocate memory
412 static const gchar *sipe_get_domain(const gchar *email)
414 gchar *tmp;
416 if (!email) return NULL;
418 tmp = strstr(email, "@");
420 if (tmp && ((tmp+1) < (email + strlen(email)))) {
421 return tmp+1;
422 } else {
423 return NULL;
427 /* @TODO: replace with binary search for faster access? */
428 /** source: http://support.microsoft.com/kb/897567 */
429 static const gchar * const public_domains[] = {
430 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
431 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
432 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
433 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
434 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
435 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
436 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
437 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
438 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
439 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
440 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
441 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
442 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
443 "yahoo.com",
444 NULL};
446 static gboolean sipe_is_public_domain(const gchar *domain)
448 int i = 0;
449 while (public_domains[i]) {
450 if (sipe_strcase_equal(public_domains[i], domain)) {
451 return TRUE;
453 i++;
455 return FALSE;
459 * Access Levels
460 * 32000 - Blocked
461 * 400 - Personal
462 * 300 - Team
463 * 200 - Company
464 * 100 - Public
466 const gchar *sipe_ocs2007_access_level_name(guint id)
468 switch (id) {
469 case 32000: return _("Blocked");
470 case 400: return _("Personal");
471 case 300: return _("Team");
472 case 200: return _("Company");
473 case 100: return _("Public");
475 return _("Unknown");
478 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
479 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
480 const gchar *type,
481 const gchar *value,
482 gboolean *is_group_access)
484 int container_id = -1;
486 if (sipe_strequal("user", type)) {
487 const char *domain;
488 const char *no_sip_uri = sipe_get_no_sip_uri(value);
490 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
491 if (container_id >= 0) {
492 if (is_group_access) *is_group_access = FALSE;
493 return container_id;
496 domain = sipe_get_domain(no_sip_uri);
497 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
498 if (container_id >= 0) {
499 if (is_group_access) *is_group_access = TRUE;
500 return container_id;
503 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
504 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
505 if (is_group_access) *is_group_access = TRUE;
506 return container_id;
509 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
510 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
511 if (is_group_access) *is_group_access = TRUE;
512 return container_id;
515 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
516 if ((container_id >= 0)) {
517 if (is_group_access) *is_group_access = TRUE;
518 return container_id;
520 } else {
521 container_id = sipe_find_member_access_level(sipe_private, type, value);
522 if (is_group_access) *is_group_access = FALSE;
525 return container_id;
528 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
530 struct sipe_container *container;
531 struct sipe_container_member *member;
532 GSList *entry;
533 GSList *entry2;
534 GSList *res = NULL;
536 entry = sipe_private->containers;
537 while (entry) {
538 container = entry->data;
540 entry2 = container->members;
541 while (entry2) {
542 member = entry2->data;
543 if (sipe_strcase_equal(member->type, "domain"))
545 res = slist_insert_unique_sorted(res, g_strdup(member->value), (GCompareFunc)g_ascii_strcasecmp);
547 entry2 = entry2->next;
549 entry = entry->next;
551 return res;
554 static void sipe_send_container_members_prepare(const guint container_id,
555 const guint container_version,
556 const gchar *action,
557 const gchar *type,
558 const gchar *value,
559 char **container_xmls)
561 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
562 gchar *body;
564 if (!container_xmls) return;
566 body = g_strdup_printf(
567 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
568 container_id,
569 container_version,
570 action,
571 type,
572 value_str);
573 g_free(value_str);
575 if ((*container_xmls) == NULL) {
576 *container_xmls = body;
577 } else {
578 char *tmp = *container_xmls;
580 *container_xmls = g_strconcat(*container_xmls, body, NULL);
581 g_free(tmp);
582 g_free(body);
586 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
587 char *container_xmls)
589 gchar *self;
590 gchar *contact;
591 gchar *hdr;
592 gchar *body;
594 if (!container_xmls) return;
596 self = sip_uri_self(sipe_private);
597 body = g_strdup_printf(
598 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
599 "%s"
600 "</setContainerMembers>",
601 container_xmls);
603 contact = get_contact(sipe_private);
604 hdr = g_strdup_printf("Contact: %s\r\n"
605 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
606 g_free(contact);
608 sip_transport_service(sipe_private,
609 self,
610 hdr,
611 body,
612 NULL);
614 g_free(hdr);
615 g_free(body);
616 g_free(self);
620 * @param container_id a new access level. If -1 then current access level
621 * is just removed (I.e. the member is removed from all containers).
622 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
623 * @param value a value for member. E.g. SIP URI for "user" member type.
625 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
626 const int container_id,
627 const gchar *type,
628 const gchar *value)
630 unsigned int i;
631 int current_container_id = -1;
632 char *container_xmls = NULL;
634 /* for each container: find/delete */
635 for (i = 0; i < CONTAINERS_LEN; i++) {
636 struct sipe_container_member *member;
637 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
639 if (!container) continue;
641 member = sipe_find_container_member(container, type, value);
642 if (member) {
643 current_container_id = containers[i];
644 /* delete/publish current access level */
645 if (container_id < 0 || container_id != current_container_id) {
646 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
647 /* remove member from our cache, to be able to recalculate AL below */
648 container->members = g_slist_remove(container->members, member);
649 current_container_id = -1;
654 /* recalculate AL below */
655 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
657 /* assign/publish new access level */
658 if (container_id != current_container_id && container_id >= 0) {
659 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
660 guint version = container ? container->version : 0;
662 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
665 if (container_xmls) {
666 sipe_send_set_container_members(sipe_private, container_xmls);
668 g_free(container_xmls);
671 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
672 gpointer parameter)
674 struct sipe_container *container = parameter;
675 struct sipe_container_member *member;
677 if (!container || !container->members) return;
679 member = ((struct sipe_container_member *)container->members->data);
681 if (!member->type) return;
683 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
684 container->id, member->type, member->value ? member->value : "");
686 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
687 container->id,
688 member->type,
689 member->value);
693 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
694 const gchar *domain,
695 guint index)
697 /* move Blocked first */
698 guint i = (index == 4) ? 0 : index + 1;
699 guint container_id = containers[i];
701 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
702 domain ? domain : "", index, container_id);
704 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
705 container_id,
706 "domain",
707 domain);
711 * Schedules process of self status publish
712 * based on own calendar information.
713 * Should be scheduled to the beginning of every
714 * 15 min interval, like:
715 * 13:00, 13:15, 13:30, 13:45, etc.
718 static void schedule_publish_update(struct sipe_core_private *sipe_private,
719 time_t calculate_from)
721 int interval = 5*60;
722 /** start of the beginning of closest 5 min interval. */
723 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
725 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
726 asctime(localtime(&calculate_from)));
727 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
728 asctime(localtime(&next_start)));
730 sipe_schedule_seconds(sipe_private,
731 "<+2007-cal-status>",
732 NULL,
733 next_start - time(NULL),
734 sipe_ocs2007_presence_publish,
735 NULL);
739 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
740 * @param availability (%d) Ex.: 6500
742 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
743 "<availability>%d</availability>"
745 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
746 * @param token (%s) Ex.: in-a-meeting
747 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
748 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
750 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
751 "<activity token=\"%s\" %s %s></activity>"
753 * Publishes 'calendarState' category.
754 * @param instance (%u) Ex.: 1339299275
755 * @param version (%u) Ex.: 1
756 * @param uri (%s) Ex.: john@contoso.com
757 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
758 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
759 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
760 * @param meeting_subject (%s) Ex.: Customer Meeting
761 * @param meeting_location (%s) Ex.: Conf Room 100
763 * @param instance (%u) Ex.: 1339299275
764 * @param version (%u) Ex.: 1
765 * @param uri (%s) Ex.: john@contoso.com
766 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
767 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
768 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
769 * @param meeting_subject (%s) Ex.: Customer Meeting
770 * @param meeting_location (%s) Ex.: Conf Room 100
772 #define SIPE_PUB_XML_STATE_CALENDAR \
773 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
774 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
775 "%s"\
776 "%s"\
777 "<endpointLocation/>"\
778 "<meetingSubject>%s</meetingSubject>"\
779 "<meetingLocation>%s</meetingLocation>"\
780 "</state>"\
781 "</publication>"\
782 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
783 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
784 "%s"\
785 "%s"\
786 "<endpointLocation/>"\
787 "<meetingSubject>%s</meetingSubject>"\
788 "<meetingLocation>%s</meetingLocation>"\
789 "</state>"\
790 "</publication>"
792 * Publishes to clear 'calendarState' and 'phoneState' category
793 * @param instance (%u) Ex.: 1251210982
794 * @param version (%u) Ex.: 1
796 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
797 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
798 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
801 * Publishes to clear any category
802 * @param category_name (%s) Ex.: state
803 * @param instance (%u) Ex.: 536870912
804 * @param container (%u) Ex.: 3
805 * @param version (%u) Ex.: 1
806 * @param expireType (%s) Ex.: static
808 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
809 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
812 * Publishes 'note' category.
813 * @param instance (%u) Ex.: 2135971629; 0 for personal
814 * @param container (%u) Ex.: 200
815 * @param version (%u) Ex.: 2
816 * @param type (%s) Ex.: personal or OOF
817 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
818 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
819 * @param body (%s) Ex.: In the office
821 #define SIPE_PUB_XML_NOTE \
822 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
823 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
824 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
825 "</note>"\
826 "</publication>"
828 * Publishes 'phoneState' category.
829 * @param instance (%u) Ex.: 1339299275
830 * @param version (%u) Ex.: 1
831 * @param availability (%u) Ex.: 6500
832 * @param token (%s) Ex.: on-the-phone
833 * @param minAvailability (%u) generally same as availability
835 * @param instance (%u) Ex.: 1339299275
836 * @param version (%u) Ex.: 1
837 * @param availability (%u) Ex.: 6500
838 * @param token (%s) Ex.: on-the-phone
839 * @param minAvailability (%u) generally same as availability
841 #define SIPE_PUB_XML_STATE_PHONE \
842 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
843 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
844 "<availability>%u</availability>"\
845 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
846 "</state>"\
847 "</publication>"\
848 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
849 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
850 "<availability>%u</availability>"\
851 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
852 "</state>"\
853 "</publication>"
856 * Only Busy and OOF calendar event are published.
857 * Different instances are used for that.
859 * Must be g_free'd after use.
861 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
862 struct sipe_cal_event *event,
863 const char *uri,
864 int cal_satus)
866 gchar *start_time_str;
867 int availability = 0;
868 gchar *res;
869 gchar *tmp = NULL;
870 guint instance = (cal_satus == SIPE_CAL_OOF) ?
871 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
872 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
874 /* key is <category><instance><container> */
875 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
876 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
877 struct sipe_publication *publication_2 =
878 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
879 struct sipe_publication *publication_3 =
880 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
882 g_free(key_2);
883 g_free(key_3);
885 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
886 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
887 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
888 return NULL;
891 if (event &&
892 publication_3 &&
893 (publication_3->availability == availability) &&
894 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
896 g_free(tmp);
897 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
898 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
899 return NULL; /* nothing to update */
901 g_free(tmp);
903 if (event &&
904 (event->cal_status == SIPE_CAL_BUSY ||
905 event->cal_status == SIPE_CAL_OOF))
907 gchar *availability_xml_str = NULL;
908 gchar *activity_xml_str = NULL;
909 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
910 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
912 if (event->cal_status == SIPE_CAL_BUSY) {
913 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
914 SIPE_OCS2007_AVAILABILITY_BUSY);
917 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
918 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
919 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
920 "minAvailability=\"6500\"",
921 "maxAvailability=\"8999\"");
922 } else if (event->cal_status == SIPE_CAL_OOF) {
923 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
924 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
925 "minAvailability=\"12000\"",
926 "");
928 start_time_str = sipe_utils_time_to_str(event->start_time);
930 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
931 instance,
932 publication_2 ? publication_2->version : 0,
933 uri,
934 start_time_str,
935 availability_xml_str ? availability_xml_str : "",
936 activity_xml_str ? activity_xml_str : "",
937 escaped_subject ? escaped_subject : "",
938 escaped_location ? escaped_location : "",
940 instance,
941 publication_3 ? publication_3->version : 0,
942 uri,
943 start_time_str,
944 availability_xml_str ? availability_xml_str : "",
945 activity_xml_str ? activity_xml_str : "",
946 escaped_subject ? escaped_subject : "",
947 escaped_location ? escaped_location : ""
949 g_free(escaped_location);
950 g_free(escaped_subject);
951 g_free(start_time_str);
952 g_free(availability_xml_str);
953 g_free(activity_xml_str);
956 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
958 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
959 instance,
960 publication_2 ? publication_2->version : 0,
962 instance,
963 publication_3 ? publication_3->version : 0
967 return res;
971 * Returns 'note' XML part for publication.
972 * Must be g_free'd after use.
974 * Protocol format for Note is plain text.
976 * @param note a note in Sipe internal HTML format
977 * @param note_type either personal or OOF
979 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
980 const char *note, /* html */
981 const char *note_type,
982 time_t note_start,
983 time_t note_end)
985 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
986 /* key is <category><instance><container> */
987 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
988 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
989 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
991 struct sipe_publication *publication_note_200 =
992 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_200);
993 struct sipe_publication *publication_note_300 =
994 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_300);
995 struct sipe_publication *publication_note_400 =
996 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_400);
998 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
999 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
1000 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
1001 char *res, *tmp1, *tmp2, *tmp3;
1002 char *start_time_attr;
1003 char *end_time_attr;
1005 g_free(tmp);
1006 tmp = NULL;
1007 g_free(key_note_200);
1008 g_free(key_note_300);
1009 g_free(key_note_400);
1011 /* we even need to republish empty note */
1012 if (sipe_strequal(n1, n2))
1014 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1015 g_free(n1);
1016 return NULL; /* nothing to update */
1019 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1020 g_free(tmp);
1021 tmp = NULL;
1022 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1023 g_free(tmp);
1025 if (n1) {
1026 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1027 instance,
1028 200,
1029 publication_note_200 ? publication_note_200->version : 0,
1030 note_type,
1031 start_time_attr ? start_time_attr : "",
1032 end_time_attr ? end_time_attr : "",
1033 n1);
1035 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1036 instance,
1037 300,
1038 publication_note_300 ? publication_note_300->version : 0,
1039 note_type,
1040 start_time_attr ? start_time_attr : "",
1041 end_time_attr ? end_time_attr : "",
1042 n1);
1044 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1045 instance,
1046 400,
1047 publication_note_400 ? publication_note_400->version : 0,
1048 note_type,
1049 start_time_attr ? start_time_attr : "",
1050 end_time_attr ? end_time_attr : "",
1051 n1);
1052 } else {
1053 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1054 "note",
1055 instance,
1056 200,
1057 publication_note_200 ? publication_note_200->version : 0,
1058 "static");
1059 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1060 "note",
1061 instance,
1062 300,
1063 publication_note_200 ? publication_note_200->version : 0,
1064 "static");
1065 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1066 "note",
1067 instance,
1068 400,
1069 publication_note_200 ? publication_note_200->version : 0,
1070 "static");
1072 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
1074 g_free(start_time_attr);
1075 g_free(end_time_attr);
1076 g_free(tmp1);
1077 g_free(tmp2);
1078 g_free(tmp3);
1079 g_free(n1);
1081 return res;
1085 * Publishes 'calendarData' category's WorkingHours.
1087 * @param version (%u) Ex.: 1
1088 * @param email (%s) Ex.: alice@cosmo.local
1089 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1091 * @param version (%u)
1093 * @param version (%u)
1094 * @param email (%s)
1095 * @param working_hours_xml_str (%s)
1097 * @param version (%u)
1098 * @param email (%s)
1099 * @param working_hours_xml_str (%s)
1101 * @param version (%u)
1102 * @param email (%s)
1103 * @param working_hours_xml_str (%s)
1105 * @param version (%u)
1107 #define SIPE_PUB_XML_WORKING_HOURS \
1108 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1109 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1110 "</calendarData>"\
1111 "</publication>"\
1112 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1113 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1114 "</publication>"\
1115 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1116 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1117 "</calendarData>"\
1118 "</publication>"\
1119 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1120 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1121 "</calendarData>"\
1122 "</publication>"\
1123 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1124 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1125 "</calendarData>"\
1126 "</publication>"\
1127 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1128 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1129 "</publication>"
1132 * Returns 'calendarData' XML part with WorkingHours for publication.
1133 * Must be g_free'd after use.
1135 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1137 struct sipe_calendar* cal = sipe_private->calendar;
1139 /* key is <category><instance><container> */
1140 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1141 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1142 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1143 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1144 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1145 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1147 struct sipe_publication *publication_cal_1 =
1148 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1149 struct sipe_publication *publication_cal_100 =
1150 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1151 struct sipe_publication *publication_cal_200 =
1152 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1153 struct sipe_publication *publication_cal_300 =
1154 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1155 struct sipe_publication *publication_cal_400 =
1156 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1157 struct sipe_publication *publication_cal_32000 =
1158 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1160 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1161 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1163 g_free(key_cal_1);
1164 g_free(key_cal_100);
1165 g_free(key_cal_200);
1166 g_free(key_cal_300);
1167 g_free(key_cal_400);
1168 g_free(key_cal_32000);
1170 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1171 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1172 return NULL;
1175 if (sipe_strequal(n1, n2))
1177 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1178 return NULL; /* nothing to update */
1181 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1182 /* 1 */
1183 publication_cal_1 ? publication_cal_1->version : 0,
1184 cal->email,
1185 cal->working_hours_xml_str,
1186 /* 100 - Public */
1187 publication_cal_100 ? publication_cal_100->version : 0,
1188 /* 200 - Company */
1189 publication_cal_200 ? publication_cal_200->version : 0,
1190 cal->email,
1191 cal->working_hours_xml_str,
1192 /* 300 - Team */
1193 publication_cal_300 ? publication_cal_300->version : 0,
1194 cal->email,
1195 cal->working_hours_xml_str,
1196 /* 400 - Personal */
1197 publication_cal_400 ? publication_cal_400->version : 0,
1198 cal->email,
1199 cal->working_hours_xml_str,
1200 /* 32000 - Blocked */
1201 publication_cal_32000 ? publication_cal_32000->version : 0
1206 * Publishes 'calendarData' category's FreeBusy.
1208 * @param instance (%u) Ex.: 1300372959
1209 * @param version (%u) Ex.: 1
1211 * @param instance (%u) Ex.: 1300372959
1212 * @param version (%u) Ex.: 1
1214 * @param instance (%u) Ex.: 1300372959
1215 * @param version (%u) Ex.: 1
1216 * @param email (%s) Ex.: alice@cosmo.local
1217 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1218 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1220 * @param instance (%u) Ex.: 1300372959
1221 * @param version (%u) Ex.: 1
1222 * @param email (%s) Ex.: alice@cosmo.local
1223 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1224 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1226 * @param instance (%u) Ex.: 1300372959
1227 * @param version (%u) Ex.: 1
1228 * @param email (%s) Ex.: alice@cosmo.local
1229 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1230 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1232 * @param instance (%u) Ex.: 1300372959
1233 * @param version (%u) Ex.: 1
1235 #define SIPE_PUB_XML_FREE_BUSY \
1236 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1237 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1238 "</publication>"\
1239 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1240 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1241 "</publication>"\
1242 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1243 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1244 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1245 "</calendarData>"\
1246 "</publication>"\
1247 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1248 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1249 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1250 "</calendarData>"\
1251 "</publication>"\
1252 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1253 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1254 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1255 "</calendarData>"\
1256 "</publication>"\
1257 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1258 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1259 "</publication>"
1262 * Returns 'calendarData' XML part with FreeBusy for publication.
1263 * Must be g_free'd after use.
1265 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1267 struct sipe_calendar* cal = sipe_private->calendar;
1268 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1269 char *fb_start_str;
1270 char *free_busy_base64;
1271 /* const char *st; */
1272 /* const char *fb; */
1273 char *res;
1275 /* key is <category><instance><container> */
1276 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1277 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1278 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1279 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1280 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1281 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1283 struct sipe_publication *publication_cal_1 =
1284 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1285 struct sipe_publication *publication_cal_100 =
1286 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1287 struct sipe_publication *publication_cal_200 =
1288 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1289 struct sipe_publication *publication_cal_300 =
1290 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1291 struct sipe_publication *publication_cal_400 =
1292 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1293 struct sipe_publication *publication_cal_32000 =
1294 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1296 g_free(key_cal_1);
1297 g_free(key_cal_100);
1298 g_free(key_cal_200);
1299 g_free(key_cal_300);
1300 g_free(key_cal_400);
1301 g_free(key_cal_32000);
1303 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1304 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1305 return NULL;
1308 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1309 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1311 /* we will rebuplish the same data to refresh publication time,
1312 * so if data from multiple sources, most recent will be choosen
1314 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1315 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1317 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1319 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1320 // g_free(fb_start_str);
1321 // g_free(free_busy_base64);
1322 // return NULL; /* nothing to update */
1325 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1326 /* 1 */
1327 cal_data_instance,
1328 publication_cal_1 ? publication_cal_1->version : 0,
1329 /* 100 - Public */
1330 cal_data_instance,
1331 publication_cal_100 ? publication_cal_100->version : 0,
1332 /* 200 - Company */
1333 cal_data_instance,
1334 publication_cal_200 ? publication_cal_200->version : 0,
1335 cal->email,
1336 fb_start_str,
1337 free_busy_base64,
1338 /* 300 - Team */
1339 cal_data_instance,
1340 publication_cal_300 ? publication_cal_300->version : 0,
1341 cal->email,
1342 fb_start_str,
1343 free_busy_base64,
1344 /* 400 - Personal */
1345 cal_data_instance,
1346 publication_cal_400 ? publication_cal_400->version : 0,
1347 cal->email,
1348 fb_start_str,
1349 free_busy_base64,
1350 /* 32000 - Blocked */
1351 cal_data_instance,
1352 publication_cal_32000 ? publication_cal_32000->version : 0
1355 g_free(fb_start_str);
1356 g_free(free_busy_base64);
1357 return res;
1362 * Publishes 'device' category.
1363 * @param instance (%u) Ex.: 1938468728
1364 * @param version (%u) Ex.: 1
1365 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1366 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1367 * @param timezone (%s) Ex.: 00:00:00+01:00
1368 * @param machineName (%s) Ex.: BOSTON-OCS07
1370 #define SIPE_PUB_XML_DEVICE \
1371 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1372 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1373 "<capabilities preferred=\"false\" uri=\"%s\">"\
1374 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1375 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1376 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1377 "</capabilities>"\
1378 "<timezone>%s</timezone>"\
1379 "<machineName>%s</machineName>"\
1380 "</device>"\
1381 "</publication>"
1384 * Returns 'device' XML part for publication.
1385 * Must be g_free'd after use.
1387 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1389 gchar *uri;
1390 gchar *doc;
1391 gchar *uuid = get_uuid(sipe_private);
1392 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1393 /* key is <category><instance><container> */
1394 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1395 struct sipe_publication *publication =
1396 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "device"), key);
1398 g_free(key);
1400 uri = sip_uri_self(sipe_private);
1401 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1402 device_instance,
1403 publication ? publication->version : 0,
1404 uuid,
1405 uri,
1406 "00:00:00+01:00", /* @TODO make timezone real*/
1407 g_get_host_name()
1410 g_free(uri);
1411 g_free(uuid);
1413 return doc;
1417 * Publishes 'machineState' category.
1418 * @param instance (%u) Ex.: 926460663
1419 * @param version (%u) Ex.: 22
1420 * @param availability (%d) Ex.: 3500
1421 * @param instance (%u) Ex.: 926460663
1422 * @param version (%u) Ex.: 22
1423 * @param availability (%d) Ex.: 3500
1425 #define SIPE_PUB_XML_STATE_MACHINE \
1426 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1427 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1428 "<availability>%d</availability>"\
1429 "<endpointLocation/>"\
1430 "</state>"\
1431 "</publication>"\
1432 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1433 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1434 "<availability>%d</availability>"\
1435 "<endpointLocation/>"\
1436 "</state>"\
1437 "</publication>"
1440 * Publishes 'userState' category.
1441 * @param instance (%u) User. Ex.: 536870912
1442 * @param version (%u) User Container 2. Ex.: 22
1443 * @param availability (%d) User Container 2. Ex.: 15500
1444 * @param instance (%u) User. Ex.: 536870912
1445 * @param version (%u) User Container 3.Ex.: 22
1446 * @param availability (%d) User Container 3. Ex.: 15500
1448 #define SIPE_PUB_XML_STATE_USER \
1449 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1450 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1451 "<availability>%d</availability>"\
1452 "<endpointLocation/>"\
1453 "</state>"\
1454 "</publication>"\
1455 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1456 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1457 "<availability>%d</availability>"\
1458 "<endpointLocation/>"\
1459 "</state>"\
1460 "</publication>"
1463 * A service method - use
1464 * - send_publish_get_category_state_machine and
1465 * - send_publish_get_category_state_user instead.
1466 * Must be g_free'd after use.
1468 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1469 gboolean is_user_state)
1471 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1472 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1473 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1474 /* key is <category><instance><container> */
1475 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1476 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1477 struct sipe_publication *publication_2 =
1478 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1479 struct sipe_publication *publication_3 =
1480 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1482 g_free(key_2);
1483 g_free(key_3);
1485 if (publication_2 && (publication_2->availability == availability))
1487 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1488 return NULL; /* nothing to update */
1491 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1492 instance,
1493 publication_2 ? publication_2->version : 0,
1494 availability,
1495 instance,
1496 publication_3 ? publication_3->version : 0,
1497 availability);
1501 * Returns 'machineState' XML part for publication.
1502 * Must be g_free'd after use.
1504 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
1506 return sipe_publish_get_category_state(sipe_private, FALSE);
1510 * Returns 'userState' XML part for publication.
1511 * Must be g_free'd after use.
1513 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
1515 return sipe_publish_get_category_state(sipe_private, TRUE);
1518 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1520 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1521 gchar *pub_machine;
1522 gchar *publications;
1524 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1526 pub_machine = sipe_publish_get_category_state_machine(sipe_private);
1527 publications = g_strdup_printf("%s%s",
1528 pub_device,
1529 pub_machine ? pub_machine : "");
1530 g_free(pub_device);
1531 g_free(pub_machine);
1533 send_presence_publish(sipe_private, publications);
1534 g_free(publications);
1537 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1538 struct sipmsg *msg,
1539 struct transaction *trans)
1541 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1543 if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1544 sipe_xml *xml;
1545 const sipe_xml *node;
1546 gchar *fault_code;
1547 GHashTable *faults;
1548 int index_our;
1549 gboolean has_device_publication = FALSE;
1551 xml = sipe_xml_parse(msg->body, msg->bodylen);
1553 /* test if version mismatch fault */
1554 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1555 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1556 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1557 g_free(fault_code);
1558 sipe_xml_free(xml);
1559 return TRUE;
1561 g_free(fault_code);
1563 /* accumulating information about faulty versions */
1564 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1565 for (node = sipe_xml_child(xml, "details/operation");
1566 node;
1567 node = sipe_xml_twin(node))
1569 const gchar *index = sipe_xml_attribute(node, "index");
1570 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1572 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1573 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1575 sipe_xml_free(xml);
1577 /* here we are parsing our own request to figure out what publication
1578 * referenced here only by index went wrong
1580 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1582 /* publication */
1583 for (node = sipe_xml_child(xml, "publications/publication"),
1584 index_our = 1; /* starts with 1 - our first publication */
1585 node;
1586 node = sipe_xml_twin(node), index_our++)
1588 gchar *idx = g_strdup_printf("%d", index_our);
1589 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1590 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1591 g_free(idx);
1593 if (sipe_strequal("device", categoryName)) {
1594 has_device_publication = TRUE;
1597 if (curVersion) { /* fault exist on this index */
1598 const gchar *container = sipe_xml_attribute(node, "container");
1599 const gchar *instance = sipe_xml_attribute(node, "instance");
1600 /* key is <category><instance><container> */
1601 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1602 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1604 if (category) {
1605 struct sipe_publication *publication =
1606 g_hash_table_lookup(category, key);
1608 SIPE_DEBUG_INFO("key is %s", key);
1610 if (publication) {
1611 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1612 key, curVersion, publication->version);
1613 /* updating publication's version to the correct one */
1614 publication->version = atoi(curVersion);
1616 } else {
1617 /* We somehow lost this category from our publications... */
1618 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1619 publication->category = g_strdup(categoryName);
1620 publication->instance = atoi(instance);
1621 publication->container = atoi(container);
1622 publication->version = atoi(curVersion);
1623 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1624 g_free, (GDestroyNotify)free_publication);
1625 g_hash_table_insert(category, g_strdup(key), publication);
1626 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1627 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1629 g_free(key);
1632 sipe_xml_free(xml);
1633 g_hash_table_destroy(faults);
1635 /* rebublishing with right versions */
1636 if (has_device_publication) {
1637 send_publish_category_initial(sipe_private);
1638 } else {
1639 sipe_status_update(sipe_private, NULL);
1642 return TRUE;
1646 * Publishes categories.
1647 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1648 * @param publications (%s) XML publications
1650 #define SIPE_SEND_PRESENCE \
1651 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1652 "<publications uri=\"%s\">"\
1653 "%s"\
1654 "</publications>"\
1655 "</publish>"
1657 static void send_presence_publish(struct sipe_core_private *sipe_private,
1658 const char *publications)
1660 gchar *uri;
1661 gchar *doc;
1662 gchar *tmp;
1663 gchar *hdr;
1665 uri = sip_uri_self(sipe_private);
1666 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1667 uri,
1668 publications);
1670 tmp = get_contact(sipe_private);
1671 hdr = g_strdup_printf("Contact: %s\r\n"
1672 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1674 sip_transport_service(sipe_private,
1675 uri,
1676 hdr,
1677 doc,
1678 process_send_presence_category_publish_response);
1680 g_free(tmp);
1681 g_free(hdr);
1682 g_free(uri);
1683 g_free(doc);
1687 * Publishes self status
1688 * based on own calendar information.
1690 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1691 SIPE_UNUSED_PARAMETER void *unused)
1693 struct sipe_calendar* cal = sipe_private->calendar;
1694 struct sipe_cal_event* event = NULL;
1695 gchar *pub_cal_working_hours = NULL;
1696 gchar *pub_cal_free_busy = NULL;
1697 gchar *pub_calendar = NULL;
1698 gchar *pub_calendar2 = NULL;
1699 gchar *pub_oof_note = NULL;
1700 const gchar *oof_note;
1701 time_t oof_start = 0;
1702 time_t oof_end = 0;
1704 if (!cal) {
1705 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1706 return;
1709 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1710 if (cal->cal_events) {
1711 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1714 if (!event) {
1715 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1716 } else {
1717 char *desc = sipe_cal_event_describe(event);
1718 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
1719 g_free(desc);
1722 /* Logic
1723 if OOF
1724 OOF publish, Busy clean
1725 ilse if Busy
1726 OOF clean, Busy publish
1727 else
1728 OOF clean, Busy clean
1730 if (event && event->cal_status == SIPE_CAL_OOF) {
1731 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1732 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1733 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1734 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1735 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1736 } else {
1737 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1738 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1741 oof_note = sipe_ews_get_oof_note(cal);
1742 if (sipe_strequal("Scheduled", cal->oof_state)) {
1743 oof_start = cal->oof_start;
1744 oof_end = cal->oof_end;
1746 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
1748 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1749 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1751 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1752 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1753 } else {
1754 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1755 pub_cal_working_hours ? pub_cal_working_hours : "",
1756 pub_cal_free_busy ? pub_cal_free_busy : "",
1757 pub_calendar ? pub_calendar : "",
1758 pub_calendar2 ? pub_calendar2 : "",
1759 pub_oof_note ? pub_oof_note : "");
1761 send_presence_publish(sipe_private, publications);
1762 g_free(publications);
1765 g_free(pub_cal_working_hours);
1766 g_free(pub_cal_free_busy);
1767 g_free(pub_calendar);
1768 g_free(pub_calendar2);
1769 g_free(pub_oof_note);
1771 /* repeat scheduling */
1772 schedule_publish_update(sipe_private, time(NULL));
1775 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private)
1777 gchar *pub_state = sipe_status_changed_by_user(sipe_private) ?
1778 sipe_publish_get_category_state_user(sipe_private) :
1779 sipe_publish_get_category_state_machine(sipe_private);
1780 gchar *pub_note = sipe_publish_get_category_note(sipe_private,
1781 sipe_private->note,
1782 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1785 gchar *publications;
1787 if (!pub_state && !pub_note) {
1788 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1789 return;
1792 publications = g_strdup_printf("%s%s",
1793 pub_state ? pub_state : "",
1794 pub_note ? pub_note : "");
1796 g_free(pub_state);
1797 g_free(pub_note);
1799 send_presence_publish(sipe_private, publications);
1800 g_free(publications);
1803 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1805 gchar *publications = NULL;
1806 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1808 /* key is <category><instance><container> */
1809 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1810 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1811 struct sipe_publication *publication_2 =
1812 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1813 struct sipe_publication *publication_3 =
1814 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1815 g_free(key_2);
1816 g_free(key_3);
1818 #ifdef HAVE_VV
1819 if (sipe_private->media_call) {
1820 guint availability;
1821 const gchar *token;
1822 if (sipe_media_is_conference_call(sipe_private->media_call)) {
1823 availability = 7000;
1824 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1825 } else {
1826 availability = 6500;
1827 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1830 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1831 instance, publication_2 ? publication_2->version : 0,
1832 availability, token, availability,
1833 instance, publication_3 ? publication_3->version : 0,
1834 availability, token, availability);
1835 } else
1836 #endif
1838 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1839 instance, publication_2 ? publication_2->version : 0,
1840 instance, publication_3 ? publication_3->version : 0);
1843 send_presence_publish(sipe_private, publications);
1844 g_free(publications);
1847 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1848 gpointer value,
1849 GString* str)
1851 struct sipe_publication *publication = value;
1853 g_string_append_printf( str,
1854 SIPE_PUB_XML_PUBLICATION_CLEAR,
1855 publication->category,
1856 publication->instance,
1857 publication->container,
1858 publication->version,
1859 "static");
1862 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1864 GString* str;
1865 gchar *publications;
1867 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1868 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1869 return;
1872 str = g_string_new(NULL);
1873 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1874 publications = g_string_free(str, FALSE);
1876 send_presence_publish(sipe_private, publications);
1877 g_free(publications);
1880 /* key is <category><instance><container> */
1881 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1882 const gchar *key)
1884 GSList *entry;
1886 /* filling keys for our publications if not yet cached */
1887 if (!sipe_private->our_publication_keys) {
1888 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1889 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1890 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1891 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1892 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1893 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1894 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1895 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1897 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1898 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1899 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1900 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1901 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1902 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1903 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1904 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1905 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1906 SIPE_DEBUG_INFO("\tNote : %u", 0);
1907 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1909 /* device */
1910 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1911 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1913 /* state:machineState */
1914 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1915 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1916 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1917 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1919 /* state:userState */
1920 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1921 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1922 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1923 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1925 /* state:calendarState */
1926 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1927 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1928 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1929 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1931 /* state:calendarState OOF */
1932 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1933 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1934 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1935 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1937 /* state:phoneState */
1938 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1939 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1940 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1941 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1943 /* note */
1944 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1945 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1946 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1947 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1948 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1949 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1951 /* note OOF */
1952 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1953 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1954 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1955 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1956 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1957 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1959 /* calendarData:WorkingHours */
1960 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1961 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1962 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1963 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1964 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1965 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1966 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1967 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1968 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1969 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1970 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1971 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1973 /* calendarData:FreeBusy */
1974 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1975 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
1976 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1977 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
1978 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1979 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
1980 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1981 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
1982 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1983 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
1984 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1985 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
1987 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
1988 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
1991 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1993 entry = sipe_private->our_publication_keys;
1994 while (entry) {
1995 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1996 if (sipe_strequal(entry->data, key)) {
1997 return TRUE;
1999 entry = entry->next;
2001 return FALSE;
2004 static void sipe_refresh_blocked_status_cb(char *buddy_name,
2005 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
2006 struct sipe_core_private *sipe_private)
2008 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2009 gboolean blocked = (container_id == 32000);
2010 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2012 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2013 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2015 if (blocked != blocked_in_blist) {
2016 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2020 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2022 g_hash_table_foreach(sipe_private->buddies,
2023 (GHFunc) sipe_refresh_blocked_status_cb,
2024 sipe_private);
2028 * When we receive some self (BE) NOTIFY with a new subscriber
2029 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2032 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2033 struct sipmsg *msg)
2035 gchar *contact;
2036 gchar *to;
2037 sipe_xml *xml;
2038 const sipe_xml *node;
2039 const sipe_xml *node2;
2040 char *display_name = NULL;
2041 char *uri;
2042 GSList *category_names = NULL;
2043 int aggreg_avail = 0;
2044 gchar *activity_token = NULL;
2045 gboolean do_update_status = FALSE;
2046 gboolean has_note_cleaned = FALSE;
2047 GHashTable *devices;
2049 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2051 xml = sipe_xml_parse(msg->body, msg->bodylen);
2052 if (!xml) return;
2054 contact = get_contact(sipe_private);
2055 to = sip_uri_self(sipe_private);
2057 /* categories */
2058 /* set list of categories participating in this XML */
2059 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2060 const gchar *name = sipe_xml_attribute(node, "name");
2061 category_names = slist_insert_unique_sorted(category_names, (gchar *)name, (GCompareFunc)strcmp);
2063 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2064 category_names ? (int) g_slist_length(category_names) : -1);
2065 /* drop category information */
2066 if (category_names) {
2067 GSList *entry = category_names;
2068 while (entry) {
2069 GHashTable *cat_publications;
2070 const gchar *category = entry->data;
2071 entry = entry->next;
2072 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2073 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2074 if (cat_publications) {
2075 g_hash_table_remove(sipe_private->our_publications, category);
2076 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2080 g_slist_free(category_names);
2082 /* filling our categories reflected in roaming data */
2083 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2084 g_free, NULL);
2085 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2086 const char *tmp;
2087 const gchar *name = sipe_xml_attribute(node, "name");
2088 guint container = sipe_xml_int_attribute(node, "container", -1);
2089 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2090 guint version = sipe_xml_int_attribute(node, "version", 0);
2091 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2092 sipe_utils_str_to_time(tmp) : 0;
2093 gchar *key;
2094 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2096 /* Ex. clear note: <category name="note"/> */
2097 if (container == (guint)-1) {
2098 g_free(sipe_private->note);
2099 sipe_private->note = NULL;
2100 do_update_status = TRUE;
2101 continue;
2104 /* Ex. clear note: <category name="note" container="200"/> */
2105 if (instance == (guint)-1) {
2106 if (container == 200) {
2107 g_free(sipe_private->note);
2108 sipe_private->note = NULL;
2109 do_update_status = TRUE;
2111 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2112 sipe_remove_category_container_publications(
2113 sipe_private->our_publications, name, container);
2114 continue;
2117 /* key is <category><instance><container> */
2118 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2119 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2121 /* capture all userState publication for later clean up if required */
2122 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2123 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2125 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2126 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2127 publication->category = g_strdup(name);
2128 publication->instance = instance;
2129 publication->container = container;
2130 publication->version = version;
2132 if (!sipe_private->user_state_publications) {
2133 sipe_private->user_state_publications = g_hash_table_new_full(
2134 g_str_hash, g_str_equal,
2135 g_free, (GDestroyNotify)free_publication);
2137 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2138 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2139 key, version);
2143 /* count each client instance only once */
2144 if (sipe_strequal(name, "device"))
2145 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2147 if (sipe_is_our_publication(sipe_private, key)) {
2148 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2150 publication->category = g_strdup(name);
2151 publication->instance = instance;
2152 publication->container = container;
2153 publication->version = version;
2155 /* filling publication->availability */
2156 if (sipe_strequal(name, "state")) {
2157 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2158 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2160 if (xn_avail) {
2161 gchar *avail_str = sipe_xml_data(xn_avail);
2162 if (avail_str) {
2163 publication->availability = atoi(avail_str);
2165 g_free(avail_str);
2167 /* for calendarState */
2168 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2169 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2170 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2172 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2173 if (xn_activity) {
2174 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2175 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2177 event->is_meeting = TRUE;
2180 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2181 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2183 publication->cal_event_hash = sipe_cal_event_hash(event);
2184 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2185 publication->cal_event_hash);
2186 sipe_cal_event_free(event);
2189 /* filling publication->note */
2190 if (sipe_strequal(name, "note")) {
2191 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2193 if (!has_note_cleaned) {
2194 has_note_cleaned = TRUE;
2196 g_free(sipe_private->note);
2197 sipe_private->note = NULL;
2198 sipe_private->note_since = publish_time;
2200 do_update_status = TRUE;
2203 g_free(publication->note);
2204 publication->note = NULL;
2205 if (xn_body) {
2206 char *tmp;
2208 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2209 g_free(tmp);
2210 if (publish_time >= sipe_private->note_since) {
2211 g_free(sipe_private->note);
2212 sipe_private->note = g_strdup(publication->note);
2213 sipe_private->note_since = publish_time;
2214 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2215 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2216 else
2217 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2219 do_update_status = TRUE;
2224 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2225 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2226 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2227 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2228 if (xn_free_busy) {
2229 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2230 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2232 if (xn_working_hours) {
2233 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2237 if (!cat_publications) {
2238 cat_publications = g_hash_table_new_full(
2239 g_str_hash, g_str_equal,
2240 g_free, (GDestroyNotify)free_publication);
2241 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2242 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2244 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2245 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2247 g_free(key);
2249 /* aggregateState (not an our publication) from 2-nd container */
2250 if (sipe_strequal(name, "state") && container == 2) {
2251 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2252 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2254 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2255 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2257 if (xn_avail) {
2258 gchar *avail_str = sipe_xml_data(xn_avail);
2259 if (avail_str) {
2260 aggreg_avail = atoi(avail_str);
2262 g_free(avail_str);
2265 do_update_status = TRUE;
2268 if (xn_activity) {
2269 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2273 /* userProperties published by server from AD */
2274 if (!sipe_private->csta &&
2275 sipe_strequal(name, "userProperties")) {
2276 const sipe_xml *line;
2277 /* line, for Remote Call Control (RCC) */
2278 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2279 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2280 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2281 gchar *line_uri;
2283 if (!line_server || !(sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual"))) continue;
2285 line_uri = sipe_xml_data(line);
2286 if (line_uri) {
2287 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s", line_uri, line_server);
2288 sip_csta_open(sipe_private, line_uri, line_server);
2290 g_free(line_uri);
2292 break;
2296 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2297 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2299 /* active clients for user account */
2300 if (g_hash_table_size(devices) > 1) {
2301 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2302 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2303 g_hash_table_size(devices));
2304 } else {
2305 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2306 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2308 g_hash_table_destroy(devices);
2310 /* containers */
2311 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2312 guint id = sipe_xml_int_attribute(node, "id", 0);
2313 struct sipe_container *container = sipe_find_container(sipe_private, id);
2315 if (container) {
2316 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2317 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2318 sipe_ocs2007_free_container(container);
2320 container = g_new0(struct sipe_container, 1);
2321 container->id = id;
2322 container->version = sipe_xml_int_attribute(node, "version", 0);
2323 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2324 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2326 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2327 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2328 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2329 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2330 container->members = g_slist_append(container->members, member);
2331 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2332 member->type, member->value ? member->value : "");
2336 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2337 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2338 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2339 char *container_xmls = NULL;
2340 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2341 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2343 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2344 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2345 /* initial set-up to let counterparties see your status */
2346 if (sameEnterpriseAL < 0) {
2347 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2348 guint version = container ? container->version : 0;
2349 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2351 if (federatedAL < 0) {
2352 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2353 guint version = container ? container->version : 0;
2354 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2356 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2358 if (container_xmls) {
2359 sipe_send_set_container_members(sipe_private, container_xmls);
2361 g_free(container_xmls);
2364 /* Refresh contacts' blocked status */
2365 sipe_refresh_blocked_status(sipe_private);
2367 /* subscribers */
2368 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2369 const char *user;
2370 const char *acknowledged;
2371 gchar *hdr;
2372 gchar *body;
2374 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2375 if (!user) continue;
2376 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2377 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2378 uri = sip_uri_from_name(user);
2380 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2381 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2383 acknowledged= sipe_xml_attribute(node, "acknowledged");
2384 if(sipe_strcase_equal(acknowledged,"false")){
2385 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2386 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2387 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2390 hdr = g_strdup_printf(
2391 "Contact: %s\r\n"
2392 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2394 body = g_strdup_printf(
2395 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2396 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2397 "</setSubscribers>", user);
2399 sip_transport_service(sipe_private,
2401 hdr,
2402 body,
2403 NULL);
2404 g_free(body);
2405 g_free(hdr);
2407 g_free(display_name);
2408 g_free(uri);
2411 g_free(contact);
2412 sipe_xml_free(xml);
2414 /* Publish initial state if not yet.
2415 * Assuming this happens on initial responce to subscription to roaming-self
2416 * so we've already updated our roaming data in full.
2417 * Only for 2007+
2419 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2420 send_publish_category_initial(sipe_private);
2421 sipe_groupchat_init(sipe_private);
2422 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2423 /* dalayed run */
2424 sipe_cal_delayed_calendar_update(sipe_private);
2425 do_update_status = FALSE;
2426 } else if (aggreg_avail) {
2428 if (aggreg_avail &&
2429 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2430 /* not offline */
2431 sipe_status_set_token(sipe_private,
2432 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2433 } else {
2434 /* do not let offline status switch us off */
2435 sipe_status_set_activity(sipe_private,
2436 SIPE_ACTIVITY_INVISIBLE);
2440 if (do_update_status) {
2441 sipe_status_and_note(sipe_private, NULL);
2444 g_free(to);
2445 g_free(activity_token);
2449 * for Access levels menu
2451 #define INDENT_FMT " %s"
2454 * Member is indirectly belong to access level container.
2455 * For example 'sameEnterprise' is in the container and user
2456 * belongs to that same enterprise.
2458 #define INDENT_MARKED_INHERITED_FMT "= %s"
2460 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2461 struct sipe_backend_buddy_menu *menu,
2462 const gchar *member_type,
2463 const gchar *member_value,
2464 const gboolean extra_menu)
2466 unsigned int i;
2467 gboolean is_group_access = FALSE;
2468 int container_id;
2470 if (!menu)
2471 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2473 container_id = sipe_ocs2007_find_access_level(sipe_private,
2474 member_type,
2475 member_value,
2476 &is_group_access);
2478 for (i = 1; i <= CONTAINERS_LEN; i++) {
2480 * Blocked should remain in the first place
2481 * in the containers[] array.
2483 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2484 int container_j = containers[j];
2485 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2486 struct sipe_container *container = create_container(j,
2487 member_type,
2488 member_value,
2489 FALSE);
2490 gchar *label;
2492 /* libpurple memory leak workaround */
2493 blist_menu_remember_container(sipe_private, container);
2495 /* current container/access level */
2496 if (container_j == container_id) {
2497 label = is_group_access ?
2498 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2499 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2500 } else {
2501 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2504 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2505 menu,
2506 label,
2507 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2508 container);
2509 g_free(label);
2512 if (extra_menu && (container_id >= 0) && !is_group_access) {
2513 struct sipe_container *container = create_container(0,
2514 member_type,
2515 member_value,
2516 TRUE);
2517 gchar *label;
2519 /* separator */
2520 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2521 menu,
2522 " --------------");
2525 /* libpurple memory leak workaround */
2526 blist_menu_remember_container(sipe_private, container);
2528 /* Translators: remove (clear) previously assigned access level */
2529 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2530 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2531 menu,
2532 label,
2533 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2534 container);
2535 g_free(label);
2538 return(menu);
2541 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2543 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2544 GSList *access_domains, *entry;
2546 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2547 menu,
2548 _("People in my company"),
2549 access_levels_menu(sipe_private,
2550 NULL,
2551 "sameEnterprise",
2552 NULL,
2553 FALSE));
2555 /* this is original name, don't edit */
2556 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2557 menu,
2558 _("People in domains connected with my company"),
2559 access_levels_menu(sipe_private,
2560 NULL,
2561 "federated",
2562 NULL,
2563 FALSE));
2565 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2566 menu,
2567 _("People in public domains"),
2568 access_levels_menu(sipe_private,
2569 NULL,
2570 "publicCloud",
2571 NULL,
2572 TRUE));
2574 entry = access_domains = get_access_domains(sipe_private);
2575 while (entry) {
2576 gchar *domain = entry->data;
2577 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2579 /* takes over ownership of entry->data (= domain) */
2580 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2581 menu,
2582 menu_name,
2583 access_levels_menu(sipe_private,
2584 NULL,
2585 "domain",
2586 domain,
2587 TRUE));
2588 g_free(menu_name);
2590 entry = entry->next;
2592 g_slist_free(access_domains);
2594 /* separator */
2595 /* People in domains connected with my company */
2596 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2597 menu,
2598 "-------------------------------------------");
2600 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2601 menu,
2602 _("Add new domain..."),
2603 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2604 NULL);
2606 return(menu);
2609 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2610 const gchar *buddy_name)
2612 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2613 gchar *label;
2616 * Workaround for missing libpurple API to release resources allocated
2617 * during blist_node_menu() callback. See also:
2619 * <http://developer.pidgin.im/ticket/12597>
2621 * We remember all memory blocks in a list and deallocate them when
2623 * - the next time we enter the callback, or
2624 * - the account is disconnected
2626 * That means that after the buddy menu has been closed we have unused
2627 * resources but at least we don't leak them anymore...
2629 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2631 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2632 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2633 menu,
2634 label,
2635 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2636 NULL);
2637 g_free(label);
2639 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2640 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2641 menu,
2642 label,
2643 access_groups_menu(sipe_private));
2644 g_free(label);
2646 menu = access_levels_menu(sipe_private,
2647 menu,
2648 "user",
2649 sipe_get_no_sip_uri(buddy_name),
2650 TRUE);
2652 return(menu);
2656 Local Variables:
2657 mode: c
2658 c-file-style: "bsd"
2659 indent-tabs-mode: t
2660 tab-width: 8
2661 End: