ocs2007: don't try to access no-existing hash table entries
[siplcs.git] / src / core / sipe-ocs2007.c
blob3730220fda3cbb46c57bb89eb2149a83df7f0e38
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2017 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-media.h"
49 #include "sipe-nls.h"
50 #include "sipe-ocs2007.h"
51 #include "sipe-schedule.h"
52 #include "sipe-status.h"
53 #include "sipe-utils.h"
54 #include "sipe-xml.h"
56 /** MS-PRES publication */
57 struct sipe_publication {
58 gchar *category;
59 guint instance;
60 guint container;
61 guint version;
62 /** for 'state' category */
63 int availability;
64 /** for 'state:calendarState' category */
65 char *cal_event_hash;
66 /** for 'note' category */
67 gchar *note;
68 /** for 'calendarData' category; 300(Team) container */
69 char *working_hours_xml_str;
70 char *fb_start_str;
71 char *free_busy_base64;
74 /**
75 * 2007-style Activity and Availability.
77 * [MS-PRES] 3.7.5.5
79 * Conversion of legacyInterop availability ranges and activity tokens into
80 * SIPE activity tokens. The descriptions of availability ranges are defined at:
82 * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
84 * The values define the starting point of a range.
86 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
95 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
96 const gchar *activity)
98 guint type;
100 if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
101 type = SIPE_ACTIVITY_OFFLINE;
102 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
103 type = SIPE_ACTIVITY_AVAILABLE;
104 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
105 type = SIPE_ACTIVITY_INACTIVE;
106 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
107 type = sipe_status_token_to_activity(activity);
108 if ((type != SIPE_ACTIVITY_ON_PHONE) &&
109 (type != SIPE_ACTIVITY_IN_CONF))
110 type = SIPE_ACTIVITY_BUSY;
111 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
112 type = SIPE_ACTIVITY_BUSYIDLE;
113 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
114 type = SIPE_ACTIVITY_DND;
115 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
116 type = SIPE_ACTIVITY_BRB;
117 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
118 type = SIPE_ACTIVITY_AWAY;
119 } else {
120 type = SIPE_ACTIVITY_OFFLINE;
123 return sipe_status_activity_to_token(type);
126 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
128 if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
129 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
130 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
131 } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
132 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
133 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
134 } else {
135 return(NULL);
140 * @param sipe_status_id (in)
141 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
142 * requests this token]
144 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
145 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
146 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
147 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
148 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
149 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
150 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
151 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
152 const gchar **activity_token)
154 guint availability;
155 guint activity;
157 if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
158 availability = SIPE_OCS2007_AVAILABILITY_AWAY;
159 activity = SIPE_ACTIVITY_AWAY;
160 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
161 availability = SIPE_OCS2007_AVAILABILITY_BRB;
162 activity = SIPE_ACTIVITY_BRB;
163 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
164 availability = SIPE_OCS2007_AVAILABILITY_DND;
165 activity = SIPE_ACTIVITY_DND;
166 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
167 availability = SIPE_OCS2007_AVAILABILITY_BUSY;
168 activity = SIPE_ACTIVITY_BUSY;
169 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
170 availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
171 activity = SIPE_ACTIVITY_ONLINE;
172 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
173 availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
174 activity = SIPE_ACTIVITY_UNSET;
175 } else {
176 /* Offline or invisible */
177 availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
178 activity = SIPE_ACTIVITY_OFFLINE;
181 if (activity_token) {
182 *activity_token = sipe_status_activity_to_token(activity);
185 return(availability);
188 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
190 return(SIPE_OCS2007_AVAILABILITY_BUSY >=
191 sipe_ocs2007_availability_from_status(status_id, NULL));
195 gboolean sipe_ocs2007_availability_is_away(guint availability)
197 return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
200 static void send_presence_publish(struct sipe_core_private *sipe_private,
201 const char *publications);
203 static void free_publication(struct sipe_publication *publication)
205 g_free(publication->category);
206 g_free(publication->cal_event_hash);
207 g_free(publication->note);
209 g_free(publication->working_hours_xml_str);
210 g_free(publication->fb_start_str);
211 g_free(publication->free_busy_base64);
213 g_free(publication);
216 struct hash_table_delete_payload {
217 GHashTable *hash_table;
218 guint container;
221 static void sipe_remove_category_container_publications_cb(const gchar *name,
222 struct sipe_publication *publication,
223 struct hash_table_delete_payload *payload)
225 if (publication->container == payload->container) {
226 g_hash_table_remove(payload->hash_table, name);
230 static void sipe_remove_category_container_publications(GHashTable *our_publications,
231 const gchar *category,
232 guint container)
234 struct hash_table_delete_payload payload;
235 payload.hash_table = g_hash_table_lookup(our_publications, category);
237 if (!payload.hash_table) return;
239 payload.container = container;
240 g_hash_table_foreach(payload.hash_table,
241 (GHFunc)sipe_remove_category_container_publications_cb,
242 &payload);
245 /** MS-PRES container */
246 struct sipe_container {
247 guint id;
248 guint version;
249 GSList *members;
252 /** MS-PRES container member */
253 struct sipe_container_member {
254 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
255 gchar *type;
256 gchar *value;
259 static const guint containers[] = {32000, 400, 300, 200, 100};
260 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
262 static void free_container_member(struct sipe_container_member *member)
264 if (!member) return;
266 g_free(member->type);
267 g_free(member->value);
268 g_free(member);
271 static void sipe_ocs2007_free_container(struct sipe_container *container)
273 GSList *entry;
275 if (!container) return;
277 entry = container->members;
278 while (entry) {
279 void *data = entry->data;
280 entry = g_slist_remove(entry, data);
281 free_container_member((struct sipe_container_member *)data);
283 g_free(container);
286 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
288 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
289 sipe_utils_slist_free_full(sipe_private->blist_menu_containers,
290 (GDestroyNotify) sipe_ocs2007_free_container);
291 sipe_private->blist_menu_containers = NULL;
294 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
295 struct sipe_container *container)
297 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
298 container);
301 static struct sipe_container *create_container(guint index,
302 const gchar *member_type,
303 const gchar *member_value,
304 gboolean is_group)
306 struct sipe_container *container = g_new0(struct sipe_container, 1);
307 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
309 container->id = is_group ? (guint) -1 : containers[index];
310 container->members = g_slist_append(container->members, member);
311 member->type = g_strdup(member_type);
312 member->value = g_strdup(member_value);
314 return(container);
317 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
319 sipe_utils_slist_free_full(sipe_private->containers,
320 (GDestroyNotify) sipe_ocs2007_free_container);
324 * Finds locally stored MS-PRES container member
326 static struct sipe_container_member *
327 sipe_find_container_member(struct sipe_container *container,
328 const gchar *type,
329 const gchar *value)
331 struct sipe_container_member *member;
332 GSList *entry;
334 if (container == NULL || type == NULL) {
335 return NULL;
338 entry = container->members;
339 while (entry) {
340 member = entry->data;
341 if (sipe_strcase_equal(member->type, type) &&
342 sipe_strcase_equal(member->value, value))
344 return member;
346 entry = entry->next;
348 return NULL;
352 * Finds locally stored MS-PRES container by id
354 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
355 guint id)
357 GSList *entry = sipe_private->containers;
358 while (entry) {
359 struct sipe_container *container = entry->data;
360 if (id == container->id) {
361 return container;
363 entry = entry->next;
365 return NULL;
368 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
369 const gchar *type,
370 const gchar *value)
372 unsigned int i = 0;
373 const gchar *value_mod = value;
375 if (!type) return -1;
377 if (sipe_strequal("user", type)) {
378 value_mod = sipe_get_no_sip_uri(value);
381 for (i = 0; i < CONTAINERS_LEN; i++) {
382 struct sipe_container_member *member;
383 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
384 if (!container) continue;
386 member = sipe_find_container_member(container, type, value_mod);
387 if (member) return containers[i];
390 return -1;
394 * Returns pointer to domain part in provided Email URL
396 * @param email an email URL. Example: first.last@hq.company.com
397 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
399 * Doesn't allocate memory
401 static const gchar *sipe_get_domain(const gchar *email)
403 gchar *tmp;
405 if (!email) return NULL;
407 tmp = strstr(email, "@");
409 if (tmp && ((tmp+1) < (email + strlen(email)))) {
410 return tmp+1;
411 } else {
412 return NULL;
416 /* @TODO: replace with binary search for faster access? */
417 /** source: http://support.microsoft.com/kb/897567 */
418 static const gchar * const public_domains[] = {
419 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
420 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
421 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
422 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
423 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
424 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
425 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
426 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
427 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
428 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
429 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
430 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
431 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
432 "yahoo.com",
433 NULL};
435 static gboolean sipe_is_public_domain(const gchar *domain)
437 int i = 0;
438 while (public_domains[i]) {
439 if (sipe_strcase_equal(public_domains[i], domain)) {
440 return TRUE;
442 i++;
444 return FALSE;
448 * Access Levels
449 * 32000 - Blocked
450 * 400 - Personal
451 * 300 - Team
452 * 200 - Company
453 * 100 - Public
455 const gchar *sipe_ocs2007_access_level_name(guint id)
457 switch (id) {
458 case 32000: return _("Blocked");
459 case 400: return _("Personal");
460 case 300: return _("Team");
461 case 200: return _("Company");
462 case 100: return _("Public");
464 return _("Unknown");
467 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
468 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
469 const gchar *type,
470 const gchar *value,
471 gboolean *is_group_access)
473 int container_id = -1;
475 if (sipe_strequal("user", type)) {
476 const char *domain;
477 const char *no_sip_uri = sipe_get_no_sip_uri(value);
479 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
480 if (container_id >= 0) {
481 if (is_group_access) *is_group_access = FALSE;
482 return container_id;
485 domain = sipe_get_domain(no_sip_uri);
486 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
487 if (container_id >= 0) {
488 if (is_group_access) *is_group_access = TRUE;
489 return container_id;
492 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
493 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
494 if (is_group_access) *is_group_access = TRUE;
495 return container_id;
498 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
499 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
500 if (is_group_access) *is_group_access = TRUE;
501 return container_id;
504 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
505 if ((container_id >= 0)) {
506 if (is_group_access) *is_group_access = TRUE;
507 return container_id;
509 } else {
510 container_id = sipe_find_member_access_level(sipe_private, type, value);
511 if (is_group_access) *is_group_access = FALSE;
514 return container_id;
517 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
519 struct sipe_container *container;
520 struct sipe_container_member *member;
521 GSList *entry;
522 GSList *entry2;
523 GSList *res = NULL;
525 entry = sipe_private->containers;
526 while (entry) {
527 container = entry->data;
529 entry2 = container->members;
530 while (entry2) {
531 member = entry2->data;
532 if (sipe_strcase_equal(member->type, "domain"))
534 res = sipe_utils_slist_insert_unique_sorted(res,
535 g_strdup(member->value),
536 (GCompareFunc)g_ascii_strcasecmp,
537 g_free);
539 entry2 = entry2->next;
541 entry = entry->next;
543 return res;
546 static void sipe_send_container_members_prepare(const guint container_id,
547 const guint container_version,
548 const gchar *action,
549 const gchar *type,
550 const gchar *value,
551 char **container_xmls)
553 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
554 gchar *body;
556 if (!container_xmls) return;
558 body = g_strdup_printf(
559 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
560 container_id,
561 container_version,
562 action,
563 type,
564 value_str);
565 g_free(value_str);
567 if ((*container_xmls) == NULL) {
568 *container_xmls = body;
569 } else {
570 char *tmp = *container_xmls;
572 *container_xmls = g_strconcat(*container_xmls, body, NULL);
573 g_free(tmp);
574 g_free(body);
578 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
579 char *container_xmls)
581 gchar *self;
582 gchar *contact;
583 gchar *hdr;
584 gchar *body;
586 if (!container_xmls) return;
588 self = sip_uri_self(sipe_private);
589 body = g_strdup_printf(
590 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
591 "%s"
592 "</setContainerMembers>",
593 container_xmls);
595 contact = get_contact(sipe_private);
596 hdr = g_strdup_printf("Contact: %s\r\n"
597 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
598 g_free(contact);
600 sip_transport_service(sipe_private,
601 self,
602 hdr,
603 body,
604 NULL);
606 g_free(hdr);
607 g_free(body);
608 g_free(self);
612 * @param container_id a new access level. If -1 then current access level
613 * is just removed (I.e. the member is removed from all containers).
614 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
615 * @param value a value for member. E.g. SIP URI for "user" member type.
617 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
618 const int container_id,
619 const gchar *type,
620 const gchar *value)
622 unsigned int i;
623 int current_container_id = -1;
624 char *container_xmls = NULL;
626 /* for each container: find/delete */
627 for (i = 0; i < CONTAINERS_LEN; i++) {
628 struct sipe_container_member *member;
629 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
631 if (!container) continue;
633 member = sipe_find_container_member(container, type, value);
634 if (member) {
635 current_container_id = containers[i];
636 /* delete/publish current access level */
637 if (container_id < 0 || container_id != current_container_id) {
638 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
639 /* remove member from our cache, to be able to recalculate AL below */
640 container->members = g_slist_remove(container->members, member);
645 /* recalculate AL below */
646 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
648 /* assign/publish new access level */
649 if (container_id != current_container_id && container_id >= 0) {
650 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
651 guint version = container ? container->version : 0;
653 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
656 if (container_xmls) {
657 sipe_send_set_container_members(sipe_private, container_xmls);
659 g_free(container_xmls);
662 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
663 gpointer parameter)
665 struct sipe_container *container = parameter;
666 struct sipe_container_member *member;
668 if (!container || !container->members) return;
670 member = ((struct sipe_container_member *)container->members->data);
672 if (!member->type) return;
674 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
675 container->id, member->type, member->value ? member->value : "");
677 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
678 container->id,
679 member->type,
680 member->value);
684 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
685 const gchar *domain,
686 guint index)
688 /* move Blocked first */
689 guint i = (index == 4) ? 0 : index + 1;
690 guint container_id = containers[i];
692 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
693 domain ? domain : "", index, container_id);
695 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
696 container_id,
697 "domain",
698 domain);
702 * Schedules process of self status publish
703 * based on own calendar information.
704 * Should be scheduled to the beginning of every
705 * 15 min interval, like:
706 * 13:00, 13:15, 13:30, 13:45, etc.
709 static void schedule_publish_update(struct sipe_core_private *sipe_private,
710 time_t calculate_from)
712 int interval = 5*60;
713 /** start of the beginning of closest 5 min interval. */
714 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
716 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
717 sipe_utils_time_to_debug_str(localtime(&calculate_from)));
718 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
719 sipe_utils_time_to_debug_str(localtime(&next_start)));
721 sipe_schedule_seconds(sipe_private,
722 "<+2007-cal-status>",
723 NULL,
724 next_start - time(NULL),
725 sipe_ocs2007_presence_publish,
726 NULL);
730 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
731 * @param availability (%d) Ex.: 6500
733 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
734 "<availability>%d</availability>"
736 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
737 * @param token (%s) Ex.: in-a-meeting
738 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
739 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
741 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
742 "<activity token=\"%s\" %s %s></activity>"
744 * Publishes 'calendarState' category.
745 * @param instance (%u) Ex.: 1339299275
746 * @param version (%u) Ex.: 1
747 * @param uri (%s) Ex.: john@contoso.com
748 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
749 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
750 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
751 * @param meeting_subject (%s) Ex.: Customer Meeting
752 * @param meeting_location (%s) Ex.: Conf Room 100
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 #define SIPE_PUB_XML_STATE_CALENDAR \
764 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
765 "<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\">"\
766 "%s"\
767 "%s"\
768 "<endpointLocation/>"\
769 "<meetingSubject>%s</meetingSubject>"\
770 "<meetingLocation>%s</meetingLocation>"\
771 "</state>"\
772 "</publication>"\
773 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" 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>"
783 * Publishes to clear 'calendarState' and 'phoneState' category
784 * @param instance (%u) Ex.: 1251210982
785 * @param version (%u) Ex.: 1
787 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
788 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
789 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
792 * Publishes to clear any category
793 * @param category_name (%s) Ex.: state
794 * @param instance (%u) Ex.: 536870912
795 * @param container (%u) Ex.: 3
796 * @param version (%u) Ex.: 1
797 * @param expireType (%s) Ex.: static
799 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
800 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
803 * Publishes 'note' category.
804 * @param instance (%u) Ex.: 2135971629; 0 for personal
805 * @param container (%u) Ex.: 200
806 * @param version (%u) Ex.: 2
807 * @param type (%s) Ex.: personal or OOF
808 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
809 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
810 * @param body (%s) Ex.: In the office
812 #define SIPE_PUB_XML_NOTE \
813 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
814 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
815 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
816 "</note>"\
817 "</publication>"
819 * Publishes 'phoneState' category.
820 * @param instance (%u) Ex.: 1339299275
821 * @param version (%u) Ex.: 1
822 * @param availability (%u) Ex.: 6500
823 * @param token (%s) Ex.: on-the-phone
824 * @param minAvailability (%u) generally same as availability
826 * @param instance (%u) Ex.: 1339299275
827 * @param version (%u) Ex.: 1
828 * @param availability (%u) Ex.: 6500
829 * @param token (%s) Ex.: on-the-phone
830 * @param minAvailability (%u) generally same as availability
832 #define SIPE_PUB_XML_STATE_PHONE \
833 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
834 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
835 "<availability>%u</availability>"\
836 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
837 "</state>"\
838 "</publication>"\
839 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
840 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
841 "<availability>%u</availability>"\
842 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
843 "</state>"\
844 "</publication>"
847 * Only Busy and OOF calendar event are published.
848 * Different instances are used for that.
850 * Must be g_free'd after use.
852 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
853 struct sipe_cal_event *event,
854 const char *uri,
855 int cal_satus)
857 gchar *start_time_str;
858 int availability = 0;
859 gchar *res;
860 gchar *tmp = NULL;
861 guint instance = (cal_satus == SIPE_CAL_OOF) ?
862 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
863 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
865 /* key is <category><instance><container> */
866 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
867 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
868 gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
869 struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
870 struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
872 g_free(key_2);
873 g_free(key_3);
875 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
876 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
877 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
878 return NULL;
881 if (event &&
882 publication_3 &&
883 (publication_3->availability == availability) &&
884 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
886 g_free(tmp);
887 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
888 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
889 return NULL; /* nothing to update */
891 g_free(tmp);
893 if (event &&
894 (event->cal_status == SIPE_CAL_BUSY ||
895 event->cal_status == SIPE_CAL_OOF))
897 gchar *availability_xml_str = NULL;
898 gchar *activity_xml_str = NULL;
899 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
900 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
902 if (event->cal_status == SIPE_CAL_BUSY) {
903 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
904 SIPE_OCS2007_AVAILABILITY_BUSY);
907 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
908 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
909 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
910 "minAvailability=\"6500\"",
911 "maxAvailability=\"8999\"");
912 } else if (event->cal_status == SIPE_CAL_OOF) {
913 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
914 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
915 "minAvailability=\"12000\"",
916 "");
918 start_time_str = sipe_utils_time_to_str(event->start_time);
920 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
921 instance,
922 publication_2 ? publication_2->version : 0,
923 uri,
924 start_time_str,
925 availability_xml_str ? availability_xml_str : "",
926 activity_xml_str ? activity_xml_str : "",
927 escaped_subject ? escaped_subject : "",
928 escaped_location ? escaped_location : "",
930 instance,
931 publication_3 ? publication_3->version : 0,
932 uri,
933 start_time_str,
934 availability_xml_str ? availability_xml_str : "",
935 activity_xml_str ? activity_xml_str : "",
936 escaped_subject ? escaped_subject : "",
937 escaped_location ? escaped_location : ""
939 g_free(escaped_location);
940 g_free(escaped_subject);
941 g_free(start_time_str);
942 g_free(availability_xml_str);
943 g_free(activity_xml_str);
946 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
948 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
949 instance,
950 publication_2 ? publication_2->version : 0,
952 instance,
953 publication_3 ? publication_3->version : 0
957 return res;
961 * Returns 'note' XML part for publication.
962 * Must be g_free'd after use.
964 * Protocol format for Note is plain text.
966 * @param note a note in Sipe internal HTML format
967 * @param note_type either personal or OOF
969 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
970 const char *note, /* html */
971 const char *note_type,
972 time_t note_start,
973 time_t note_end,
974 gboolean force_publish)
976 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
977 /* key is <category><instance><container> */
978 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
979 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
980 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
982 gpointer notes = g_hash_table_lookup(sipe_private->our_publications, "note");
983 struct sipe_publication *publication_note_200 = notes ? g_hash_table_lookup(notes, key_note_200) : NULL;
984 struct sipe_publication *publication_note_300 = notes ? g_hash_table_lookup(notes, key_note_300) : NULL;
985 struct sipe_publication *publication_note_400 = notes ? g_hash_table_lookup(notes, key_note_400) : NULL;
987 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
988 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
989 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
990 char *res, *tmp1, *tmp2, *tmp3;
991 char *start_time_attr;
992 char *end_time_attr;
994 g_free(tmp);
995 tmp = NULL;
996 g_free(key_note_200);
997 g_free(key_note_300);
998 g_free(key_note_400);
1000 /* we even need to republish empty note */
1001 if (!force_publish && sipe_strequal(n1, n2))
1003 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1004 g_free(n1);
1005 return NULL; /* nothing to update */
1008 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1009 g_free(tmp);
1010 tmp = NULL;
1011 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1012 g_free(tmp);
1014 if (n1) {
1015 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1016 instance,
1017 200,
1018 publication_note_200 ? publication_note_200->version : 0,
1019 note_type,
1020 start_time_attr ? start_time_attr : "",
1021 end_time_attr ? end_time_attr : "",
1022 n1);
1024 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1025 instance,
1026 300,
1027 publication_note_300 ? publication_note_300->version : 0,
1028 note_type,
1029 start_time_attr ? start_time_attr : "",
1030 end_time_attr ? end_time_attr : "",
1031 n1);
1033 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1034 instance,
1035 400,
1036 publication_note_400 ? publication_note_400->version : 0,
1037 note_type,
1038 start_time_attr ? start_time_attr : "",
1039 end_time_attr ? end_time_attr : "",
1040 n1);
1041 } else {
1042 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1043 "note",
1044 instance,
1045 200,
1046 publication_note_200 ? publication_note_200->version : 0,
1047 "static");
1048 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1049 "note",
1050 instance,
1051 300,
1052 publication_note_200 ? publication_note_200->version : 0,
1053 "static");
1054 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1055 "note",
1056 instance,
1057 400,
1058 publication_note_200 ? publication_note_200->version : 0,
1059 "static");
1061 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
1063 g_free(start_time_attr);
1064 g_free(end_time_attr);
1065 g_free(tmp1);
1066 g_free(tmp2);
1067 g_free(tmp3);
1068 g_free(n1);
1070 return res;
1074 * Publishes 'calendarData' category's WorkingHours.
1076 * @param version (%u) Ex.: 1
1077 * @param email (%s) Ex.: alice@cosmo.local
1078 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1080 * @param version (%u)
1082 * @param version (%u)
1083 * @param email (%s)
1084 * @param working_hours_xml_str (%s)
1086 * @param version (%u)
1087 * @param email (%s)
1088 * @param working_hours_xml_str (%s)
1090 * @param version (%u)
1091 * @param email (%s)
1092 * @param working_hours_xml_str (%s)
1094 * @param version (%u)
1096 #define SIPE_PUB_XML_WORKING_HOURS \
1097 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1098 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1099 "</calendarData>"\
1100 "</publication>"\
1101 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1102 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1103 "</publication>"\
1104 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1105 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1106 "</calendarData>"\
1107 "</publication>"\
1108 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" 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=\"400\" version=\"%d\" expireType=\"static\">"\
1113 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1114 "</calendarData>"\
1115 "</publication>"\
1116 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1117 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1118 "</publication>"
1121 * Returns 'calendarData' XML part with WorkingHours for publication.
1122 * Must be g_free'd after use.
1124 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1126 struct sipe_calendar* cal = sipe_private->calendar;
1128 /* key is <category><instance><container> */
1129 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1130 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1131 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1132 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1133 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1134 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1136 gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData");
1137 struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL;
1138 struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL;
1139 struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL;
1140 struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL;
1141 struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL;
1142 struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL;
1144 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1145 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1147 g_free(key_cal_1);
1148 g_free(key_cal_100);
1149 g_free(key_cal_200);
1150 g_free(key_cal_300);
1151 g_free(key_cal_400);
1152 g_free(key_cal_32000);
1154 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1155 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1156 return NULL;
1159 if (sipe_strequal(n1, n2))
1161 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1162 return NULL; /* nothing to update */
1165 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1166 /* 1 */
1167 publication_cal_1 ? publication_cal_1->version : 0,
1168 cal->email,
1169 cal->working_hours_xml_str,
1170 /* 100 - Public */
1171 publication_cal_100 ? publication_cal_100->version : 0,
1172 /* 200 - Company */
1173 publication_cal_200 ? publication_cal_200->version : 0,
1174 cal->email,
1175 cal->working_hours_xml_str,
1176 /* 300 - Team */
1177 publication_cal_300 ? publication_cal_300->version : 0,
1178 cal->email,
1179 cal->working_hours_xml_str,
1180 /* 400 - Personal */
1181 publication_cal_400 ? publication_cal_400->version : 0,
1182 cal->email,
1183 cal->working_hours_xml_str,
1184 /* 32000 - Blocked */
1185 publication_cal_32000 ? publication_cal_32000->version : 0
1190 * Publishes 'calendarData' category's FreeBusy.
1192 * @param instance (%u) Ex.: 1300372959
1193 * @param version (%u) Ex.: 1
1195 * @param instance (%u) Ex.: 1300372959
1196 * @param version (%u) Ex.: 1
1198 * @param instance (%u) Ex.: 1300372959
1199 * @param version (%u) Ex.: 1
1200 * @param email (%s) Ex.: alice@cosmo.local
1201 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1202 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1204 * @param instance (%u) Ex.: 1300372959
1205 * @param version (%u) Ex.: 1
1206 * @param email (%s) Ex.: alice@cosmo.local
1207 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1208 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1210 * @param instance (%u) Ex.: 1300372959
1211 * @param version (%u) Ex.: 1
1212 * @param email (%s) Ex.: alice@cosmo.local
1213 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1214 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1216 * @param instance (%u) Ex.: 1300372959
1217 * @param version (%u) Ex.: 1
1219 #define SIPE_PUB_XML_FREE_BUSY \
1220 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1221 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1222 "</publication>"\
1223 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1224 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1225 "</publication>"\
1226 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1227 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1228 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1229 "</calendarData>"\
1230 "</publication>"\
1231 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1232 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1233 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1234 "</calendarData>"\
1235 "</publication>"\
1236 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1237 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1238 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1239 "</calendarData>"\
1240 "</publication>"\
1241 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1242 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1243 "</publication>"
1246 * Returns 'calendarData' XML part with FreeBusy for publication.
1247 * Must be g_free'd after use.
1249 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1251 struct sipe_calendar* cal = sipe_private->calendar;
1252 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1253 char *fb_start_str;
1254 char *free_busy_base64;
1255 /* const char *st; */
1256 /* const char *fb; */
1257 char *res;
1259 /* key is <category><instance><container> */
1260 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1261 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1262 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1263 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1264 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1265 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1267 gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData");
1268 struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL;
1269 struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL;
1270 struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL;
1271 struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL;
1272 struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL;
1273 struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL;
1275 g_free(key_cal_1);
1276 g_free(key_cal_100);
1277 g_free(key_cal_200);
1278 g_free(key_cal_300);
1279 g_free(key_cal_400);
1280 g_free(key_cal_32000);
1282 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1283 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1284 return NULL;
1287 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1288 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1290 /* we will rebuplish the same data to refresh publication time,
1291 * so if data from multiple sources, most recent will be choosen
1293 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1294 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1296 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1298 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1299 // g_free(fb_start_str);
1300 // g_free(free_busy_base64);
1301 // return NULL; /* nothing to update */
1304 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1305 /* 1 */
1306 cal_data_instance,
1307 publication_cal_1 ? publication_cal_1->version : 0,
1308 /* 100 - Public */
1309 cal_data_instance,
1310 publication_cal_100 ? publication_cal_100->version : 0,
1311 /* 200 - Company */
1312 cal_data_instance,
1313 publication_cal_200 ? publication_cal_200->version : 0,
1314 cal->email,
1315 fb_start_str,
1316 free_busy_base64,
1317 /* 300 - Team */
1318 cal_data_instance,
1319 publication_cal_300 ? publication_cal_300->version : 0,
1320 cal->email,
1321 fb_start_str,
1322 free_busy_base64,
1323 /* 400 - Personal */
1324 cal_data_instance,
1325 publication_cal_400 ? publication_cal_400->version : 0,
1326 cal->email,
1327 fb_start_str,
1328 free_busy_base64,
1329 /* 32000 - Blocked */
1330 cal_data_instance,
1331 publication_cal_32000 ? publication_cal_32000->version : 0
1334 g_free(fb_start_str);
1335 g_free(free_busy_base64);
1336 return res;
1339 #ifdef HAVE_VV
1340 #define SIPE_PUB_XML_DEVICE_VV \
1341 "<voice capture=\"true\" render=\"true\" publish=\"false\"/>"\
1342 "<video capture=\"true\" render=\"true\" publish=\"false\"/>"
1343 #else
1344 #define SIPE_PUB_XML_DEVICE_VV
1345 #endif
1347 #ifdef HAVE_FREERDP
1348 #define SIPE_PUB_XML_DEVICE_APPSHARE \
1349 "<applicationSharing capture=\"true\" render=\"true\" publish=\"false\"/>"\
1350 "<contentPowerPoint capture=\"true\" render=\"true\" publish=\"false\"/>"
1351 #else
1352 #define SIPE_PUB_XML_DEVICE_APPSHARE
1353 #endif
1356 * Publishes 'device' category.
1357 * @param instance (%u) Ex.: 1938468728
1358 * @param version (%u) Ex.: 1
1359 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1360 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1361 * @param timezone (%s) Ex.: 00:00:00+01:00
1362 * @param machineName (%s) Ex.: BOSTON-OCS07
1364 #define SIPE_PUB_XML_DEVICE \
1365 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1366 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1367 "<capabilities preferred=\"false\" uri=\"%s\">"\
1368 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1369 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1370 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1371 SIPE_PUB_XML_DEVICE_VV\
1372 SIPE_PUB_XML_DEVICE_APPSHARE\
1373 "</capabilities>"\
1374 "<timezone>%s</timezone>"\
1375 "<machineName>%s</machineName>"\
1376 "</device>"\
1377 "</publication>"
1380 * Returns 'device' XML part for publication.
1381 * Must be g_free'd after use.
1383 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1385 gchar *uri;
1386 gchar *doc;
1387 gchar *uuid = get_uuid(sipe_private);
1388 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1389 /* key is <category><instance><container> */
1390 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1391 GHashTable *tmp = g_hash_table_lookup(sipe_private->our_publications, "device");
1392 struct sipe_publication *publication = tmp ? g_hash_table_lookup(tmp, key) : NULL;
1394 g_free(key);
1396 uri = sip_uri_self(sipe_private);
1397 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1398 device_instance,
1399 publication ? publication->version : 0,
1400 uuid,
1401 uri,
1402 "00:00:00+01:00", /* @TODO make timezone real*/
1403 g_get_host_name()
1406 g_free(uri);
1407 g_free(uuid);
1409 return doc;
1413 * Publishes 'machineState' category.
1414 * @param instance (%u) Ex.: 926460663
1415 * @param version (%u) Ex.: 22
1416 * @param availability (%d) Ex.: 3500
1417 * @param instance (%u) Ex.: 926460663
1418 * @param version (%u) Ex.: 22
1419 * @param availability (%d) Ex.: 3500
1421 #define SIPE_PUB_XML_STATE_MACHINE \
1422 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1423 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1424 "<availability>%d</availability>"\
1425 "<endpointLocation/>"\
1426 "</state>"\
1427 "</publication>"\
1428 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1429 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1430 "<availability>%d</availability>"\
1431 "<endpointLocation/>"\
1432 "</state>"\
1433 "</publication>"
1436 * Publishes 'userState' category.
1437 * @param instance (%u) User. Ex.: 536870912
1438 * @param version (%u) User Container 2. Ex.: 22
1439 * @param availability (%d) User Container 2. Ex.: 15500
1440 * @param instance (%u) User. Ex.: 536870912
1441 * @param version (%u) User Container 3.Ex.: 22
1442 * @param availability (%d) User Container 3. Ex.: 15500
1444 #define SIPE_PUB_XML_STATE_USER \
1445 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1446 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1447 "<availability>%d</availability>"\
1448 "<endpointLocation/>"\
1449 "</state>"\
1450 "</publication>"\
1451 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1452 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1453 "<availability>%d</availability>"\
1454 "<endpointLocation/>"\
1455 "</state>"\
1456 "</publication>"
1459 * A service method - use
1460 * - send_publish_get_category_state_machine and
1461 * - send_publish_get_category_state_user instead.
1462 * Must be g_free'd after use.
1464 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1465 gboolean force_publish,
1466 gboolean is_user_state)
1468 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1469 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1470 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1471 /* key is <category><instance><container> */
1472 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1473 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1474 gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
1475 struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
1476 struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
1478 g_free(key_2);
1479 g_free(key_3);
1481 if (!force_publish && publication_2 && (publication_2->availability == availability))
1483 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1484 return NULL; /* nothing to update */
1487 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1488 instance,
1489 publication_2 ? publication_2->version : 0,
1490 availability,
1491 instance,
1492 publication_3 ? publication_3->version : 0,
1493 availability);
1497 * Returns 'machineState' XML part for publication.
1498 * Must be g_free'd after use.
1500 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private,
1501 gboolean force_publish)
1503 return sipe_publish_get_category_state(sipe_private, force_publish, FALSE);
1507 * Returns 'userState' XML part for publication.
1508 * Must be g_free'd after use.
1510 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private,
1511 gboolean force_publish)
1513 return sipe_publish_get_category_state(sipe_private, force_publish, TRUE);
1516 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1518 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1519 gchar *pub_machine;
1520 gchar *publications;
1522 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1524 pub_machine = sipe_publish_get_category_state_machine(sipe_private,
1525 TRUE);
1526 publications = g_strdup_printf("%s%s",
1527 pub_device,
1528 pub_machine ? pub_machine : "");
1529 g_free(pub_device);
1530 g_free(pub_machine);
1532 send_presence_publish(sipe_private, publications);
1533 g_free(publications);
1536 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1537 struct sipmsg *msg,
1538 struct transaction *trans)
1540 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1542 if (msg->response == 200 && g_str_has_prefix(contenttype, "application/vnd-microsoft-roaming-self+xml")) {
1543 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1544 } else if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1545 sipe_xml *xml;
1546 const sipe_xml *node;
1547 gchar *fault_code;
1548 GHashTable *faults;
1549 int index_our;
1550 gboolean has_device_publication = FALSE;
1552 xml = sipe_xml_parse(msg->body, msg->bodylen);
1554 /* test if version mismatch fault */
1555 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1556 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1557 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1558 g_free(fault_code);
1559 sipe_xml_free(xml);
1560 return TRUE;
1562 g_free(fault_code);
1564 /* accumulating information about faulty versions */
1565 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1566 for (node = sipe_xml_child(xml, "details/operation");
1567 node;
1568 node = sipe_xml_twin(node))
1570 const gchar *index = sipe_xml_attribute(node, "index");
1571 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1573 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1574 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1576 sipe_xml_free(xml);
1578 /* here we are parsing our own request to figure out what publication
1579 * referenced here only by index went wrong
1581 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1583 /* publication */
1584 for (node = sipe_xml_child(xml, "publications/publication"),
1585 index_our = 1; /* starts with 1 - our first publication */
1586 node;
1587 node = sipe_xml_twin(node), index_our++)
1589 gchar *idx = g_strdup_printf("%d", index_our);
1590 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1591 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1592 g_free(idx);
1594 if (sipe_strequal("device", categoryName)) {
1595 has_device_publication = TRUE;
1598 if (curVersion) { /* fault exist on this index */
1599 const gchar *container = sipe_xml_attribute(node, "container");
1600 const gchar *instance = sipe_xml_attribute(node, "instance");
1601 /* key is <category><instance><container> */
1602 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1603 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1605 if (category) {
1606 struct sipe_publication *publication =
1607 g_hash_table_lookup(category, key);
1609 SIPE_DEBUG_INFO("key is %s", key);
1611 if (publication) {
1612 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1613 key, curVersion, publication->version);
1614 /* updating publication's version to the correct one */
1615 publication->version = atoi(curVersion);
1617 } else {
1618 /* We somehow lost this category from our publications... */
1619 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1620 publication->category = g_strdup(categoryName);
1621 publication->instance = atoi(instance);
1622 publication->container = atoi(container);
1623 publication->version = atoi(curVersion);
1624 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1625 g_free, (GDestroyNotify)free_publication);
1626 g_hash_table_insert(category, g_strdup(key), publication);
1627 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1628 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1630 g_free(key);
1633 sipe_xml_free(xml);
1634 g_hash_table_destroy(faults);
1636 /* rebublishing with right versions */
1637 if (has_device_publication) {
1638 send_publish_category_initial(sipe_private);
1639 } else {
1640 sipe_ocs2007_category_publish(sipe_private, TRUE);
1643 return TRUE;
1647 * Publishes categories.
1648 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1649 * @param publications (%s) XML publications
1651 #define SIPE_SEND_PRESENCE \
1652 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1653 "<publications uri=\"%s\">"\
1654 "%s"\
1655 "</publications>"\
1656 "</publish>"
1658 static void send_presence_publish(struct sipe_core_private *sipe_private,
1659 const char *publications)
1661 gchar *uri;
1662 gchar *doc;
1663 gchar *tmp;
1664 gchar *hdr;
1666 uri = sip_uri_self(sipe_private);
1667 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1668 uri,
1669 publications);
1671 tmp = get_contact(sipe_private);
1672 hdr = g_strdup_printf("Contact: %s\r\n"
1673 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1675 sip_transport_service(sipe_private,
1676 uri,
1677 hdr,
1678 doc,
1679 process_send_presence_category_publish_response);
1681 g_free(tmp);
1682 g_free(hdr);
1683 g_free(uri);
1684 g_free(doc);
1688 * Publishes self status
1689 * based on own calendar information.
1691 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1692 SIPE_UNUSED_PARAMETER void *unused)
1694 struct sipe_calendar* cal = sipe_private->calendar;
1695 struct sipe_cal_event* event = NULL;
1696 gchar *pub_cal_working_hours = NULL;
1697 gchar *pub_cal_free_busy = NULL;
1698 gchar *pub_calendar = NULL;
1699 gchar *pub_calendar2 = NULL;
1700 gchar *pub_oof_note = NULL;
1701 const gchar *oof_note;
1702 time_t oof_start = 0;
1703 time_t oof_end = 0;
1705 if (!cal) {
1706 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1707 return;
1710 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1711 if (cal->cal_events) {
1712 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1715 if (event) {
1716 sipe_cal_event_debug(event, "publish_calendar_status_self: current event is:\n");
1717 } else {
1718 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1721 /* Logic
1722 if OOF
1723 OOF publish, Busy clean
1724 ilse if Busy
1725 OOF clean, Busy publish
1726 else
1727 OOF clean, Busy clean
1729 if (event && event->cal_status == SIPE_CAL_OOF) {
1730 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1731 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1732 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1733 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1734 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1735 } else {
1736 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1737 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1740 oof_note = sipe_ews_get_oof_note(cal);
1741 if (sipe_strequal("Scheduled", cal->oof_state)) {
1742 oof_start = cal->oof_start;
1743 oof_end = cal->oof_end;
1745 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end, FALSE);
1747 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1748 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1750 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1751 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1752 } else {
1753 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1754 pub_cal_working_hours ? pub_cal_working_hours : "",
1755 pub_cal_free_busy ? pub_cal_free_busy : "",
1756 pub_calendar ? pub_calendar : "",
1757 pub_calendar2 ? pub_calendar2 : "",
1758 pub_oof_note ? pub_oof_note : "");
1760 send_presence_publish(sipe_private, publications);
1761 g_free(publications);
1764 g_free(pub_cal_working_hours);
1765 g_free(pub_cal_free_busy);
1766 g_free(pub_calendar);
1767 g_free(pub_calendar2);
1768 g_free(pub_oof_note);
1770 /* repeat scheduling */
1771 schedule_publish_update(sipe_private, time(NULL));
1774 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
1775 gboolean force_publish)
1777 GString *publications = g_string_new("");
1778 gchar *tmp;
1780 if (force_publish || sipe_private->status_set_by_user) {
1781 tmp = sipe_publish_get_category_state_user(sipe_private,
1782 force_publish);
1783 if (tmp) {
1784 g_string_append(publications, tmp);
1785 g_free(tmp);
1789 tmp = sipe_publish_get_category_state_machine(sipe_private,
1790 force_publish);
1791 if (tmp) {
1792 g_string_append(publications, tmp);
1793 g_free(tmp);
1796 tmp = sipe_publish_get_category_note(sipe_private,
1797 sipe_private->note,
1798 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1801 force_publish);
1802 if (tmp) {
1803 g_string_append(publications, tmp);
1804 g_free(tmp);
1807 if (publications->len)
1808 send_presence_publish(sipe_private, publications->str);
1809 else
1810 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1812 g_string_free(publications, TRUE);
1815 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1817 gchar *publications = NULL;
1818 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1820 /* key is <category><instance><container> */
1821 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1822 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1823 gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
1824 struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
1825 struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
1826 g_free(key_2);
1827 g_free(key_3);
1829 #ifdef HAVE_VV
1830 if (g_hash_table_size(sipe_private->media_calls)) {
1831 guint availability = 0;
1832 const gchar *token = NULL;
1833 GList *calls = g_hash_table_get_values(sipe_private->media_calls);
1834 GList *i;
1836 if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) {
1837 availability = 6500;
1838 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1841 for (i = calls; i; i = i->next) {
1842 if (sipe_media_is_conference_call(i->data)) {
1843 availability = 7000;
1844 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1845 break;
1849 g_list_free(calls);
1851 if (token) {
1852 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1853 instance, publication_2 ? publication_2->version : 0,
1854 availability, token, availability,
1855 instance, publication_3 ? publication_3->version : 0,
1856 availability, token, availability);
1858 } else
1859 #endif
1861 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1862 instance, publication_2 ? publication_2->version : 0,
1863 instance, publication_3 ? publication_3->version : 0);
1866 if (publications) {
1867 send_presence_publish(sipe_private, publications);
1868 g_free(publications);
1872 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1873 gpointer value,
1874 GString* str)
1876 struct sipe_publication *publication = value;
1878 g_string_append_printf( str,
1879 SIPE_PUB_XML_PUBLICATION_CLEAR,
1880 publication->category,
1881 publication->instance,
1882 publication->container,
1883 publication->version,
1884 "static");
1887 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1889 GString* str;
1890 gchar *publications;
1892 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1893 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1894 return;
1897 str = g_string_new(NULL);
1898 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1899 publications = g_string_free(str, FALSE);
1901 send_presence_publish(sipe_private, publications);
1902 g_free(publications);
1905 /* key is <category><instance><container> */
1906 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1907 const gchar *key)
1909 GSList *entry;
1911 /* filling keys for our publications if not yet cached */
1912 if (!sipe_private->our_publication_keys) {
1913 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1914 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1915 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1916 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1917 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1918 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1919 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1920 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1922 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1923 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1924 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1925 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1926 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1927 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1928 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1929 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1930 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1931 SIPE_DEBUG_INFO("\tNote : %u", 0);
1932 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1934 /* device */
1935 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1936 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1938 /* state:machineState */
1939 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1940 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1941 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1942 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1944 /* state:userState */
1945 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1946 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1947 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1948 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1950 /* state:calendarState */
1951 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1952 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1953 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1954 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1956 /* state:calendarState OOF */
1957 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1958 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1959 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1960 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1962 /* state:phoneState */
1963 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1964 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1965 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1966 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1968 /* note */
1969 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1970 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1971 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1972 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1973 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1974 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1976 /* note OOF */
1977 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1978 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1979 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1980 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1981 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1982 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1984 /* calendarData:WorkingHours */
1985 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1986 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1987 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1988 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1989 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1990 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1991 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1992 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1993 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1994 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1995 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1996 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1998 /* calendarData:FreeBusy */
1999 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2000 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
2001 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2002 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
2003 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2004 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
2005 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2006 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
2007 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2008 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
2009 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2010 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
2012 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
2013 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
2016 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2018 entry = sipe_private->our_publication_keys;
2019 while (entry) {
2020 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2021 if (sipe_strequal(entry->data, key)) {
2022 return TRUE;
2024 entry = entry->next;
2026 return FALSE;
2029 static void sipe_refresh_blocked_status_cb(char *buddy_name,
2030 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
2031 struct sipe_core_private *sipe_private)
2033 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2034 gboolean blocked = (container_id == 32000);
2035 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2037 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2038 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2040 if (blocked != blocked_in_blist) {
2041 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2045 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2047 sipe_buddy_foreach(sipe_private,
2048 (GHFunc) sipe_refresh_blocked_status_cb,
2049 sipe_private);
2053 * When we receive some self (BE) NOTIFY with a new subscriber
2054 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2057 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2058 struct sipmsg *msg)
2060 gchar *contact;
2061 gchar *to;
2062 sipe_xml *xml;
2063 const sipe_xml *node;
2064 const sipe_xml *node2;
2065 char *display_name = NULL;
2066 char *uri;
2067 GSList *category_names = NULL;
2068 int aggreg_avail = 0;
2069 gchar *activity_token = NULL;
2070 gboolean do_update_status = FALSE;
2071 gboolean has_note_cleaned = FALSE;
2072 GHashTable *devices;
2074 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2076 xml = sipe_xml_parse(msg->body, msg->bodylen);
2077 if (!xml) return;
2079 contact = get_contact(sipe_private);
2080 to = sip_uri_self(sipe_private);
2082 /* categories */
2083 /* set list of categories participating in this XML */
2084 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2085 const gchar *name = sipe_xml_attribute(node, "name");
2086 category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2087 (gchar *)name,
2088 (GCompareFunc)strcmp,
2089 NULL);
2091 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2092 category_names ? (int) g_slist_length(category_names) : -1);
2093 /* drop category information */
2094 if (category_names) {
2095 GSList *entry = category_names;
2096 while (entry) {
2097 GHashTable *cat_publications;
2098 const gchar *category = entry->data;
2099 entry = entry->next;
2100 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2101 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2102 if (cat_publications) {
2103 g_hash_table_remove(sipe_private->our_publications, category);
2104 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2108 g_slist_free(category_names);
2110 /* filling our categories reflected in roaming data */
2111 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2112 g_free, NULL);
2113 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2114 const char *tmp;
2115 const gchar *name = sipe_xml_attribute(node, "name");
2116 guint container = sipe_xml_int_attribute(node, "container", -1);
2117 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2118 guint version = sipe_xml_int_attribute(node, "version", 0);
2119 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2120 sipe_utils_str_to_time(tmp) : 0;
2121 gchar *key;
2122 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2124 /* Ex. clear note: <category name="note"/> */
2125 if (container == (guint)-1) {
2126 g_free(sipe_private->note);
2127 sipe_private->note = NULL;
2128 do_update_status = TRUE;
2129 continue;
2132 /* Ex. clear note: <category name="note" container="200"/> */
2133 if (instance == (guint)-1) {
2134 if (container == 200) {
2135 g_free(sipe_private->note);
2136 sipe_private->note = NULL;
2137 do_update_status = TRUE;
2139 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2140 sipe_remove_category_container_publications(
2141 sipe_private->our_publications, name, container);
2142 continue;
2145 /* key is <category><instance><container> */
2146 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2147 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2149 /* capture all userState publication for later clean up if required */
2150 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2151 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2153 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2154 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2155 publication->category = g_strdup(name);
2156 publication->instance = instance;
2157 publication->container = container;
2158 publication->version = version;
2160 if (!sipe_private->user_state_publications) {
2161 sipe_private->user_state_publications = g_hash_table_new_full(
2162 g_str_hash, g_str_equal,
2163 g_free, (GDestroyNotify)free_publication);
2165 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2166 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2167 key, version);
2171 /* count each client instance only once */
2172 if (sipe_strequal(name, "device"))
2173 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2175 if (sipe_is_our_publication(sipe_private, key)) {
2176 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2178 publication->category = g_strdup(name);
2179 publication->instance = instance;
2180 publication->container = container;
2181 publication->version = version;
2183 /* filling publication->availability */
2184 if (sipe_strequal(name, "state")) {
2185 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2186 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2188 if (xn_avail) {
2189 gchar *avail_str = sipe_xml_data(xn_avail);
2190 if (avail_str) {
2191 publication->availability = atoi(avail_str);
2193 g_free(avail_str);
2195 /* for calendarState */
2196 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2197 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2198 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2200 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2201 if (xn_activity) {
2202 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2203 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2205 event->is_meeting = TRUE;
2208 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2209 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2211 publication->cal_event_hash = sipe_cal_event_hash(event);
2212 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2213 publication->cal_event_hash);
2214 sipe_cal_event_free(event);
2217 /* filling publication->note */
2218 if (sipe_strequal(name, "note")) {
2219 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2221 if (!has_note_cleaned) {
2222 has_note_cleaned = TRUE;
2224 g_free(sipe_private->note);
2225 sipe_private->note = NULL;
2226 sipe_private->note_since = publish_time;
2228 do_update_status = TRUE;
2231 g_free(publication->note);
2232 publication->note = NULL;
2233 if (xn_body) {
2234 char *tmp;
2236 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2237 g_free(tmp);
2238 if (publish_time >= sipe_private->note_since) {
2239 g_free(sipe_private->note);
2240 sipe_private->note = g_strdup(publication->note);
2241 sipe_private->note_since = publish_time;
2242 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2243 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2244 else
2245 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2247 do_update_status = TRUE;
2252 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2253 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2254 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2255 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2256 if (xn_free_busy) {
2257 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2258 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2260 if (xn_working_hours) {
2261 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2265 if (!cat_publications) {
2266 cat_publications = g_hash_table_new_full(
2267 g_str_hash, g_str_equal,
2268 g_free, (GDestroyNotify)free_publication);
2269 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2270 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2272 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2273 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2275 g_free(key);
2277 /* aggregateState (not an our publication) from 2-nd container */
2278 if (sipe_strequal(name, "state") && container == 2) {
2279 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2280 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2282 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2283 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2285 if (xn_avail) {
2286 gchar *avail_str = sipe_xml_data(xn_avail);
2287 if (avail_str) {
2288 aggreg_avail = atoi(avail_str);
2290 g_free(avail_str);
2293 do_update_status = TRUE;
2296 if (xn_activity) {
2297 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2301 /* userProperties published by server from AD */
2302 if (!sipe_private->csta &&
2303 sipe_strequal(name, "userProperties")) {
2304 const sipe_xml *line;
2305 /* line, for Remote Call Control (RCC) or external Lync/Communicator call */
2306 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2307 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2308 gchar *line_uri = sipe_xml_data(line);
2309 if (!line_uri) {
2310 continue;
2313 if (sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual")) {
2314 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2315 if (line_server) {
2316 gchar *tmp = g_strstrip(line_uri);
2317 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s",
2318 tmp, line_server);
2319 sip_csta_open(sipe_private, tmp, line_server);
2322 #ifdef HAVE_VV
2323 else if (sipe_strequal(line_type, "Uc")) {
2325 if (!sipe_private->uc_line_uri) {
2326 sipe_private->uc_line_uri = g_strdup(g_strstrip(line_uri));
2327 } else {
2328 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: "
2329 "sipe_private->uc_line_uri is already set.");
2332 #endif
2334 g_free(line_uri);
2336 break;
2340 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2341 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2343 /* active clients for user account */
2344 if (g_hash_table_size(devices) == 0) {
2345 /* updated roaming information without device information - no need to update MPOP flag */
2346 } else if (g_hash_table_size(devices) > 1) {
2347 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2348 SIPE_LOG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2349 g_hash_table_size(devices));
2350 } else {
2351 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2352 SIPE_LOG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2354 g_hash_table_destroy(devices);
2356 /* containers */
2357 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2358 guint id = sipe_xml_int_attribute(node, "id", 0);
2359 struct sipe_container *container = sipe_find_container(sipe_private, id);
2361 if (container) {
2362 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2363 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2364 sipe_ocs2007_free_container(container);
2366 container = g_new0(struct sipe_container, 1);
2367 container->id = id;
2368 container->version = sipe_xml_int_attribute(node, "version", 0);
2369 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2370 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2372 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2373 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2374 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2375 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2376 container->members = g_slist_append(container->members, member);
2377 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2378 member->type, member->value ? member->value : "");
2382 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2383 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2384 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2385 char *container_xmls = NULL;
2386 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2387 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2389 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2390 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2391 /* initial set-up to let counterparties see your status */
2392 if (sameEnterpriseAL < 0) {
2393 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2394 guint version = container ? container->version : 0;
2395 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2397 if (federatedAL < 0) {
2398 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2399 guint version = container ? container->version : 0;
2400 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2402 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2404 if (container_xmls) {
2405 sipe_send_set_container_members(sipe_private, container_xmls);
2407 g_free(container_xmls);
2410 /* Refresh contacts' blocked status */
2411 sipe_refresh_blocked_status(sipe_private);
2413 /* subscribers */
2414 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2415 const char *user;
2416 const char *acknowledged;
2417 gchar *hdr;
2418 gchar *body;
2420 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2421 if (!user) continue;
2422 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2423 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2424 uri = sip_uri_from_name(user);
2426 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2427 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2429 acknowledged= sipe_xml_attribute(node, "acknowledged");
2430 if(sipe_strcase_equal(acknowledged,"false")){
2431 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2432 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2433 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2436 hdr = g_strdup_printf(
2437 "Contact: %s\r\n"
2438 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2440 body = g_strdup_printf(
2441 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2442 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2443 "</setSubscribers>", user);
2445 sip_transport_service(sipe_private,
2447 hdr,
2448 body,
2449 NULL);
2450 g_free(body);
2451 g_free(hdr);
2453 g_free(display_name);
2454 g_free(uri);
2457 g_free(contact);
2458 sipe_xml_free(xml);
2460 /* Publish initial state if not yet.
2461 * Assuming this happens on initial responce to subscription to roaming-self
2462 * so we've already updated our roaming data in full.
2463 * Only for 2007+
2465 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2466 send_publish_category_initial(sipe_private);
2467 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2468 /* dalayed run */
2469 sipe_cal_delayed_calendar_update(sipe_private);
2470 do_update_status = FALSE;
2471 } else if (aggreg_avail) {
2473 if (aggreg_avail &&
2474 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2475 /* not offline */
2476 sipe_status_set_token(sipe_private,
2477 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2478 } else {
2479 /* do not let offline status switch us off */
2480 sipe_status_set_activity(sipe_private,
2481 SIPE_ACTIVITY_INVISIBLE);
2485 if (do_update_status) {
2486 sipe_status_and_note(sipe_private, NULL);
2489 g_free(to);
2490 g_free(activity_token);
2494 * for Access levels menu
2496 #define INDENT_FMT " %s"
2499 * Member is indirectly belong to access level container.
2500 * For example 'sameEnterprise' is in the container and user
2501 * belongs to that same enterprise.
2503 #define INDENT_MARKED_INHERITED_FMT "= %s"
2505 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2506 struct sipe_backend_buddy_menu *menu,
2507 const gchar *member_type,
2508 const gchar *member_value,
2509 const gboolean extra_menu)
2511 unsigned int i;
2512 gboolean is_group_access = FALSE;
2513 int container_id;
2515 if (!menu)
2516 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2518 container_id = sipe_ocs2007_find_access_level(sipe_private,
2519 member_type,
2520 member_value,
2521 &is_group_access);
2523 for (i = 1; i <= CONTAINERS_LEN; i++) {
2525 * Blocked should remain in the first place
2526 * in the containers[] array.
2528 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2529 int container_j = containers[j];
2530 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2531 struct sipe_container *container = create_container(j,
2532 member_type,
2533 member_value,
2534 FALSE);
2535 gchar *label;
2537 /* libpurple memory leak workaround */
2538 blist_menu_remember_container(sipe_private, container);
2540 /* current container/access level */
2541 if (container_j == container_id) {
2542 label = is_group_access ?
2543 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2544 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2545 } else {
2546 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2549 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2550 menu,
2551 label,
2552 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2553 container);
2554 g_free(label);
2557 if (extra_menu && (container_id >= 0) && !is_group_access) {
2558 struct sipe_container *container = create_container(0,
2559 member_type,
2560 member_value,
2561 TRUE);
2562 gchar *label;
2564 /* separator */
2565 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2566 menu,
2567 " --------------");
2570 /* libpurple memory leak workaround */
2571 blist_menu_remember_container(sipe_private, container);
2573 /* Translators: remove (clear) previously assigned access level */
2574 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2575 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2576 menu,
2577 label,
2578 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2579 container);
2580 g_free(label);
2583 return(menu);
2586 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2588 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2589 GSList *access_domains, *entry;
2591 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2592 menu,
2593 _("People in my company"),
2594 access_levels_menu(sipe_private,
2595 NULL,
2596 "sameEnterprise",
2597 NULL,
2598 FALSE));
2600 /* this is original name, don't edit */
2601 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2602 menu,
2603 _("People in domains connected with my company"),
2604 access_levels_menu(sipe_private,
2605 NULL,
2606 "federated",
2607 NULL,
2608 FALSE));
2610 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2611 menu,
2612 _("People in public domains"),
2613 access_levels_menu(sipe_private,
2614 NULL,
2615 "publicCloud",
2616 NULL,
2617 TRUE));
2619 entry = access_domains = get_access_domains(sipe_private);
2620 while (entry) {
2621 gchar *domain = entry->data;
2622 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2624 /* takes over ownership of entry->data (= domain) */
2625 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2626 menu,
2627 menu_name,
2628 access_levels_menu(sipe_private,
2629 NULL,
2630 "domain",
2631 domain,
2632 TRUE));
2633 g_free(menu_name);
2635 entry = entry->next;
2637 g_slist_free(access_domains);
2639 /* separator */
2640 /* People in domains connected with my company */
2641 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2642 menu,
2643 "-------------------------------------------");
2645 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2646 menu,
2647 _("Add new domain..."),
2648 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2649 NULL);
2651 return(menu);
2654 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2655 const gchar *buddy_name)
2657 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2658 gchar *label;
2661 * Workaround for missing libpurple API to release resources allocated
2662 * during blist_node_menu() callback. See also:
2664 * <http://developer.pidgin.im/ticket/12597>
2666 * We remember all memory blocks in a list and deallocate them when
2668 * - the next time we enter the callback, or
2669 * - the account is disconnected
2671 * That means that after the buddy menu has been closed we have unused
2672 * resources but at least we don't leak them anymore...
2674 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2676 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2677 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2678 menu,
2679 label,
2680 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2681 NULL);
2682 g_free(label);
2684 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2685 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2686 menu,
2687 label,
2688 access_groups_menu(sipe_private));
2689 g_free(label);
2691 menu = access_levels_menu(sipe_private,
2692 menu,
2693 "user",
2694 sipe_get_no_sip_uri(buddy_name),
2695 TRUE);
2697 return(menu);
2701 Local Variables:
2702 mode: c
2703 c-file-style: "bsd"
2704 indent-tabs-mode: t
2705 tab-width: 8
2706 End: