purple: fix build when appshare server is disabled
[siplcs.git] / src / core / sipe-ocs2007.c
blob9f388ca577625984818add2209fba637dc7167a6
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2018 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-appshare.h"
48 #include "sipe-ews.h"
49 #include "sipe-media.h"
50 #include "sipe-nls.h"
51 #include "sipe-ocs2007.h"
52 #include "sipe-schedule.h"
53 #include "sipe-status.h"
54 #include "sipe-utils.h"
55 #include "sipe-xml.h"
57 /** MS-PRES publication */
58 struct sipe_publication {
59 gchar *category;
60 guint instance;
61 guint container;
62 guint version;
63 /** for 'state' category */
64 int availability;
65 /** for 'state:calendarState' category */
66 char *cal_event_hash;
67 /** for 'note' category */
68 gchar *note;
69 /** for 'calendarData' category; 300(Team) container */
70 char *working_hours_xml_str;
71 char *fb_start_str;
72 char *free_busy_base64;
75 /**
76 * 2007-style Activity and Availability.
78 * [MS-PRES] 3.7.5.5
80 * Conversion of legacyInterop availability ranges and activity tokens into
81 * SIPE activity tokens. The descriptions of availability ranges are defined at:
83 * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
85 * The values define the starting point of a range.
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000
94 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
96 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
97 const gchar *activity)
99 guint type;
101 if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
102 type = SIPE_ACTIVITY_OFFLINE;
103 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
104 type = SIPE_ACTIVITY_AVAILABLE;
105 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
106 type = SIPE_ACTIVITY_INACTIVE;
107 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
108 type = sipe_status_token_to_activity(activity);
109 if ((type != SIPE_ACTIVITY_ON_PHONE) &&
110 (type != SIPE_ACTIVITY_IN_CONF))
111 type = SIPE_ACTIVITY_BUSY;
112 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
113 type = SIPE_ACTIVITY_BUSYIDLE;
114 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
115 type = sipe_status_token_to_activity(activity);
116 if (type != SIPE_ACTIVITY_IN_PRES) {
117 type = SIPE_ACTIVITY_DND;
119 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
120 type = SIPE_ACTIVITY_BRB;
121 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
122 type = SIPE_ACTIVITY_AWAY;
123 } else {
124 type = SIPE_ACTIVITY_OFFLINE;
127 return sipe_status_activity_to_token(type);
130 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
132 if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
133 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
134 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
135 } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
136 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
137 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
138 } else {
139 return(NULL);
144 * @param sipe_status_id (in)
145 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
146 * requests this token]
148 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
149 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
150 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
151 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
152 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
153 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
154 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
155 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
156 const gchar **activity_token)
158 guint availability;
159 guint activity;
161 if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
162 availability = SIPE_OCS2007_AVAILABILITY_AWAY;
163 activity = SIPE_ACTIVITY_AWAY;
164 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
165 availability = SIPE_OCS2007_AVAILABILITY_BRB;
166 activity = SIPE_ACTIVITY_BRB;
167 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
168 availability = SIPE_OCS2007_AVAILABILITY_DND;
169 activity = SIPE_ACTIVITY_DND;
170 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
171 availability = SIPE_OCS2007_AVAILABILITY_BUSY;
172 activity = SIPE_ACTIVITY_BUSY;
173 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
174 availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
175 activity = SIPE_ACTIVITY_ONLINE;
176 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
177 availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
178 activity = SIPE_ACTIVITY_UNSET;
179 } else {
180 /* Offline or invisible */
181 availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
182 activity = SIPE_ACTIVITY_OFFLINE;
185 if (activity_token) {
186 *activity_token = sipe_status_activity_to_token(activity);
189 return(availability);
192 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
194 return(SIPE_OCS2007_AVAILABILITY_BUSY >=
195 sipe_ocs2007_availability_from_status(status_id, NULL));
199 gboolean sipe_ocs2007_availability_is_away(guint availability)
201 return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
204 static void send_presence_publish(struct sipe_core_private *sipe_private,
205 const char *publications);
207 static void free_publication(struct sipe_publication *publication)
209 g_free(publication->category);
210 g_free(publication->cal_event_hash);
211 g_free(publication->note);
213 g_free(publication->working_hours_xml_str);
214 g_free(publication->fb_start_str);
215 g_free(publication->free_busy_base64);
217 g_free(publication);
220 struct hash_table_delete_payload {
221 GHashTable *hash_table;
222 guint container;
225 static void sipe_remove_category_container_publications_cb(const gchar *name,
226 struct sipe_publication *publication,
227 struct hash_table_delete_payload *payload)
229 if (publication->container == payload->container) {
230 g_hash_table_remove(payload->hash_table, name);
234 static void sipe_remove_category_container_publications(GHashTable *our_publications,
235 const gchar *category,
236 guint container)
238 struct hash_table_delete_payload payload;
239 payload.hash_table = g_hash_table_lookup(our_publications, category);
241 if (!payload.hash_table) return;
243 payload.container = container;
244 g_hash_table_foreach(payload.hash_table,
245 (GHFunc)sipe_remove_category_container_publications_cb,
246 &payload);
249 /** MS-PRES container */
250 struct sipe_container {
251 guint id;
252 guint version;
253 GSList *members;
256 /** MS-PRES container member */
257 struct sipe_container_member {
258 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
259 gchar *type;
260 gchar *value;
263 static const guint containers[] = {32000, 400, 300, 200, 100};
264 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
266 static void free_container_member(struct sipe_container_member *member)
268 if (!member) return;
270 g_free(member->type);
271 g_free(member->value);
272 g_free(member);
275 static void sipe_ocs2007_free_container(struct sipe_container *container)
277 GSList *entry;
279 if (!container) return;
281 entry = container->members;
282 while (entry) {
283 void *data = entry->data;
284 entry = g_slist_remove(entry, data);
285 free_container_member((struct sipe_container_member *)data);
287 g_free(container);
290 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
292 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
293 sipe_utils_slist_free_full(sipe_private->blist_menu_containers,
294 (GDestroyNotify) sipe_ocs2007_free_container);
295 sipe_private->blist_menu_containers = NULL;
298 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
299 struct sipe_container *container)
301 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
302 container);
305 static struct sipe_container *create_container(guint index,
306 const gchar *member_type,
307 const gchar *member_value,
308 gboolean is_group)
310 struct sipe_container *container = g_new0(struct sipe_container, 1);
311 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
313 container->id = is_group ? (guint) -1 : containers[index];
314 container->members = g_slist_append(container->members, member);
315 member->type = g_strdup(member_type);
316 member->value = g_strdup(member_value);
318 return(container);
321 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
323 sipe_utils_slist_free_full(sipe_private->containers,
324 (GDestroyNotify) sipe_ocs2007_free_container);
328 * Finds locally stored MS-PRES container member
330 static struct sipe_container_member *
331 sipe_find_container_member(struct sipe_container *container,
332 const gchar *type,
333 const gchar *value)
335 struct sipe_container_member *member;
336 GSList *entry;
338 if (container == NULL || type == NULL) {
339 return NULL;
342 entry = container->members;
343 while (entry) {
344 member = entry->data;
345 if (sipe_strcase_equal(member->type, type) &&
346 sipe_strcase_equal(member->value, value))
348 return member;
350 entry = entry->next;
352 return NULL;
356 * Finds locally stored MS-PRES container by id
358 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
359 guint id)
361 GSList *entry = sipe_private->containers;
362 while (entry) {
363 struct sipe_container *container = entry->data;
364 if (id == container->id) {
365 return container;
367 entry = entry->next;
369 return NULL;
372 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
373 const gchar *type,
374 const gchar *value)
376 unsigned int i = 0;
377 const gchar *value_mod = value;
379 if (!type) return -1;
381 if (sipe_strequal("user", type)) {
382 value_mod = sipe_get_no_sip_uri(value);
385 for (i = 0; i < CONTAINERS_LEN; i++) {
386 struct sipe_container_member *member;
387 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
388 if (!container) continue;
390 member = sipe_find_container_member(container, type, value_mod);
391 if (member) return containers[i];
394 return -1;
398 * Returns pointer to domain part in provided Email URL
400 * @param email an email URL. Example: first.last@hq.company.com
401 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
403 * Doesn't allocate memory
405 static const gchar *sipe_get_domain(const gchar *email)
407 gchar *tmp;
409 if (!email) return NULL;
411 tmp = strstr(email, "@");
413 if (tmp && ((tmp+1) < (email + strlen(email)))) {
414 return tmp+1;
415 } else {
416 return NULL;
420 /* @TODO: replace with binary search for faster access? */
421 /** source: http://support.microsoft.com/kb/897567 */
422 static const gchar * const public_domains[] = {
423 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
424 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
425 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
426 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
427 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
428 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
429 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
430 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
431 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
432 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
433 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
434 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
435 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
436 "yahoo.com",
437 NULL};
439 static gboolean sipe_is_public_domain(const gchar *domain)
441 int i = 0;
442 while (public_domains[i]) {
443 if (sipe_strcase_equal(public_domains[i], domain)) {
444 return TRUE;
446 i++;
448 return FALSE;
452 * Access Levels
453 * 32000 - Blocked
454 * 400 - Personal
455 * 300 - Team
456 * 200 - Company
457 * 100 - Public
459 const gchar *sipe_ocs2007_access_level_name(guint id)
461 switch (id) {
462 case 32000: return _("Blocked");
463 case 400: return _("Personal");
464 case 300: return _("Team");
465 case 200: return _("Company");
466 case 100: return _("Public");
468 return _("Unknown");
471 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
472 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
473 const gchar *type,
474 const gchar *value,
475 gboolean *is_group_access)
477 int container_id = -1;
479 if (sipe_strequal("user", type)) {
480 const char *domain;
481 const char *no_sip_uri = sipe_get_no_sip_uri(value);
483 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
484 if (container_id >= 0) {
485 if (is_group_access) *is_group_access = FALSE;
486 return container_id;
489 domain = sipe_get_domain(no_sip_uri);
490 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
491 if (container_id >= 0) {
492 if (is_group_access) *is_group_access = TRUE;
493 return container_id;
496 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
497 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
498 if (is_group_access) *is_group_access = TRUE;
499 return container_id;
502 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
503 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
504 if (is_group_access) *is_group_access = TRUE;
505 return container_id;
508 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
509 if ((container_id >= 0)) {
510 if (is_group_access) *is_group_access = TRUE;
511 return container_id;
513 } else {
514 container_id = sipe_find_member_access_level(sipe_private, type, value);
515 if (is_group_access) *is_group_access = FALSE;
518 return container_id;
521 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
523 struct sipe_container *container;
524 struct sipe_container_member *member;
525 GSList *entry;
526 GSList *entry2;
527 GSList *res = NULL;
529 entry = sipe_private->containers;
530 while (entry) {
531 container = entry->data;
533 entry2 = container->members;
534 while (entry2) {
535 member = entry2->data;
536 if (sipe_strcase_equal(member->type, "domain"))
538 res = sipe_utils_slist_insert_unique_sorted(res,
539 g_strdup(member->value),
540 (GCompareFunc)g_ascii_strcasecmp,
541 g_free);
543 entry2 = entry2->next;
545 entry = entry->next;
547 return res;
550 static void sipe_send_container_members_prepare(const guint container_id,
551 const guint container_version,
552 const gchar *action,
553 const gchar *type,
554 const gchar *value,
555 char **container_xmls)
557 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
558 gchar *body;
560 if (!container_xmls) return;
562 body = g_strdup_printf(
563 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
564 container_id,
565 container_version,
566 action,
567 type,
568 value_str);
569 g_free(value_str);
571 if ((*container_xmls) == NULL) {
572 *container_xmls = body;
573 } else {
574 char *tmp = *container_xmls;
576 *container_xmls = g_strconcat(*container_xmls, body, NULL);
577 g_free(tmp);
578 g_free(body);
582 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
583 char *container_xmls)
585 gchar *self;
586 gchar *contact;
587 gchar *hdr;
588 gchar *body;
590 if (!container_xmls) return;
592 self = sip_uri_self(sipe_private);
593 body = g_strdup_printf(
594 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
595 "%s"
596 "</setContainerMembers>",
597 container_xmls);
599 contact = get_contact(sipe_private);
600 hdr = g_strdup_printf("Contact: %s\r\n"
601 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
602 g_free(contact);
604 sip_transport_service(sipe_private,
605 self,
606 hdr,
607 body,
608 NULL);
610 g_free(hdr);
611 g_free(body);
612 g_free(self);
616 * @param container_id a new access level. If -1 then current access level
617 * is just removed (I.e. the member is removed from all containers).
618 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
619 * @param value a value for member. E.g. SIP URI for "user" member type.
621 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
622 const int container_id,
623 const gchar *type,
624 const gchar *value)
626 unsigned int i;
627 int current_container_id = -1;
628 char *container_xmls = NULL;
630 /* for each container: find/delete */
631 for (i = 0; i < CONTAINERS_LEN; i++) {
632 struct sipe_container_member *member;
633 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
635 if (!container) continue;
637 member = sipe_find_container_member(container, type, value);
638 if (member) {
639 current_container_id = containers[i];
640 /* delete/publish current access level */
641 if (container_id < 0 || container_id != current_container_id) {
642 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
643 /* remove member from our cache, to be able to recalculate AL below */
644 container->members = g_slist_remove(container->members, member);
649 /* recalculate AL below */
650 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
652 /* assign/publish new access level */
653 if (container_id != current_container_id && container_id >= 0) {
654 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
655 guint version = container ? container->version : 0;
657 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
660 if (container_xmls) {
661 sipe_send_set_container_members(sipe_private, container_xmls);
663 g_free(container_xmls);
666 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
667 gpointer parameter)
669 struct sipe_container *container = parameter;
670 struct sipe_container_member *member;
672 if (!container || !container->members) return;
674 member = ((struct sipe_container_member *)container->members->data);
676 if (!member->type) return;
678 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
679 container->id, member->type, member->value ? member->value : "");
681 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
682 container->id,
683 member->type,
684 member->value);
688 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
689 const gchar *domain,
690 guint index)
692 /* move Blocked first */
693 guint i = (index == 4) ? 0 : index + 1;
694 guint container_id = containers[i];
696 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
697 domain ? domain : "", index, container_id);
699 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
700 container_id,
701 "domain",
702 domain);
706 * Schedules process of self status publish
707 * based on own calendar information.
708 * Should be scheduled to the beginning of every
709 * 15 min interval, like:
710 * 13:00, 13:15, 13:30, 13:45, etc.
713 static void schedule_publish_update(struct sipe_core_private *sipe_private,
714 time_t calculate_from)
716 int interval = 5*60;
717 /** start of the beginning of closest 5 min interval. */
718 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
720 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
721 sipe_utils_time_to_debug_str(localtime(&calculate_from)));
722 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
723 sipe_utils_time_to_debug_str(localtime(&next_start)));
725 sipe_schedule_seconds(sipe_private,
726 "<+2007-cal-status>",
727 NULL,
728 next_start - time(NULL),
729 sipe_ocs2007_presence_publish,
730 NULL);
734 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
735 * @param availability (%d) Ex.: 6500
737 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
738 "<availability>%d</availability>"
740 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
741 * @param token (%s) Ex.: in-a-meeting
742 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
743 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
745 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
746 "<activity token=\"%s\" %s %s></activity>"
748 * Publishes 'calendarState' category.
749 * @param instance (%u) Ex.: 1339299275
750 * @param version (%u) Ex.: 1
751 * @param uri (%s) Ex.: john@contoso.com
752 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
753 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
754 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
755 * @param meeting_subject (%s) Ex.: Customer Meeting
756 * @param meeting_location (%s) Ex.: Conf Room 100
758 * @param instance (%u) Ex.: 1339299275
759 * @param version (%u) Ex.: 1
760 * @param uri (%s) Ex.: john@contoso.com
761 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
762 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
763 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
764 * @param meeting_subject (%s) Ex.: Customer Meeting
765 * @param meeting_location (%s) Ex.: Conf Room 100
767 #define SIPE_PUB_XML_STATE_CALENDAR \
768 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
769 "<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\">"\
770 "%s"\
771 "%s"\
772 "<endpointLocation/>"\
773 "<meetingSubject>%s</meetingSubject>"\
774 "<meetingLocation>%s</meetingLocation>"\
775 "</state>"\
776 "</publication>"\
777 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
778 "<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\">"\
779 "%s"\
780 "%s"\
781 "<endpointLocation/>"\
782 "<meetingSubject>%s</meetingSubject>"\
783 "<meetingLocation>%s</meetingLocation>"\
784 "</state>"\
785 "</publication>"
787 * Publishes to clear 'calendarState' and 'phoneState' category
788 * @param instance (%u) Ex.: 1251210982
789 * @param version (%u) Ex.: 1
791 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
792 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
793 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
796 * Publishes to clear any category
797 * @param category_name (%s) Ex.: state
798 * @param instance (%u) Ex.: 536870912
799 * @param container (%u) Ex.: 3
800 * @param version (%u) Ex.: 1
801 * @param expireType (%s) Ex.: static
803 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
804 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
807 * Publishes 'note' category.
808 * @param instance (%u) Ex.: 2135971629; 0 for personal
809 * @param container (%u) Ex.: 200
810 * @param version (%u) Ex.: 2
811 * @param type (%s) Ex.: personal or OOF
812 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
813 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
814 * @param body (%s) Ex.: In the office
816 #define SIPE_PUB_XML_NOTE \
817 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
818 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
819 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
820 "</note>"\
821 "</publication>"
823 * Publishes 'phoneState' category.
824 * @param instance (%u) Ex.: 1339299275
825 * @param version (%u) Ex.: 1
826 * @param availability (%u) Ex.: 6500
827 * @param token (%s) Ex.: on-the-phone
828 * @param minAvailability (%u) generally same as availability
830 * @param instance (%u) Ex.: 1339299275
831 * @param version (%u) Ex.: 1
832 * @param availability (%u) Ex.: 6500
833 * @param token (%s) Ex.: on-the-phone
834 * @param minAvailability (%u) generally same as availability
836 #define SIPE_PUB_XML_STATE_PHONE \
837 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
838 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
839 "<availability>%u</availability>"\
840 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"%u\"/>"\
841 "</state>"\
842 "</publication>"\
843 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
844 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
845 "<availability>%u</availability>"\
846 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"%u\"/>"\
847 "</state>"\
848 "</publication>"
851 * Only Busy and OOF calendar event are published.
852 * Different instances are used for that.
854 * Must be g_free'd after use.
856 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
857 struct sipe_cal_event *event,
858 const char *uri,
859 int cal_satus)
861 gchar *start_time_str;
862 int availability = 0;
863 gchar *res;
864 gchar *tmp = NULL;
865 guint instance = (cal_satus == SIPE_CAL_OOF) ?
866 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
867 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
869 /* key is <category><instance><container> */
870 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
871 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
872 gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
873 struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
874 struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
876 g_free(key_2);
877 g_free(key_3);
879 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
880 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
881 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
882 return NULL;
885 if (event &&
886 publication_3 &&
887 (publication_3->availability == availability) &&
888 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
890 g_free(tmp);
891 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
892 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
893 return NULL; /* nothing to update */
895 g_free(tmp);
897 if (event &&
898 (event->cal_status == SIPE_CAL_BUSY ||
899 event->cal_status == SIPE_CAL_OOF))
901 gchar *availability_xml_str = NULL;
902 gchar *activity_xml_str = NULL;
903 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
904 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
906 if (event->cal_status == SIPE_CAL_BUSY) {
907 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
908 SIPE_OCS2007_AVAILABILITY_BUSY);
911 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
912 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
913 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
914 "minAvailability=\"6500\"",
915 "maxAvailability=\"8999\"");
916 } else if (event->cal_status == SIPE_CAL_OOF) {
917 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
918 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
919 "minAvailability=\"12000\"",
920 "");
922 start_time_str = sipe_utils_time_to_str(event->start_time);
924 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
925 instance,
926 publication_2 ? publication_2->version : 0,
927 uri,
928 start_time_str,
929 availability_xml_str ? availability_xml_str : "",
930 activity_xml_str ? activity_xml_str : "",
931 escaped_subject ? escaped_subject : "",
932 escaped_location ? escaped_location : "",
934 instance,
935 publication_3 ? publication_3->version : 0,
936 uri,
937 start_time_str,
938 availability_xml_str ? availability_xml_str : "",
939 activity_xml_str ? activity_xml_str : "",
940 escaped_subject ? escaped_subject : "",
941 escaped_location ? escaped_location : ""
943 g_free(escaped_location);
944 g_free(escaped_subject);
945 g_free(start_time_str);
946 g_free(availability_xml_str);
947 g_free(activity_xml_str);
950 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
952 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
953 instance,
954 publication_2 ? publication_2->version : 0,
956 instance,
957 publication_3 ? publication_3->version : 0
961 return res;
965 * Returns 'note' XML part for publication.
966 * Must be g_free'd after use.
968 * Protocol format for Note is plain text.
970 * @param note a note in Sipe internal HTML format
971 * @param note_type either personal or OOF
973 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
974 const char *note, /* html */
975 const char *note_type,
976 time_t note_start,
977 time_t note_end,
978 gboolean force_publish)
980 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
981 /* key is <category><instance><container> */
982 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
983 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
984 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
986 gpointer notes = g_hash_table_lookup(sipe_private->our_publications, "note");
987 struct sipe_publication *publication_note_200 = notes ? g_hash_table_lookup(notes, key_note_200) : NULL;
988 struct sipe_publication *publication_note_300 = notes ? g_hash_table_lookup(notes, key_note_300) : NULL;
989 struct sipe_publication *publication_note_400 = notes ? g_hash_table_lookup(notes, key_note_400) : NULL;
991 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
992 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
993 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
994 char *res, *tmp1, *tmp2, *tmp3;
995 char *start_time_attr;
996 char *end_time_attr;
998 g_free(tmp);
999 tmp = NULL;
1000 g_free(key_note_200);
1001 g_free(key_note_300);
1002 g_free(key_note_400);
1004 /* we even need to republish empty note */
1005 if (!force_publish && sipe_strequal(n1, n2))
1007 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1008 g_free(n1);
1009 return NULL; /* nothing to update */
1012 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1013 g_free(tmp);
1014 tmp = NULL;
1015 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1016 g_free(tmp);
1018 if (n1) {
1019 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1020 instance,
1021 200,
1022 publication_note_200 ? publication_note_200->version : 0,
1023 note_type,
1024 start_time_attr ? start_time_attr : "",
1025 end_time_attr ? end_time_attr : "",
1026 n1);
1028 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1029 instance,
1030 300,
1031 publication_note_300 ? publication_note_300->version : 0,
1032 note_type,
1033 start_time_attr ? start_time_attr : "",
1034 end_time_attr ? end_time_attr : "",
1035 n1);
1037 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1038 instance,
1039 400,
1040 publication_note_400 ? publication_note_400->version : 0,
1041 note_type,
1042 start_time_attr ? start_time_attr : "",
1043 end_time_attr ? end_time_attr : "",
1044 n1);
1045 } else {
1046 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1047 "note",
1048 instance,
1049 200,
1050 publication_note_200 ? publication_note_200->version : 0,
1051 "static");
1052 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1053 "note",
1054 instance,
1055 300,
1056 publication_note_200 ? publication_note_200->version : 0,
1057 "static");
1058 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1059 "note",
1060 instance,
1061 400,
1062 publication_note_200 ? publication_note_200->version : 0,
1063 "static");
1065 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
1067 g_free(start_time_attr);
1068 g_free(end_time_attr);
1069 g_free(tmp1);
1070 g_free(tmp2);
1071 g_free(tmp3);
1072 g_free(n1);
1074 return res;
1078 * Publishes 'calendarData' category's WorkingHours.
1080 * @param version (%u) Ex.: 1
1081 * @param email (%s) Ex.: alice@cosmo.local
1082 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1084 * @param version (%u)
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)
1095 * @param email (%s)
1096 * @param working_hours_xml_str (%s)
1098 * @param version (%u)
1100 #define SIPE_PUB_XML_WORKING_HOURS \
1101 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1102 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1103 "</calendarData>"\
1104 "</publication>"\
1105 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1106 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1107 "</publication>"\
1108 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" 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=\"300\" 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=\"400\" version=\"%d\" expireType=\"static\">"\
1117 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1118 "</calendarData>"\
1119 "</publication>"\
1120 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1121 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1122 "</publication>"
1125 * Returns 'calendarData' XML part with WorkingHours for publication.
1126 * Must be g_free'd after use.
1128 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1130 struct sipe_calendar* cal = sipe_private->calendar;
1132 /* key is <category><instance><container> */
1133 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1134 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1135 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1136 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1137 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1138 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1140 gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData");
1141 struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL;
1142 struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL;
1143 struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL;
1144 struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL;
1145 struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL;
1146 struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL;
1148 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1149 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1151 g_free(key_cal_1);
1152 g_free(key_cal_100);
1153 g_free(key_cal_200);
1154 g_free(key_cal_300);
1155 g_free(key_cal_400);
1156 g_free(key_cal_32000);
1158 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1159 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1160 return NULL;
1163 if (sipe_strequal(n1, n2))
1165 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1166 return NULL; /* nothing to update */
1169 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1170 /* 1 */
1171 publication_cal_1 ? publication_cal_1->version : 0,
1172 cal->email,
1173 cal->working_hours_xml_str,
1174 /* 100 - Public */
1175 publication_cal_100 ? publication_cal_100->version : 0,
1176 /* 200 - Company */
1177 publication_cal_200 ? publication_cal_200->version : 0,
1178 cal->email,
1179 cal->working_hours_xml_str,
1180 /* 300 - Team */
1181 publication_cal_300 ? publication_cal_300->version : 0,
1182 cal->email,
1183 cal->working_hours_xml_str,
1184 /* 400 - Personal */
1185 publication_cal_400 ? publication_cal_400->version : 0,
1186 cal->email,
1187 cal->working_hours_xml_str,
1188 /* 32000 - Blocked */
1189 publication_cal_32000 ? publication_cal_32000->version : 0
1194 * Publishes 'calendarData' category's FreeBusy.
1196 * @param instance (%u) Ex.: 1300372959
1197 * @param version (%u) Ex.: 1
1199 * @param instance (%u) Ex.: 1300372959
1200 * @param version (%u) Ex.: 1
1202 * @param instance (%u) Ex.: 1300372959
1203 * @param version (%u) Ex.: 1
1204 * @param email (%s) Ex.: alice@cosmo.local
1205 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1206 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1208 * @param instance (%u) Ex.: 1300372959
1209 * @param version (%u) Ex.: 1
1210 * @param email (%s) Ex.: alice@cosmo.local
1211 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1212 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1214 * @param instance (%u) Ex.: 1300372959
1215 * @param version (%u) Ex.: 1
1216 * @param email (%s) Ex.: alice@cosmo.local
1217 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1218 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1220 * @param instance (%u) Ex.: 1300372959
1221 * @param version (%u) Ex.: 1
1223 #define SIPE_PUB_XML_FREE_BUSY \
1224 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1225 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1226 "</publication>"\
1227 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1228 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1229 "</publication>"\
1230 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1231 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1232 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1233 "</calendarData>"\
1234 "</publication>"\
1235 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1236 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1237 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1238 "</calendarData>"\
1239 "</publication>"\
1240 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1241 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1242 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1243 "</calendarData>"\
1244 "</publication>"\
1245 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1246 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1247 "</publication>"
1250 * Returns 'calendarData' XML part with FreeBusy for publication.
1251 * Must be g_free'd after use.
1253 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1255 struct sipe_calendar* cal = sipe_private->calendar;
1256 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1257 char *fb_start_str;
1258 char *free_busy_base64;
1259 /* const char *st; */
1260 /* const char *fb; */
1261 char *res;
1263 /* key is <category><instance><container> */
1264 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1265 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1266 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1267 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1268 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1269 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1271 gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData");
1272 struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL;
1273 struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL;
1274 struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL;
1275 struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL;
1276 struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL;
1277 struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL;
1279 g_free(key_cal_1);
1280 g_free(key_cal_100);
1281 g_free(key_cal_200);
1282 g_free(key_cal_300);
1283 g_free(key_cal_400);
1284 g_free(key_cal_32000);
1286 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1287 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1288 return NULL;
1291 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1292 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1294 /* we will rebuplish the same data to refresh publication time,
1295 * so if data from multiple sources, most recent will be choosen
1297 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1298 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1300 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1302 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1303 // g_free(fb_start_str);
1304 // g_free(free_busy_base64);
1305 // return NULL; /* nothing to update */
1308 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1309 /* 1 */
1310 cal_data_instance,
1311 publication_cal_1 ? publication_cal_1->version : 0,
1312 /* 100 - Public */
1313 cal_data_instance,
1314 publication_cal_100 ? publication_cal_100->version : 0,
1315 /* 200 - Company */
1316 cal_data_instance,
1317 publication_cal_200 ? publication_cal_200->version : 0,
1318 cal->email,
1319 fb_start_str,
1320 free_busy_base64,
1321 /* 300 - Team */
1322 cal_data_instance,
1323 publication_cal_300 ? publication_cal_300->version : 0,
1324 cal->email,
1325 fb_start_str,
1326 free_busy_base64,
1327 /* 400 - Personal */
1328 cal_data_instance,
1329 publication_cal_400 ? publication_cal_400->version : 0,
1330 cal->email,
1331 fb_start_str,
1332 free_busy_base64,
1333 /* 32000 - Blocked */
1334 cal_data_instance,
1335 publication_cal_32000 ? publication_cal_32000->version : 0
1338 g_free(fb_start_str);
1339 g_free(free_busy_base64);
1340 return res;
1343 #ifdef HAVE_VV
1344 #define SIPE_PUB_XML_DEVICE_VV \
1345 "<voice capture=\"true\" render=\"true\" publish=\"false\"/>"\
1346 "<video capture=\"true\" render=\"true\" publish=\"false\"/>"
1347 #else
1348 #define SIPE_PUB_XML_DEVICE_VV
1349 #endif
1351 #ifdef HAVE_FREERDP
1352 #define SIPE_PUB_XML_DEVICE_APPSHARE \
1353 "<applicationSharing capture=\"true\" render=\"true\" publish=\"false\"/>"\
1354 "<contentPowerPoint capture=\"true\" render=\"true\" publish=\"false\"/>"
1355 #else
1356 #define SIPE_PUB_XML_DEVICE_APPSHARE
1357 #endif
1360 * Publishes 'device' category.
1361 * @param instance (%u) Ex.: 1938468728
1362 * @param version (%u) Ex.: 1
1363 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1364 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1365 * @param timezone (%s) Ex.: 00:00:00+01:00
1366 * @param machineName (%s) Ex.: BOSTON-OCS07
1368 #define SIPE_PUB_XML_DEVICE \
1369 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1370 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1371 "<capabilities preferred=\"false\" uri=\"%s\">"\
1372 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1373 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1374 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1375 SIPE_PUB_XML_DEVICE_VV\
1376 SIPE_PUB_XML_DEVICE_APPSHARE\
1377 "</capabilities>"\
1378 "<timezone>%s</timezone>"\
1379 "<machineName>%s</machineName>"\
1380 "</device>"\
1381 "</publication>"
1384 * Returns 'device' XML part for publication.
1385 * Must be g_free'd after use.
1387 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1389 gchar *uri;
1390 gchar *doc;
1391 gchar *uuid = get_uuid(sipe_private);
1392 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1393 /* key is <category><instance><container> */
1394 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1395 GHashTable *tmp = g_hash_table_lookup(sipe_private->our_publications, "device");
1396 struct sipe_publication *publication = tmp ? g_hash_table_lookup(tmp, key) : NULL;
1398 g_free(key);
1400 uri = sip_uri_self(sipe_private);
1401 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1402 device_instance,
1403 publication ? publication->version : 0,
1404 uuid,
1405 uri,
1406 "00:00:00+01:00", /* @TODO make timezone real*/
1407 g_get_host_name()
1410 g_free(uri);
1411 g_free(uuid);
1413 return doc;
1417 * Publishes 'machineState' category.
1418 * @param instance (%u) Ex.: 926460663
1419 * @param version (%u) Ex.: 22
1420 * @param availability (%d) Ex.: 3500
1421 * @param instance (%u) Ex.: 926460663
1422 * @param version (%u) Ex.: 22
1423 * @param availability (%d) Ex.: 3500
1425 #define SIPE_PUB_XML_STATE_MACHINE \
1426 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1427 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1428 "<availability>%d</availability>"\
1429 "<endpointLocation/>"\
1430 "</state>"\
1431 "</publication>"\
1432 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1433 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1434 "<availability>%d</availability>"\
1435 "<endpointLocation/>"\
1436 "</state>"\
1437 "</publication>"
1440 * Publishes 'userState' category.
1441 * @param instance (%u) User. Ex.: 536870912
1442 * @param version (%u) User Container 2. Ex.: 22
1443 * @param availability (%d) User Container 2. Ex.: 15500
1444 * @param instance (%u) User. Ex.: 536870912
1445 * @param version (%u) User Container 3.Ex.: 22
1446 * @param availability (%d) User Container 3. Ex.: 15500
1448 #define SIPE_PUB_XML_STATE_USER \
1449 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1450 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1451 "<availability>%d</availability>"\
1452 "<endpointLocation/>"\
1453 "</state>"\
1454 "</publication>"\
1455 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1456 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1457 "<availability>%d</availability>"\
1458 "<endpointLocation/>"\
1459 "</state>"\
1460 "</publication>"
1463 * A service method - use
1464 * - send_publish_get_category_state_machine and
1465 * - send_publish_get_category_state_user instead.
1466 * Must be g_free'd after use.
1468 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1469 gboolean force_publish,
1470 gboolean is_user_state)
1472 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1473 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1474 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1475 /* key is <category><instance><container> */
1476 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1477 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1478 gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
1479 struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
1480 struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
1482 g_free(key_2);
1483 g_free(key_3);
1485 if (!force_publish && publication_2 && (publication_2->availability == availability))
1487 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1488 return NULL; /* nothing to update */
1491 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1492 instance,
1493 publication_2 ? publication_2->version : 0,
1494 availability,
1495 instance,
1496 publication_3 ? publication_3->version : 0,
1497 availability);
1501 * Returns 'machineState' XML part for publication.
1502 * Must be g_free'd after use.
1504 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private,
1505 gboolean force_publish)
1507 return sipe_publish_get_category_state(sipe_private, force_publish, FALSE);
1511 * Returns 'userState' XML part for publication.
1512 * Must be g_free'd after use.
1514 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private,
1515 gboolean force_publish)
1517 return sipe_publish_get_category_state(sipe_private, force_publish, TRUE);
1520 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1522 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1523 gchar *pub_machine;
1524 gchar *pub_user;
1525 gchar *publications;
1527 sipe_status_set_activity(sipe_private,
1528 sipe_backend_status(SIPE_CORE_PUBLIC));
1530 pub_machine = sipe_publish_get_category_state_machine(sipe_private,
1531 TRUE);
1532 pub_user = sipe_publish_get_category_state_user(sipe_private, TRUE);
1534 publications = g_strdup_printf("%s%s%s",
1535 pub_device,
1536 pub_machine ? pub_machine : "",
1537 pub_user ? pub_user : "");
1538 g_free(pub_device);
1539 g_free(pub_machine);
1540 g_free(pub_user);
1542 send_presence_publish(sipe_private, publications);
1543 g_free(publications);
1546 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1547 struct sipmsg *msg,
1548 struct transaction *trans)
1550 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1552 if (msg->response == 200 && g_str_has_prefix(contenttype, "application/vnd-microsoft-roaming-self+xml")) {
1553 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1554 } else if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1555 sipe_xml *xml;
1556 const sipe_xml *node;
1557 gchar *fault_code;
1558 GHashTable *faults;
1559 int index_our;
1560 gboolean has_device_publication = FALSE;
1562 xml = sipe_xml_parse(msg->body, msg->bodylen);
1564 /* test if version mismatch fault */
1565 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1566 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1567 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1568 g_free(fault_code);
1569 sipe_xml_free(xml);
1570 return TRUE;
1572 g_free(fault_code);
1574 /* accumulating information about faulty versions */
1575 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1576 for (node = sipe_xml_child(xml, "details/operation");
1577 node;
1578 node = sipe_xml_twin(node))
1580 const gchar *index = sipe_xml_attribute(node, "index");
1581 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1583 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1584 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1586 sipe_xml_free(xml);
1588 /* here we are parsing our own request to figure out what publication
1589 * referenced here only by index went wrong
1591 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1593 /* publication */
1594 for (node = sipe_xml_child(xml, "publications/publication"),
1595 index_our = 1; /* starts with 1 - our first publication */
1596 node;
1597 node = sipe_xml_twin(node), index_our++)
1599 gchar *idx = g_strdup_printf("%d", index_our);
1600 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1601 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1602 g_free(idx);
1604 if (sipe_strequal("device", categoryName)) {
1605 has_device_publication = TRUE;
1608 if (curVersion) { /* fault exist on this index */
1609 const gchar *container = sipe_xml_attribute(node, "container");
1610 const gchar *instance = sipe_xml_attribute(node, "instance");
1611 /* key is <category><instance><container> */
1612 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1613 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1615 if (category) {
1616 struct sipe_publication *publication =
1617 g_hash_table_lookup(category, key);
1619 SIPE_DEBUG_INFO("key is %s", key);
1621 if (publication) {
1622 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1623 key, curVersion, publication->version);
1624 /* updating publication's version to the correct one */
1625 publication->version = atoi(curVersion);
1627 } else {
1628 /* We somehow lost this category from our publications... */
1629 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1630 publication->category = g_strdup(categoryName);
1631 publication->instance = atoi(instance);
1632 publication->container = atoi(container);
1633 publication->version = atoi(curVersion);
1634 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1635 g_free, (GDestroyNotify)free_publication);
1636 g_hash_table_insert(category, g_strdup(key), publication);
1637 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1638 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1640 g_free(key);
1643 sipe_xml_free(xml);
1644 g_hash_table_destroy(faults);
1646 /* rebublishing with right versions */
1647 if (has_device_publication) {
1648 send_publish_category_initial(sipe_private);
1649 } else {
1650 sipe_ocs2007_category_publish(sipe_private, TRUE);
1653 return TRUE;
1657 * Publishes categories.
1658 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1659 * @param publications (%s) XML publications
1661 #define SIPE_SEND_PRESENCE \
1662 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1663 "<publications uri=\"%s\">"\
1664 "%s"\
1665 "</publications>"\
1666 "</publish>"
1668 static void send_presence_publish(struct sipe_core_private *sipe_private,
1669 const char *publications)
1671 gchar *uri;
1672 gchar *doc;
1673 gchar *tmp;
1674 gchar *hdr;
1676 uri = sip_uri_self(sipe_private);
1677 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1678 uri,
1679 publications);
1681 tmp = get_contact(sipe_private);
1682 hdr = g_strdup_printf("Contact: %s\r\n"
1683 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1685 sip_transport_service(sipe_private,
1686 uri,
1687 hdr,
1688 doc,
1689 process_send_presence_category_publish_response);
1691 g_free(tmp);
1692 g_free(hdr);
1693 g_free(uri);
1694 g_free(doc);
1698 * Publishes self status
1699 * based on own calendar information.
1701 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1702 SIPE_UNUSED_PARAMETER void *unused)
1704 struct sipe_calendar* cal = sipe_private->calendar;
1705 struct sipe_cal_event* event = NULL;
1706 gchar *pub_cal_working_hours = NULL;
1707 gchar *pub_cal_free_busy = NULL;
1708 gchar *pub_calendar = NULL;
1709 gchar *pub_calendar2 = NULL;
1710 gchar *pub_oof_note = NULL;
1711 const gchar *oof_note;
1712 time_t oof_start = 0;
1713 time_t oof_end = 0;
1715 if (!cal) {
1716 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1717 return;
1720 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1721 if (cal->cal_events) {
1722 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1725 if (event) {
1726 sipe_cal_event_debug(event, "publish_calendar_status_self: current event is:\n");
1727 } else {
1728 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1731 /* Logic
1732 if OOF
1733 OOF publish, Busy clean
1734 ilse if Busy
1735 OOF clean, Busy publish
1736 else
1737 OOF clean, Busy clean
1739 if (event && event->cal_status == SIPE_CAL_OOF) {
1740 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1741 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1742 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1743 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1744 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1745 } else {
1746 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1747 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1750 oof_note = sipe_ews_get_oof_note(cal);
1751 if (sipe_strequal("Scheduled", cal->oof_state)) {
1752 oof_start = cal->oof_start;
1753 oof_end = cal->oof_end;
1755 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end, FALSE);
1757 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1758 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1760 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1761 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1762 } else {
1763 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1764 pub_cal_working_hours ? pub_cal_working_hours : "",
1765 pub_cal_free_busy ? pub_cal_free_busy : "",
1766 pub_calendar ? pub_calendar : "",
1767 pub_calendar2 ? pub_calendar2 : "",
1768 pub_oof_note ? pub_oof_note : "");
1770 send_presence_publish(sipe_private, publications);
1771 g_free(publications);
1774 g_free(pub_cal_working_hours);
1775 g_free(pub_cal_free_busy);
1776 g_free(pub_calendar);
1777 g_free(pub_calendar2);
1778 g_free(pub_oof_note);
1780 /* repeat scheduling */
1781 schedule_publish_update(sipe_private, time(NULL));
1784 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
1785 gboolean force_publish)
1787 GString *publications = g_string_new("");
1788 gchar *tmp;
1790 if (force_publish || sipe_private->status_set_by_user) {
1791 tmp = sipe_publish_get_category_state_user(sipe_private,
1792 force_publish);
1793 if (tmp) {
1794 g_string_append(publications, tmp);
1795 g_free(tmp);
1799 tmp = sipe_publish_get_category_state_machine(sipe_private,
1800 force_publish);
1801 if (tmp) {
1802 g_string_append(publications, tmp);
1803 g_free(tmp);
1806 tmp = sipe_publish_get_category_note(sipe_private,
1807 sipe_private->note,
1808 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1811 force_publish);
1812 if (tmp) {
1813 g_string_append(publications, tmp);
1814 g_free(tmp);
1817 if (publications->len)
1818 send_presence_publish(sipe_private, publications->str);
1819 else
1820 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1822 g_string_free(publications, TRUE);
1825 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1827 gchar *publications = NULL;
1828 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1830 /* key is <category><instance><container> */
1831 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1832 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1833 gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
1834 struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
1835 struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
1836 g_free(key_2);
1837 g_free(key_3);
1839 #ifdef HAVE_VV
1840 if (g_hash_table_size(sipe_private->media_calls)) {
1841 guint availability_min = 0;
1842 guint availability_max = 8999;
1843 const gchar *token = NULL;
1844 GList *calls = g_hash_table_get_values(sipe_private->media_calls);
1845 GList *i;
1847 if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) {
1848 availability_min = 6500;
1849 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1852 for (i = calls; i; i = i->next) {
1853 if (sipe_media_is_conference_call(i->data)) {
1854 availability_min = 7000;
1855 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1858 if (sipe_appshare_get_role(i->data) == SIPE_APPSHARE_ROLE_PRESENTER) {
1859 availability_min = 9000;
1860 availability_max = 11999;
1861 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_PRES);
1865 g_list_free(calls);
1867 if (token) {
1868 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1869 instance, publication_2 ? publication_2->version : 0,
1870 availability_min, token, availability_min, availability_max,
1871 instance, publication_3 ? publication_3->version : 0,
1872 availability_min, token, availability_min, availability_max);
1874 } else
1875 #endif
1877 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1878 instance, publication_2 ? publication_2->version : 0,
1879 instance, publication_3 ? publication_3->version : 0);
1882 if (publications) {
1883 send_presence_publish(sipe_private, publications);
1884 g_free(publications);
1888 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1889 gpointer value,
1890 GString* str)
1892 struct sipe_publication *publication = value;
1894 g_string_append_printf( str,
1895 SIPE_PUB_XML_PUBLICATION_CLEAR,
1896 publication->category,
1897 publication->instance,
1898 publication->container,
1899 publication->version,
1900 "static");
1903 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1905 GString* str;
1906 gchar *publications;
1908 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1909 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1910 return;
1913 str = g_string_new(NULL);
1914 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1915 publications = g_string_free(str, FALSE);
1917 send_presence_publish(sipe_private, publications);
1918 g_free(publications);
1921 /* key is <category><instance><container> */
1922 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1923 const gchar *key)
1925 GSList *entry;
1927 /* filling keys for our publications if not yet cached */
1928 if (!sipe_private->our_publication_keys) {
1929 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1930 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1931 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1932 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1933 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1934 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1935 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1936 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1938 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1939 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1940 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1941 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1942 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1943 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1944 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1945 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1946 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1947 SIPE_DEBUG_INFO("\tNote : %u", 0);
1948 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1950 /* device */
1951 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1952 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1954 /* state:machineState */
1955 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1956 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1957 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1958 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1960 /* state:userState */
1961 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1962 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1963 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1964 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1966 /* state:calendarState */
1967 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1968 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1969 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1970 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1972 /* state:calendarState OOF */
1973 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1974 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1975 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1976 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1978 /* state:phoneState */
1979 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1980 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1981 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1982 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1984 /* note */
1985 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1986 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1987 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1988 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1989 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1990 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1992 /* note OOF */
1993 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1994 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1995 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1996 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1997 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1998 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
2000 /* calendarData:WorkingHours */
2001 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2002 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2003 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2004 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2005 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2006 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2007 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2008 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2009 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2010 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2011 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2012 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2014 /* calendarData:FreeBusy */
2015 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2016 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
2017 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2018 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
2019 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2020 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
2021 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2022 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
2023 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2024 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
2025 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2026 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
2028 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
2029 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
2032 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2034 entry = sipe_private->our_publication_keys;
2035 while (entry) {
2036 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2037 if (sipe_strequal(entry->data, key)) {
2038 return TRUE;
2040 entry = entry->next;
2042 return FALSE;
2045 static void sipe_refresh_blocked_status_cb(char *buddy_name,
2046 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
2047 struct sipe_core_private *sipe_private)
2049 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2050 gboolean blocked = (container_id == 32000);
2051 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2053 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2054 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2056 if (blocked != blocked_in_blist) {
2057 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2061 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2063 sipe_buddy_foreach(sipe_private,
2064 (GHFunc) sipe_refresh_blocked_status_cb,
2065 sipe_private);
2069 * When we receive some self (BE) NOTIFY with a new subscriber
2070 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2073 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2074 struct sipmsg *msg)
2076 gchar *contact;
2077 gchar *to;
2078 sipe_xml *xml;
2079 const sipe_xml *node;
2080 const sipe_xml *node2;
2081 char *display_name = NULL;
2082 char *uri;
2083 GSList *category_names = NULL;
2084 int aggreg_avail = 0;
2085 gchar *activity_token = NULL;
2086 gboolean do_update_status = FALSE;
2087 gboolean has_note_cleaned = FALSE;
2088 GHashTable *devices;
2090 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2092 xml = sipe_xml_parse(msg->body, msg->bodylen);
2093 if (!xml) return;
2095 contact = get_contact(sipe_private);
2096 to = sip_uri_self(sipe_private);
2098 /* categories */
2099 /* set list of categories participating in this XML */
2100 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2101 const gchar *name = sipe_xml_attribute(node, "name");
2102 category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2103 (gchar *)name,
2104 (GCompareFunc)strcmp,
2105 NULL);
2107 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2108 category_names ? (int) g_slist_length(category_names) : -1);
2109 /* drop category information */
2110 if (category_names) {
2111 GSList *entry = category_names;
2112 while (entry) {
2113 GHashTable *cat_publications;
2114 const gchar *category = entry->data;
2115 entry = entry->next;
2116 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2117 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2118 if (cat_publications) {
2119 g_hash_table_remove(sipe_private->our_publications, category);
2120 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2124 g_slist_free(category_names);
2126 /* filling our categories reflected in roaming data */
2127 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2128 g_free, NULL);
2129 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2130 const char *tmp;
2131 const gchar *name = sipe_xml_attribute(node, "name");
2132 guint container = sipe_xml_int_attribute(node, "container", -1);
2133 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2134 guint version = sipe_xml_int_attribute(node, "version", 0);
2135 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2136 sipe_utils_str_to_time(tmp) : 0;
2137 gchar *key;
2138 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2140 /* Ex. clear note: <category name="note"/> */
2141 if (container == (guint)-1) {
2142 g_free(sipe_private->note);
2143 sipe_private->note = NULL;
2144 do_update_status = TRUE;
2145 continue;
2148 /* Ex. clear note: <category name="note" container="200"/> */
2149 if (instance == (guint)-1) {
2150 if (container == 200) {
2151 g_free(sipe_private->note);
2152 sipe_private->note = NULL;
2153 do_update_status = TRUE;
2155 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2156 sipe_remove_category_container_publications(
2157 sipe_private->our_publications, name, container);
2158 continue;
2161 /* key is <category><instance><container> */
2162 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2163 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2165 /* capture all userState publication for later clean up if required */
2166 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2167 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2169 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2170 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2171 publication->category = g_strdup(name);
2172 publication->instance = instance;
2173 publication->container = container;
2174 publication->version = version;
2176 if (!sipe_private->user_state_publications) {
2177 sipe_private->user_state_publications = g_hash_table_new_full(
2178 g_str_hash, g_str_equal,
2179 g_free, (GDestroyNotify)free_publication);
2181 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2182 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2183 key, version);
2187 /* count each client instance only once */
2188 if (sipe_strequal(name, "device"))
2189 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2191 if (sipe_is_our_publication(sipe_private, key)) {
2192 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2194 publication->category = g_strdup(name);
2195 publication->instance = instance;
2196 publication->container = container;
2197 publication->version = version;
2199 /* filling publication->availability */
2200 if (sipe_strequal(name, "state")) {
2201 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2202 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2204 if (xn_avail) {
2205 gchar *avail_str = sipe_xml_data(xn_avail);
2206 if (avail_str) {
2207 publication->availability = atoi(avail_str);
2209 g_free(avail_str);
2211 /* for calendarState */
2212 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2213 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2214 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2216 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2217 if (xn_activity) {
2218 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2219 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2221 event->is_meeting = TRUE;
2224 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2225 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2227 publication->cal_event_hash = sipe_cal_event_hash(event);
2228 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2229 publication->cal_event_hash);
2230 sipe_cal_event_free(event);
2233 /* filling publication->note */
2234 if (sipe_strequal(name, "note")) {
2235 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2237 if (!has_note_cleaned) {
2238 has_note_cleaned = TRUE;
2240 g_free(sipe_private->note);
2241 sipe_private->note = NULL;
2242 sipe_private->note_since = publish_time;
2244 do_update_status = TRUE;
2247 g_free(publication->note);
2248 publication->note = NULL;
2249 if (xn_body) {
2250 char *tmp;
2252 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2253 g_free(tmp);
2254 if (publish_time >= sipe_private->note_since) {
2255 g_free(sipe_private->note);
2256 sipe_private->note = g_strdup(publication->note);
2257 sipe_private->note_since = publish_time;
2258 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2259 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2260 else
2261 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2263 do_update_status = TRUE;
2268 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2269 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2270 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2271 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2272 if (xn_free_busy) {
2273 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2274 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2276 if (xn_working_hours) {
2277 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2281 if (!cat_publications) {
2282 cat_publications = g_hash_table_new_full(
2283 g_str_hash, g_str_equal,
2284 g_free, (GDestroyNotify)free_publication);
2285 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2286 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2288 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2289 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2291 g_free(key);
2293 /* aggregateState (not an our publication) from 2-nd container */
2294 if (sipe_strequal(name, "state") && container == 2) {
2295 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2296 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2298 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2299 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2301 if (xn_avail) {
2302 gchar *avail_str = sipe_xml_data(xn_avail);
2303 if (avail_str) {
2304 aggreg_avail = atoi(avail_str);
2306 g_free(avail_str);
2309 do_update_status = TRUE;
2312 if (xn_activity) {
2313 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2317 /* userProperties published by server from AD */
2318 if (!sipe_private->csta &&
2319 sipe_strequal(name, "userProperties")) {
2320 const sipe_xml *line;
2321 /* line, for Remote Call Control (RCC) or external Lync/Communicator call */
2322 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2323 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2324 gchar *line_uri = sipe_xml_data(line);
2325 if (!line_uri) {
2326 continue;
2329 if (sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual")) {
2330 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2331 if (line_server) {
2332 gchar *tmp = g_strstrip(line_uri);
2333 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s",
2334 tmp, line_server);
2335 sip_csta_open(sipe_private, tmp, line_server);
2338 #ifdef HAVE_VV
2339 else if (sipe_strequal(line_type, "Uc")) {
2341 if (!sipe_private->uc_line_uri) {
2342 sipe_private->uc_line_uri = g_strdup(g_strstrip(line_uri));
2343 } else {
2344 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: "
2345 "sipe_private->uc_line_uri is already set.");
2348 #endif
2350 g_free(line_uri);
2352 break;
2356 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2357 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2359 /* active clients for user account */
2360 if (g_hash_table_size(devices) == 0) {
2361 /* updated roaming information without device information - no need to update MPOP flag */
2362 } else if (g_hash_table_size(devices) > 1) {
2363 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2364 SIPE_LOG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2365 g_hash_table_size(devices));
2366 } else {
2367 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2368 SIPE_LOG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2370 g_hash_table_destroy(devices);
2372 /* containers */
2373 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2374 guint id = sipe_xml_int_attribute(node, "id", 0);
2375 struct sipe_container *container = sipe_find_container(sipe_private, id);
2377 if (container) {
2378 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2379 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2380 sipe_ocs2007_free_container(container);
2382 container = g_new0(struct sipe_container, 1);
2383 container->id = id;
2384 container->version = sipe_xml_int_attribute(node, "version", 0);
2385 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2386 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2388 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2389 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2390 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2391 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2392 container->members = g_slist_append(container->members, member);
2393 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2394 member->type, member->value ? member->value : "");
2398 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2399 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2400 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2401 char *container_xmls = NULL;
2402 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2403 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2405 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2406 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2407 /* initial set-up to let counterparties see your status */
2408 if (sameEnterpriseAL < 0) {
2409 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2410 guint version = container ? container->version : 0;
2411 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2413 if (federatedAL < 0) {
2414 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2415 guint version = container ? container->version : 0;
2416 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2418 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2420 if (container_xmls) {
2421 sipe_send_set_container_members(sipe_private, container_xmls);
2423 g_free(container_xmls);
2426 /* Refresh contacts' blocked status */
2427 sipe_refresh_blocked_status(sipe_private);
2429 /* subscribers */
2430 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2431 const char *user;
2432 const char *acknowledged;
2433 gchar *hdr;
2434 gchar *body;
2436 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2437 if (!user) continue;
2438 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2439 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2440 uri = sip_uri_from_name(user);
2442 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2443 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2445 acknowledged= sipe_xml_attribute(node, "acknowledged");
2446 if(sipe_strcase_equal(acknowledged,"false")){
2447 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2448 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2449 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2452 hdr = g_strdup_printf(
2453 "Contact: %s\r\n"
2454 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2456 body = g_strdup_printf(
2457 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2458 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2459 "</setSubscribers>", user);
2461 sip_transport_service(sipe_private,
2463 hdr,
2464 body,
2465 NULL);
2466 g_free(body);
2467 g_free(hdr);
2469 g_free(display_name);
2470 g_free(uri);
2473 g_free(contact);
2474 sipe_xml_free(xml);
2476 /* Publish initial state if not yet.
2477 * Assuming this happens on initial responce to subscription to roaming-self
2478 * so we've already updated our roaming data in full.
2479 * Only for 2007+
2481 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2482 send_publish_category_initial(sipe_private);
2483 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2484 /* dalayed run */
2485 sipe_cal_delayed_calendar_update(sipe_private);
2486 do_update_status = FALSE;
2487 } else if (aggreg_avail) {
2489 if (aggreg_avail &&
2490 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2491 /* not offline */
2492 sipe_status_set_token(sipe_private,
2493 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2494 } else {
2495 /* do not let offline status switch us off */
2496 sipe_status_set_activity(sipe_private,
2497 SIPE_ACTIVITY_INVISIBLE);
2501 if (do_update_status) {
2502 sipe_status_and_note(sipe_private, NULL);
2505 g_free(to);
2506 g_free(activity_token);
2510 * for Access levels menu
2512 #define INDENT_FMT " %s"
2515 * Member is indirectly belong to access level container.
2516 * For example 'sameEnterprise' is in the container and user
2517 * belongs to that same enterprise.
2519 #define INDENT_MARKED_INHERITED_FMT "= %s"
2521 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2522 struct sipe_backend_buddy_menu *menu,
2523 const gchar *member_type,
2524 const gchar *member_value,
2525 const gboolean extra_menu)
2527 unsigned int i;
2528 gboolean is_group_access = FALSE;
2529 int container_id;
2531 if (!menu)
2532 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2534 container_id = sipe_ocs2007_find_access_level(sipe_private,
2535 member_type,
2536 member_value,
2537 &is_group_access);
2539 for (i = 1; i <= CONTAINERS_LEN; i++) {
2541 * Blocked should remain in the first place
2542 * in the containers[] array.
2544 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2545 int container_j = containers[j];
2546 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2547 struct sipe_container *container = create_container(j,
2548 member_type,
2549 member_value,
2550 FALSE);
2551 gchar *label;
2553 /* libpurple memory leak workaround */
2554 blist_menu_remember_container(sipe_private, container);
2556 /* current container/access level */
2557 if (container_j == container_id) {
2558 label = is_group_access ?
2559 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2560 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2561 } else {
2562 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2565 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2566 menu,
2567 label,
2568 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2569 container);
2570 g_free(label);
2573 if (extra_menu && (container_id >= 0) && !is_group_access) {
2574 struct sipe_container *container = create_container(0,
2575 member_type,
2576 member_value,
2577 TRUE);
2578 gchar *label;
2580 /* separator */
2581 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2582 menu,
2583 " --------------");
2586 /* libpurple memory leak workaround */
2587 blist_menu_remember_container(sipe_private, container);
2589 /* Translators: remove (clear) previously assigned access level */
2590 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2591 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2592 menu,
2593 label,
2594 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2595 container);
2596 g_free(label);
2599 return(menu);
2602 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2604 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2605 GSList *access_domains, *entry;
2607 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2608 menu,
2609 _("People in my company"),
2610 access_levels_menu(sipe_private,
2611 NULL,
2612 "sameEnterprise",
2613 NULL,
2614 FALSE));
2616 /* this is original name, don't edit */
2617 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2618 menu,
2619 _("People in domains connected with my company"),
2620 access_levels_menu(sipe_private,
2621 NULL,
2622 "federated",
2623 NULL,
2624 FALSE));
2626 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2627 menu,
2628 _("People in public domains"),
2629 access_levels_menu(sipe_private,
2630 NULL,
2631 "publicCloud",
2632 NULL,
2633 TRUE));
2635 entry = access_domains = get_access_domains(sipe_private);
2636 while (entry) {
2637 gchar *domain = entry->data;
2638 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2640 /* takes over ownership of entry->data (= domain) */
2641 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2642 menu,
2643 menu_name,
2644 access_levels_menu(sipe_private,
2645 NULL,
2646 "domain",
2647 domain,
2648 TRUE));
2649 g_free(menu_name);
2651 entry = entry->next;
2653 g_slist_free(access_domains);
2655 /* separator */
2656 /* People in domains connected with my company */
2657 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2658 menu,
2659 "-------------------------------------------");
2661 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2662 menu,
2663 _("Add new domain..."),
2664 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2665 NULL);
2667 return(menu);
2670 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2671 const gchar *buddy_name)
2673 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2674 gchar *label;
2677 * Workaround for missing libpurple API to release resources allocated
2678 * during blist_node_menu() callback. See also:
2680 * <http://developer.pidgin.im/ticket/12597>
2682 * We remember all memory blocks in a list and deallocate them when
2684 * - the next time we enter the callback, or
2685 * - the account is disconnected
2687 * That means that after the buddy menu has been closed we have unused
2688 * resources but at least we don't leak them anymore...
2690 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2692 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2693 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2694 menu,
2695 label,
2696 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2697 NULL);
2698 g_free(label);
2700 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2701 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2702 menu,
2703 label,
2704 access_groups_menu(sipe_private));
2705 g_free(label);
2707 menu = access_levels_menu(sipe_private,
2708 menu,
2709 "user",
2710 sipe_get_no_sip_uri(buddy_name),
2711 TRUE);
2713 return(menu);
2717 Local Variables:
2718 mode: c
2719 c-file-style: "bsd"
2720 indent-tabs-mode: t
2721 tab-width: 8
2722 End: