digest: add support for OpenSSL 1.1.0
[siplcs.git] / src / core / sipe-ocs2007.c
blob2899ce25d4100e724f5a0693ec7d09e843017369
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2017 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * OCS2007+ specific code
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
36 #include <glib.h>
38 #include "sipe-common.h"
39 #include "sipmsg.h"
40 #include "sip-csta.h"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-buddy.h"
44 #include "sipe-cal.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
47 #include "sipe-ews.h"
48 #include "sipe-media.h"
49 #include "sipe-nls.h"
50 #include "sipe-ocs2007.h"
51 #include "sipe-schedule.h"
52 #include "sipe-status.h"
53 #include "sipe-utils.h"
54 #include "sipe-xml.h"
56 /** MS-PRES publication */
57 struct sipe_publication {
58 gchar *category;
59 guint instance;
60 guint container;
61 guint version;
62 /** for 'state' category */
63 int availability;
64 /** for 'state:calendarState' category */
65 char *cal_event_hash;
66 /** for 'note' category */
67 gchar *note;
68 /** for 'calendarData' category; 300(Team) container */
69 char *working_hours_xml_str;
70 char *fb_start_str;
71 char *free_busy_base64;
74 /**
75 * 2007-style Activity and Availability.
77 * [MS-PRES] 3.7.5.5
79 * Conversion of legacyInterop availability ranges and activity tokens into
80 * SIPE activity tokens. The descriptions of availability ranges are defined at:
82 * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
84 * The values define the starting point of a range.
86 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
95 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
96 const gchar *activity)
98 guint type;
100 if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
101 type = SIPE_ACTIVITY_OFFLINE;
102 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
103 type = SIPE_ACTIVITY_AVAILABLE;
104 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
105 type = SIPE_ACTIVITY_INACTIVE;
106 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
107 type = sipe_status_token_to_activity(activity);
108 if ((type != SIPE_ACTIVITY_ON_PHONE) &&
109 (type != SIPE_ACTIVITY_IN_CONF))
110 type = SIPE_ACTIVITY_BUSY;
111 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
112 type = SIPE_ACTIVITY_BUSYIDLE;
113 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
114 type = SIPE_ACTIVITY_DND;
115 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
116 type = SIPE_ACTIVITY_BRB;
117 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
118 type = SIPE_ACTIVITY_AWAY;
119 } else {
120 type = SIPE_ACTIVITY_OFFLINE;
123 return sipe_status_activity_to_token(type);
126 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
128 if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
129 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
130 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
131 } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
132 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
133 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
134 } else {
135 return(NULL);
140 * @param sipe_status_id (in)
141 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
142 * requests this token]
144 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
145 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
146 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
147 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
148 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
149 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
150 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
151 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
152 const gchar **activity_token)
154 guint availability;
155 guint activity;
157 if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
158 availability = SIPE_OCS2007_AVAILABILITY_AWAY;
159 activity = SIPE_ACTIVITY_AWAY;
160 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
161 availability = SIPE_OCS2007_AVAILABILITY_BRB;
162 activity = SIPE_ACTIVITY_BRB;
163 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
164 availability = SIPE_OCS2007_AVAILABILITY_DND;
165 activity = SIPE_ACTIVITY_DND;
166 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
167 availability = SIPE_OCS2007_AVAILABILITY_BUSY;
168 activity = SIPE_ACTIVITY_BUSY;
169 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
170 availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
171 activity = SIPE_ACTIVITY_ONLINE;
172 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
173 availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
174 activity = SIPE_ACTIVITY_UNSET;
175 } else {
176 /* Offline or invisible */
177 availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
178 activity = SIPE_ACTIVITY_OFFLINE;
181 if (activity_token) {
182 *activity_token = sipe_status_activity_to_token(activity);
185 return(availability);
188 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
190 return(SIPE_OCS2007_AVAILABILITY_BUSY >=
191 sipe_ocs2007_availability_from_status(status_id, NULL));
195 gboolean sipe_ocs2007_availability_is_away(guint availability)
197 return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
200 static void send_presence_publish(struct sipe_core_private *sipe_private,
201 const char *publications);
203 static void free_publication(struct sipe_publication *publication)
205 g_free(publication->category);
206 g_free(publication->cal_event_hash);
207 g_free(publication->note);
209 g_free(publication->working_hours_xml_str);
210 g_free(publication->fb_start_str);
211 g_free(publication->free_busy_base64);
213 g_free(publication);
216 struct hash_table_delete_payload {
217 GHashTable *hash_table;
218 guint container;
221 static void sipe_remove_category_container_publications_cb(const gchar *name,
222 struct sipe_publication *publication,
223 struct hash_table_delete_payload *payload)
225 if (publication->container == payload->container) {
226 g_hash_table_remove(payload->hash_table, name);
230 static void sipe_remove_category_container_publications(GHashTable *our_publications,
231 const gchar *category,
232 guint container)
234 struct hash_table_delete_payload payload;
235 payload.hash_table = g_hash_table_lookup(our_publications, category);
237 if (!payload.hash_table) return;
239 payload.container = container;
240 g_hash_table_foreach(payload.hash_table,
241 (GHFunc)sipe_remove_category_container_publications_cb,
242 &payload);
245 /** MS-PRES container */
246 struct sipe_container {
247 guint id;
248 guint version;
249 GSList *members;
252 /** MS-PRES container member */
253 struct sipe_container_member {
254 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
255 gchar *type;
256 gchar *value;
259 static const guint containers[] = {32000, 400, 300, 200, 100};
260 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
262 static void free_container_member(struct sipe_container_member *member)
264 if (!member) return;
266 g_free(member->type);
267 g_free(member->value);
268 g_free(member);
271 static void sipe_ocs2007_free_container(struct sipe_container *container)
273 GSList *entry;
275 if (!container) return;
277 entry = container->members;
278 while (entry) {
279 void *data = entry->data;
280 entry = g_slist_remove(entry, data);
281 free_container_member((struct sipe_container_member *)data);
283 g_free(container);
286 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
288 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
289 sipe_utils_slist_free_full(sipe_private->blist_menu_containers,
290 (GDestroyNotify) sipe_ocs2007_free_container);
291 sipe_private->blist_menu_containers = NULL;
294 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
295 struct sipe_container *container)
297 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
298 container);
301 static struct sipe_container *create_container(guint index,
302 const gchar *member_type,
303 const gchar *member_value,
304 gboolean is_group)
306 struct sipe_container *container = g_new0(struct sipe_container, 1);
307 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
309 container->id = is_group ? (guint) -1 : containers[index];
310 container->members = g_slist_append(container->members, member);
311 member->type = g_strdup(member_type);
312 member->value = g_strdup(member_value);
314 return(container);
317 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
319 sipe_utils_slist_free_full(sipe_private->containers,
320 (GDestroyNotify) sipe_ocs2007_free_container);
324 * Finds locally stored MS-PRES container member
326 static struct sipe_container_member *
327 sipe_find_container_member(struct sipe_container *container,
328 const gchar *type,
329 const gchar *value)
331 struct sipe_container_member *member;
332 GSList *entry;
334 if (container == NULL || type == NULL) {
335 return NULL;
338 entry = container->members;
339 while (entry) {
340 member = entry->data;
341 if (sipe_strcase_equal(member->type, type) &&
342 sipe_strcase_equal(member->value, value))
344 return member;
346 entry = entry->next;
348 return NULL;
352 * Finds locally stored MS-PRES container by id
354 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
355 guint id)
357 GSList *entry = sipe_private->containers;
358 while (entry) {
359 struct sipe_container *container = entry->data;
360 if (id == container->id) {
361 return container;
363 entry = entry->next;
365 return NULL;
368 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
369 const gchar *type,
370 const gchar *value)
372 unsigned int i = 0;
373 const gchar *value_mod = value;
375 if (!type) return -1;
377 if (sipe_strequal("user", type)) {
378 value_mod = sipe_get_no_sip_uri(value);
381 for (i = 0; i < CONTAINERS_LEN; i++) {
382 struct sipe_container_member *member;
383 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
384 if (!container) continue;
386 member = sipe_find_container_member(container, type, value_mod);
387 if (member) return containers[i];
390 return -1;
394 * Returns pointer to domain part in provided Email URL
396 * @param email an email URL. Example: first.last@hq.company.com
397 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
399 * Doesn't allocate memory
401 static const gchar *sipe_get_domain(const gchar *email)
403 gchar *tmp;
405 if (!email) return NULL;
407 tmp = strstr(email, "@");
409 if (tmp && ((tmp+1) < (email + strlen(email)))) {
410 return tmp+1;
411 } else {
412 return NULL;
416 /* @TODO: replace with binary search for faster access? */
417 /** source: http://support.microsoft.com/kb/897567 */
418 static const gchar * const public_domains[] = {
419 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
420 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
421 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
422 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
423 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
424 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
425 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
426 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
427 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
428 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
429 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
430 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
431 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
432 "yahoo.com",
433 NULL};
435 static gboolean sipe_is_public_domain(const gchar *domain)
437 int i = 0;
438 while (public_domains[i]) {
439 if (sipe_strcase_equal(public_domains[i], domain)) {
440 return TRUE;
442 i++;
444 return FALSE;
448 * Access Levels
449 * 32000 - Blocked
450 * 400 - Personal
451 * 300 - Team
452 * 200 - Company
453 * 100 - Public
455 const gchar *sipe_ocs2007_access_level_name(guint id)
457 switch (id) {
458 case 32000: return _("Blocked");
459 case 400: return _("Personal");
460 case 300: return _("Team");
461 case 200: return _("Company");
462 case 100: return _("Public");
464 return _("Unknown");
467 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
468 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
469 const gchar *type,
470 const gchar *value,
471 gboolean *is_group_access)
473 int container_id = -1;
475 if (sipe_strequal("user", type)) {
476 const char *domain;
477 const char *no_sip_uri = sipe_get_no_sip_uri(value);
479 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
480 if (container_id >= 0) {
481 if (is_group_access) *is_group_access = FALSE;
482 return container_id;
485 domain = sipe_get_domain(no_sip_uri);
486 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
487 if (container_id >= 0) {
488 if (is_group_access) *is_group_access = TRUE;
489 return container_id;
492 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
493 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
494 if (is_group_access) *is_group_access = TRUE;
495 return container_id;
498 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
499 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
500 if (is_group_access) *is_group_access = TRUE;
501 return container_id;
504 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
505 if ((container_id >= 0)) {
506 if (is_group_access) *is_group_access = TRUE;
507 return container_id;
509 } else {
510 container_id = sipe_find_member_access_level(sipe_private, type, value);
511 if (is_group_access) *is_group_access = FALSE;
514 return container_id;
517 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
519 struct sipe_container *container;
520 struct sipe_container_member *member;
521 GSList *entry;
522 GSList *entry2;
523 GSList *res = NULL;
525 entry = sipe_private->containers;
526 while (entry) {
527 container = entry->data;
529 entry2 = container->members;
530 while (entry2) {
531 member = entry2->data;
532 if (sipe_strcase_equal(member->type, "domain"))
534 res = sipe_utils_slist_insert_unique_sorted(res,
535 g_strdup(member->value),
536 (GCompareFunc)g_ascii_strcasecmp,
537 g_free);
539 entry2 = entry2->next;
541 entry = entry->next;
543 return res;
546 static void sipe_send_container_members_prepare(const guint container_id,
547 const guint container_version,
548 const gchar *action,
549 const gchar *type,
550 const gchar *value,
551 char **container_xmls)
553 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
554 gchar *body;
556 if (!container_xmls) return;
558 body = g_strdup_printf(
559 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
560 container_id,
561 container_version,
562 action,
563 type,
564 value_str);
565 g_free(value_str);
567 if ((*container_xmls) == NULL) {
568 *container_xmls = body;
569 } else {
570 char *tmp = *container_xmls;
572 *container_xmls = g_strconcat(*container_xmls, body, NULL);
573 g_free(tmp);
574 g_free(body);
578 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
579 char *container_xmls)
581 gchar *self;
582 gchar *contact;
583 gchar *hdr;
584 gchar *body;
586 if (!container_xmls) return;
588 self = sip_uri_self(sipe_private);
589 body = g_strdup_printf(
590 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
591 "%s"
592 "</setContainerMembers>",
593 container_xmls);
595 contact = get_contact(sipe_private);
596 hdr = g_strdup_printf("Contact: %s\r\n"
597 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
598 g_free(contact);
600 sip_transport_service(sipe_private,
601 self,
602 hdr,
603 body,
604 NULL);
606 g_free(hdr);
607 g_free(body);
608 g_free(self);
612 * @param container_id a new access level. If -1 then current access level
613 * is just removed (I.e. the member is removed from all containers).
614 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
615 * @param value a value for member. E.g. SIP URI for "user" member type.
617 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
618 const int container_id,
619 const gchar *type,
620 const gchar *value)
622 unsigned int i;
623 int current_container_id = -1;
624 char *container_xmls = NULL;
626 /* for each container: find/delete */
627 for (i = 0; i < CONTAINERS_LEN; i++) {
628 struct sipe_container_member *member;
629 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
631 if (!container) continue;
633 member = sipe_find_container_member(container, type, value);
634 if (member) {
635 current_container_id = containers[i];
636 /* delete/publish current access level */
637 if (container_id < 0 || container_id != current_container_id) {
638 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
639 /* remove member from our cache, to be able to recalculate AL below */
640 container->members = g_slist_remove(container->members, member);
645 /* recalculate AL below */
646 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
648 /* assign/publish new access level */
649 if (container_id != current_container_id && container_id >= 0) {
650 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
651 guint version = container ? container->version : 0;
653 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
656 if (container_xmls) {
657 sipe_send_set_container_members(sipe_private, container_xmls);
659 g_free(container_xmls);
662 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
663 gpointer parameter)
665 struct sipe_container *container = parameter;
666 struct sipe_container_member *member;
668 if (!container || !container->members) return;
670 member = ((struct sipe_container_member *)container->members->data);
672 if (!member->type) return;
674 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
675 container->id, member->type, member->value ? member->value : "");
677 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
678 container->id,
679 member->type,
680 member->value);
684 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
685 const gchar *domain,
686 guint index)
688 /* move Blocked first */
689 guint i = (index == 4) ? 0 : index + 1;
690 guint container_id = containers[i];
692 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
693 domain ? domain : "", index, container_id);
695 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
696 container_id,
697 "domain",
698 domain);
702 * Schedules process of self status publish
703 * based on own calendar information.
704 * Should be scheduled to the beginning of every
705 * 15 min interval, like:
706 * 13:00, 13:15, 13:30, 13:45, etc.
709 static void schedule_publish_update(struct sipe_core_private *sipe_private,
710 time_t calculate_from)
712 int interval = 5*60;
713 /** start of the beginning of closest 5 min interval. */
714 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
716 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
717 sipe_utils_time_to_debug_str(localtime(&calculate_from)));
718 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
719 sipe_utils_time_to_debug_str(localtime(&next_start)));
721 sipe_schedule_seconds(sipe_private,
722 "<+2007-cal-status>",
723 NULL,
724 next_start - time(NULL),
725 sipe_ocs2007_presence_publish,
726 NULL);
730 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
731 * @param availability (%d) Ex.: 6500
733 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
734 "<availability>%d</availability>"
736 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
737 * @param token (%s) Ex.: in-a-meeting
738 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
739 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
741 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
742 "<activity token=\"%s\" %s %s></activity>"
744 * Publishes 'calendarState' category.
745 * @param instance (%u) Ex.: 1339299275
746 * @param version (%u) Ex.: 1
747 * @param uri (%s) Ex.: john@contoso.com
748 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
749 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
750 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
751 * @param meeting_subject (%s) Ex.: Customer Meeting
752 * @param meeting_location (%s) Ex.: Conf Room 100
754 * @param instance (%u) Ex.: 1339299275
755 * @param version (%u) Ex.: 1
756 * @param uri (%s) Ex.: john@contoso.com
757 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
758 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
759 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
760 * @param meeting_subject (%s) Ex.: Customer Meeting
761 * @param meeting_location (%s) Ex.: Conf Room 100
763 #define SIPE_PUB_XML_STATE_CALENDAR \
764 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
765 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
766 "%s"\
767 "%s"\
768 "<endpointLocation/>"\
769 "<meetingSubject>%s</meetingSubject>"\
770 "<meetingLocation>%s</meetingLocation>"\
771 "</state>"\
772 "</publication>"\
773 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
774 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
775 "%s"\
776 "%s"\
777 "<endpointLocation/>"\
778 "<meetingSubject>%s</meetingSubject>"\
779 "<meetingLocation>%s</meetingLocation>"\
780 "</state>"\
781 "</publication>"
783 * Publishes to clear 'calendarState' and 'phoneState' category
784 * @param instance (%u) Ex.: 1251210982
785 * @param version (%u) Ex.: 1
787 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
788 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
789 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
792 * Publishes to clear any category
793 * @param category_name (%s) Ex.: state
794 * @param instance (%u) Ex.: 536870912
795 * @param container (%u) Ex.: 3
796 * @param version (%u) Ex.: 1
797 * @param expireType (%s) Ex.: static
799 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
800 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
803 * Publishes 'note' category.
804 * @param instance (%u) Ex.: 2135971629; 0 for personal
805 * @param container (%u) Ex.: 200
806 * @param version (%u) Ex.: 2
807 * @param type (%s) Ex.: personal or OOF
808 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
809 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
810 * @param body (%s) Ex.: In the office
812 #define SIPE_PUB_XML_NOTE \
813 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
814 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
815 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
816 "</note>"\
817 "</publication>"
819 * Publishes 'phoneState' category.
820 * @param instance (%u) Ex.: 1339299275
821 * @param version (%u) Ex.: 1
822 * @param availability (%u) Ex.: 6500
823 * @param token (%s) Ex.: on-the-phone
824 * @param minAvailability (%u) generally same as availability
826 * @param instance (%u) Ex.: 1339299275
827 * @param version (%u) Ex.: 1
828 * @param availability (%u) Ex.: 6500
829 * @param token (%s) Ex.: on-the-phone
830 * @param minAvailability (%u) generally same as availability
832 #define SIPE_PUB_XML_STATE_PHONE \
833 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
834 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
835 "<availability>%u</availability>"\
836 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
837 "</state>"\
838 "</publication>"\
839 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
840 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
841 "<availability>%u</availability>"\
842 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
843 "</state>"\
844 "</publication>"
847 * Only Busy and OOF calendar event are published.
848 * Different instances are used for that.
850 * Must be g_free'd after use.
852 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
853 struct sipe_cal_event *event,
854 const char *uri,
855 int cal_satus)
857 gchar *start_time_str;
858 int availability = 0;
859 gchar *res;
860 gchar *tmp = NULL;
861 guint instance = (cal_satus == SIPE_CAL_OOF) ?
862 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
863 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
865 /* key is <category><instance><container> */
866 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
867 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
868 struct sipe_publication *publication_2 =
869 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
870 struct sipe_publication *publication_3 =
871 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
873 g_free(key_2);
874 g_free(key_3);
876 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
877 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
878 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
879 return NULL;
882 if (event &&
883 publication_3 &&
884 (publication_3->availability == availability) &&
885 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
887 g_free(tmp);
888 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
889 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
890 return NULL; /* nothing to update */
892 g_free(tmp);
894 if (event &&
895 (event->cal_status == SIPE_CAL_BUSY ||
896 event->cal_status == SIPE_CAL_OOF))
898 gchar *availability_xml_str = NULL;
899 gchar *activity_xml_str = NULL;
900 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
901 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
903 if (event->cal_status == SIPE_CAL_BUSY) {
904 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
905 SIPE_OCS2007_AVAILABILITY_BUSY);
908 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
909 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
910 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
911 "minAvailability=\"6500\"",
912 "maxAvailability=\"8999\"");
913 } else if (event->cal_status == SIPE_CAL_OOF) {
914 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
915 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
916 "minAvailability=\"12000\"",
917 "");
919 start_time_str = sipe_utils_time_to_str(event->start_time);
921 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
922 instance,
923 publication_2 ? publication_2->version : 0,
924 uri,
925 start_time_str,
926 availability_xml_str ? availability_xml_str : "",
927 activity_xml_str ? activity_xml_str : "",
928 escaped_subject ? escaped_subject : "",
929 escaped_location ? escaped_location : "",
931 instance,
932 publication_3 ? publication_3->version : 0,
933 uri,
934 start_time_str,
935 availability_xml_str ? availability_xml_str : "",
936 activity_xml_str ? activity_xml_str : "",
937 escaped_subject ? escaped_subject : "",
938 escaped_location ? escaped_location : ""
940 g_free(escaped_location);
941 g_free(escaped_subject);
942 g_free(start_time_str);
943 g_free(availability_xml_str);
944 g_free(activity_xml_str);
947 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
949 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
950 instance,
951 publication_2 ? publication_2->version : 0,
953 instance,
954 publication_3 ? publication_3->version : 0
958 return res;
962 * Returns 'note' XML part for publication.
963 * Must be g_free'd after use.
965 * Protocol format for Note is plain text.
967 * @param note a note in Sipe internal HTML format
968 * @param note_type either personal or OOF
970 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
971 const char *note, /* html */
972 const char *note_type,
973 time_t note_start,
974 time_t note_end,
975 gboolean force_publish)
977 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
978 /* key is <category><instance><container> */
979 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
980 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
981 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
983 struct sipe_publication *publication_note_200 =
984 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_200);
985 struct sipe_publication *publication_note_300 =
986 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_300);
987 struct sipe_publication *publication_note_400 =
988 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_400);
990 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
991 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
992 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
993 char *res, *tmp1, *tmp2, *tmp3;
994 char *start_time_attr;
995 char *end_time_attr;
997 g_free(tmp);
998 tmp = NULL;
999 g_free(key_note_200);
1000 g_free(key_note_300);
1001 g_free(key_note_400);
1003 /* we even need to republish empty note */
1004 if (!force_publish && sipe_strequal(n1, n2))
1006 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1007 g_free(n1);
1008 return NULL; /* nothing to update */
1011 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1012 g_free(tmp);
1013 tmp = NULL;
1014 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1015 g_free(tmp);
1017 if (n1) {
1018 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1019 instance,
1020 200,
1021 publication_note_200 ? publication_note_200->version : 0,
1022 note_type,
1023 start_time_attr ? start_time_attr : "",
1024 end_time_attr ? end_time_attr : "",
1025 n1);
1027 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1028 instance,
1029 300,
1030 publication_note_300 ? publication_note_300->version : 0,
1031 note_type,
1032 start_time_attr ? start_time_attr : "",
1033 end_time_attr ? end_time_attr : "",
1034 n1);
1036 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1037 instance,
1038 400,
1039 publication_note_400 ? publication_note_400->version : 0,
1040 note_type,
1041 start_time_attr ? start_time_attr : "",
1042 end_time_attr ? end_time_attr : "",
1043 n1);
1044 } else {
1045 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1046 "note",
1047 instance,
1048 200,
1049 publication_note_200 ? publication_note_200->version : 0,
1050 "static");
1051 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1052 "note",
1053 instance,
1054 300,
1055 publication_note_200 ? publication_note_200->version : 0,
1056 "static");
1057 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1058 "note",
1059 instance,
1060 400,
1061 publication_note_200 ? publication_note_200->version : 0,
1062 "static");
1064 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
1066 g_free(start_time_attr);
1067 g_free(end_time_attr);
1068 g_free(tmp1);
1069 g_free(tmp2);
1070 g_free(tmp3);
1071 g_free(n1);
1073 return res;
1077 * Publishes 'calendarData' category's WorkingHours.
1079 * @param version (%u) Ex.: 1
1080 * @param email (%s) Ex.: alice@cosmo.local
1081 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1083 * @param version (%u)
1085 * @param version (%u)
1086 * @param email (%s)
1087 * @param working_hours_xml_str (%s)
1089 * @param version (%u)
1090 * @param email (%s)
1091 * @param working_hours_xml_str (%s)
1093 * @param version (%u)
1094 * @param email (%s)
1095 * @param working_hours_xml_str (%s)
1097 * @param version (%u)
1099 #define SIPE_PUB_XML_WORKING_HOURS \
1100 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1101 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1102 "</calendarData>"\
1103 "</publication>"\
1104 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1105 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1106 "</publication>"\
1107 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1108 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1109 "</calendarData>"\
1110 "</publication>"\
1111 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1112 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1113 "</calendarData>"\
1114 "</publication>"\
1115 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1116 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1117 "</calendarData>"\
1118 "</publication>"\
1119 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1120 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1121 "</publication>"
1124 * Returns 'calendarData' XML part with WorkingHours for publication.
1125 * Must be g_free'd after use.
1127 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1129 struct sipe_calendar* cal = sipe_private->calendar;
1131 /* key is <category><instance><container> */
1132 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1133 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1134 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1135 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1136 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1137 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1139 struct sipe_publication *publication_cal_1 =
1140 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1141 struct sipe_publication *publication_cal_100 =
1142 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1143 struct sipe_publication *publication_cal_200 =
1144 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1145 struct sipe_publication *publication_cal_300 =
1146 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1147 struct sipe_publication *publication_cal_400 =
1148 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1149 struct sipe_publication *publication_cal_32000 =
1150 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1152 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1153 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1155 g_free(key_cal_1);
1156 g_free(key_cal_100);
1157 g_free(key_cal_200);
1158 g_free(key_cal_300);
1159 g_free(key_cal_400);
1160 g_free(key_cal_32000);
1162 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1163 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1164 return NULL;
1167 if (sipe_strequal(n1, n2))
1169 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1170 return NULL; /* nothing to update */
1173 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1174 /* 1 */
1175 publication_cal_1 ? publication_cal_1->version : 0,
1176 cal->email,
1177 cal->working_hours_xml_str,
1178 /* 100 - Public */
1179 publication_cal_100 ? publication_cal_100->version : 0,
1180 /* 200 - Company */
1181 publication_cal_200 ? publication_cal_200->version : 0,
1182 cal->email,
1183 cal->working_hours_xml_str,
1184 /* 300 - Team */
1185 publication_cal_300 ? publication_cal_300->version : 0,
1186 cal->email,
1187 cal->working_hours_xml_str,
1188 /* 400 - Personal */
1189 publication_cal_400 ? publication_cal_400->version : 0,
1190 cal->email,
1191 cal->working_hours_xml_str,
1192 /* 32000 - Blocked */
1193 publication_cal_32000 ? publication_cal_32000->version : 0
1198 * Publishes 'calendarData' category's FreeBusy.
1200 * @param instance (%u) Ex.: 1300372959
1201 * @param version (%u) Ex.: 1
1203 * @param instance (%u) Ex.: 1300372959
1204 * @param version (%u) Ex.: 1
1206 * @param instance (%u) Ex.: 1300372959
1207 * @param version (%u) Ex.: 1
1208 * @param email (%s) Ex.: alice@cosmo.local
1209 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1210 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1212 * @param instance (%u) Ex.: 1300372959
1213 * @param version (%u) Ex.: 1
1214 * @param email (%s) Ex.: alice@cosmo.local
1215 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1216 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1218 * @param instance (%u) Ex.: 1300372959
1219 * @param version (%u) Ex.: 1
1220 * @param email (%s) Ex.: alice@cosmo.local
1221 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1222 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1224 * @param instance (%u) Ex.: 1300372959
1225 * @param version (%u) Ex.: 1
1227 #define SIPE_PUB_XML_FREE_BUSY \
1228 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1229 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1230 "</publication>"\
1231 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1232 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1233 "</publication>"\
1234 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1235 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1236 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1237 "</calendarData>"\
1238 "</publication>"\
1239 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1240 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1241 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1242 "</calendarData>"\
1243 "</publication>"\
1244 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1245 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1246 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1247 "</calendarData>"\
1248 "</publication>"\
1249 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1250 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1251 "</publication>"
1254 * Returns 'calendarData' XML part with FreeBusy for publication.
1255 * Must be g_free'd after use.
1257 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1259 struct sipe_calendar* cal = sipe_private->calendar;
1260 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1261 char *fb_start_str;
1262 char *free_busy_base64;
1263 /* const char *st; */
1264 /* const char *fb; */
1265 char *res;
1267 /* key is <category><instance><container> */
1268 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1269 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1270 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1271 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1272 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1273 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1275 struct sipe_publication *publication_cal_1 =
1276 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1277 struct sipe_publication *publication_cal_100 =
1278 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1279 struct sipe_publication *publication_cal_200 =
1280 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1281 struct sipe_publication *publication_cal_300 =
1282 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1283 struct sipe_publication *publication_cal_400 =
1284 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1285 struct sipe_publication *publication_cal_32000 =
1286 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1288 g_free(key_cal_1);
1289 g_free(key_cal_100);
1290 g_free(key_cal_200);
1291 g_free(key_cal_300);
1292 g_free(key_cal_400);
1293 g_free(key_cal_32000);
1295 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1296 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1297 return NULL;
1300 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1301 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1303 /* we will rebuplish the same data to refresh publication time,
1304 * so if data from multiple sources, most recent will be choosen
1306 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1307 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1309 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1311 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1312 // g_free(fb_start_str);
1313 // g_free(free_busy_base64);
1314 // return NULL; /* nothing to update */
1317 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1318 /* 1 */
1319 cal_data_instance,
1320 publication_cal_1 ? publication_cal_1->version : 0,
1321 /* 100 - Public */
1322 cal_data_instance,
1323 publication_cal_100 ? publication_cal_100->version : 0,
1324 /* 200 - Company */
1325 cal_data_instance,
1326 publication_cal_200 ? publication_cal_200->version : 0,
1327 cal->email,
1328 fb_start_str,
1329 free_busy_base64,
1330 /* 300 - Team */
1331 cal_data_instance,
1332 publication_cal_300 ? publication_cal_300->version : 0,
1333 cal->email,
1334 fb_start_str,
1335 free_busy_base64,
1336 /* 400 - Personal */
1337 cal_data_instance,
1338 publication_cal_400 ? publication_cal_400->version : 0,
1339 cal->email,
1340 fb_start_str,
1341 free_busy_base64,
1342 /* 32000 - Blocked */
1343 cal_data_instance,
1344 publication_cal_32000 ? publication_cal_32000->version : 0
1347 g_free(fb_start_str);
1348 g_free(free_busy_base64);
1349 return res;
1352 #ifdef HAVE_VV
1353 #define SIPE_PUB_XML_DEVICE_VV \
1354 "<voice capture=\"true\" render=\"true\" publish=\"false\"/>"\
1355 "<video capture=\"true\" render=\"true\" publish=\"false\"/>"
1356 #else
1357 #define SIPE_PUB_XML_DEVICE_VV
1358 #endif
1360 #ifdef HAVE_FREERDP
1361 #define SIPE_PUB_XML_DEVICE_APPSHARE \
1362 "<applicationSharing capture=\"true\" render=\"true\" publish=\"false\"/>"\
1363 "<contentPowerPoint capture=\"true\" render=\"true\" publish=\"false\"/>"
1364 #else
1365 #define SIPE_PUB_XML_DEVICE_APPSHARE
1366 #endif
1369 * Publishes 'device' category.
1370 * @param instance (%u) Ex.: 1938468728
1371 * @param version (%u) Ex.: 1
1372 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1373 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1374 * @param timezone (%s) Ex.: 00:00:00+01:00
1375 * @param machineName (%s) Ex.: BOSTON-OCS07
1377 #define SIPE_PUB_XML_DEVICE \
1378 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1379 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1380 "<capabilities preferred=\"false\" uri=\"%s\">"\
1381 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1382 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1383 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1384 SIPE_PUB_XML_DEVICE_VV\
1385 SIPE_PUB_XML_DEVICE_APPSHARE\
1386 "</capabilities>"\
1387 "<timezone>%s</timezone>"\
1388 "<machineName>%s</machineName>"\
1389 "</device>"\
1390 "</publication>"
1393 * Returns 'device' XML part for publication.
1394 * Must be g_free'd after use.
1396 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1398 gchar *uri;
1399 gchar *doc;
1400 gchar *uuid = get_uuid(sipe_private);
1401 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1402 /* key is <category><instance><container> */
1403 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1404 GHashTable *tmp = g_hash_table_lookup(sipe_private->our_publications, "device");
1405 struct sipe_publication *publication = tmp ? g_hash_table_lookup(tmp, key) : NULL;
1407 g_free(key);
1409 uri = sip_uri_self(sipe_private);
1410 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1411 device_instance,
1412 publication ? publication->version : 0,
1413 uuid,
1414 uri,
1415 "00:00:00+01:00", /* @TODO make timezone real*/
1416 g_get_host_name()
1419 g_free(uri);
1420 g_free(uuid);
1422 return doc;
1426 * Publishes 'machineState' category.
1427 * @param instance (%u) Ex.: 926460663
1428 * @param version (%u) Ex.: 22
1429 * @param availability (%d) Ex.: 3500
1430 * @param instance (%u) Ex.: 926460663
1431 * @param version (%u) Ex.: 22
1432 * @param availability (%d) Ex.: 3500
1434 #define SIPE_PUB_XML_STATE_MACHINE \
1435 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1436 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1437 "<availability>%d</availability>"\
1438 "<endpointLocation/>"\
1439 "</state>"\
1440 "</publication>"\
1441 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1442 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1443 "<availability>%d</availability>"\
1444 "<endpointLocation/>"\
1445 "</state>"\
1446 "</publication>"
1449 * Publishes 'userState' category.
1450 * @param instance (%u) User. Ex.: 536870912
1451 * @param version (%u) User Container 2. Ex.: 22
1452 * @param availability (%d) User Container 2. Ex.: 15500
1453 * @param instance (%u) User. Ex.: 536870912
1454 * @param version (%u) User Container 3.Ex.: 22
1455 * @param availability (%d) User Container 3. Ex.: 15500
1457 #define SIPE_PUB_XML_STATE_USER \
1458 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1459 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1460 "<availability>%d</availability>"\
1461 "<endpointLocation/>"\
1462 "</state>"\
1463 "</publication>"\
1464 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1465 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1466 "<availability>%d</availability>"\
1467 "<endpointLocation/>"\
1468 "</state>"\
1469 "</publication>"
1472 * A service method - use
1473 * - send_publish_get_category_state_machine and
1474 * - send_publish_get_category_state_user instead.
1475 * Must be g_free'd after use.
1477 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1478 gboolean force_publish,
1479 gboolean is_user_state)
1481 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1482 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1483 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1484 /* key is <category><instance><container> */
1485 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1486 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1487 struct sipe_publication *publication_2 =
1488 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1489 struct sipe_publication *publication_3 =
1490 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1492 g_free(key_2);
1493 g_free(key_3);
1495 if (!force_publish && publication_2 && (publication_2->availability == availability))
1497 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1498 return NULL; /* nothing to update */
1501 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1502 instance,
1503 publication_2 ? publication_2->version : 0,
1504 availability,
1505 instance,
1506 publication_3 ? publication_3->version : 0,
1507 availability);
1511 * Returns 'machineState' XML part for publication.
1512 * Must be g_free'd after use.
1514 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private,
1515 gboolean force_publish)
1517 return sipe_publish_get_category_state(sipe_private, force_publish, FALSE);
1521 * Returns 'userState' XML part for publication.
1522 * Must be g_free'd after use.
1524 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private,
1525 gboolean force_publish)
1527 return sipe_publish_get_category_state(sipe_private, force_publish, TRUE);
1530 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1532 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1533 gchar *pub_machine;
1534 gchar *publications;
1536 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1538 pub_machine = sipe_publish_get_category_state_machine(sipe_private,
1539 TRUE);
1540 publications = g_strdup_printf("%s%s",
1541 pub_device,
1542 pub_machine ? pub_machine : "");
1543 g_free(pub_device);
1544 g_free(pub_machine);
1546 send_presence_publish(sipe_private, publications);
1547 g_free(publications);
1550 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1551 struct sipmsg *msg,
1552 struct transaction *trans)
1554 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1556 if (msg->response == 200 && g_str_has_prefix(contenttype, "application/vnd-microsoft-roaming-self+xml")) {
1557 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1558 } else if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1559 sipe_xml *xml;
1560 const sipe_xml *node;
1561 gchar *fault_code;
1562 GHashTable *faults;
1563 int index_our;
1564 gboolean has_device_publication = FALSE;
1566 xml = sipe_xml_parse(msg->body, msg->bodylen);
1568 /* test if version mismatch fault */
1569 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1570 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1571 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1572 g_free(fault_code);
1573 sipe_xml_free(xml);
1574 return TRUE;
1576 g_free(fault_code);
1578 /* accumulating information about faulty versions */
1579 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1580 for (node = sipe_xml_child(xml, "details/operation");
1581 node;
1582 node = sipe_xml_twin(node))
1584 const gchar *index = sipe_xml_attribute(node, "index");
1585 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1587 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1588 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1590 sipe_xml_free(xml);
1592 /* here we are parsing our own request to figure out what publication
1593 * referenced here only by index went wrong
1595 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1597 /* publication */
1598 for (node = sipe_xml_child(xml, "publications/publication"),
1599 index_our = 1; /* starts with 1 - our first publication */
1600 node;
1601 node = sipe_xml_twin(node), index_our++)
1603 gchar *idx = g_strdup_printf("%d", index_our);
1604 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1605 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1606 g_free(idx);
1608 if (sipe_strequal("device", categoryName)) {
1609 has_device_publication = TRUE;
1612 if (curVersion) { /* fault exist on this index */
1613 const gchar *container = sipe_xml_attribute(node, "container");
1614 const gchar *instance = sipe_xml_attribute(node, "instance");
1615 /* key is <category><instance><container> */
1616 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1617 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1619 if (category) {
1620 struct sipe_publication *publication =
1621 g_hash_table_lookup(category, key);
1623 SIPE_DEBUG_INFO("key is %s", key);
1625 if (publication) {
1626 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1627 key, curVersion, publication->version);
1628 /* updating publication's version to the correct one */
1629 publication->version = atoi(curVersion);
1631 } else {
1632 /* We somehow lost this category from our publications... */
1633 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1634 publication->category = g_strdup(categoryName);
1635 publication->instance = atoi(instance);
1636 publication->container = atoi(container);
1637 publication->version = atoi(curVersion);
1638 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1639 g_free, (GDestroyNotify)free_publication);
1640 g_hash_table_insert(category, g_strdup(key), publication);
1641 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1642 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1644 g_free(key);
1647 sipe_xml_free(xml);
1648 g_hash_table_destroy(faults);
1650 /* rebublishing with right versions */
1651 if (has_device_publication) {
1652 send_publish_category_initial(sipe_private);
1653 } else {
1654 sipe_ocs2007_category_publish(sipe_private, TRUE);
1657 return TRUE;
1661 * Publishes categories.
1662 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1663 * @param publications (%s) XML publications
1665 #define SIPE_SEND_PRESENCE \
1666 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1667 "<publications uri=\"%s\">"\
1668 "%s"\
1669 "</publications>"\
1670 "</publish>"
1672 static void send_presence_publish(struct sipe_core_private *sipe_private,
1673 const char *publications)
1675 gchar *uri;
1676 gchar *doc;
1677 gchar *tmp;
1678 gchar *hdr;
1680 uri = sip_uri_self(sipe_private);
1681 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1682 uri,
1683 publications);
1685 tmp = get_contact(sipe_private);
1686 hdr = g_strdup_printf("Contact: %s\r\n"
1687 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1689 sip_transport_service(sipe_private,
1690 uri,
1691 hdr,
1692 doc,
1693 process_send_presence_category_publish_response);
1695 g_free(tmp);
1696 g_free(hdr);
1697 g_free(uri);
1698 g_free(doc);
1702 * Publishes self status
1703 * based on own calendar information.
1705 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1706 SIPE_UNUSED_PARAMETER void *unused)
1708 struct sipe_calendar* cal = sipe_private->calendar;
1709 struct sipe_cal_event* event = NULL;
1710 gchar *pub_cal_working_hours = NULL;
1711 gchar *pub_cal_free_busy = NULL;
1712 gchar *pub_calendar = NULL;
1713 gchar *pub_calendar2 = NULL;
1714 gchar *pub_oof_note = NULL;
1715 const gchar *oof_note;
1716 time_t oof_start = 0;
1717 time_t oof_end = 0;
1719 if (!cal) {
1720 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1721 return;
1724 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1725 if (cal->cal_events) {
1726 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1729 if (event) {
1730 sipe_cal_event_debug(event, "publish_calendar_status_self: current event is:\n");
1731 } else {
1732 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1735 /* Logic
1736 if OOF
1737 OOF publish, Busy clean
1738 ilse if Busy
1739 OOF clean, Busy publish
1740 else
1741 OOF clean, Busy clean
1743 if (event && event->cal_status == SIPE_CAL_OOF) {
1744 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1745 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1746 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1747 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1748 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1749 } else {
1750 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1751 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1754 oof_note = sipe_ews_get_oof_note(cal);
1755 if (sipe_strequal("Scheduled", cal->oof_state)) {
1756 oof_start = cal->oof_start;
1757 oof_end = cal->oof_end;
1759 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end, FALSE);
1761 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1762 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1764 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1765 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1766 } else {
1767 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1768 pub_cal_working_hours ? pub_cal_working_hours : "",
1769 pub_cal_free_busy ? pub_cal_free_busy : "",
1770 pub_calendar ? pub_calendar : "",
1771 pub_calendar2 ? pub_calendar2 : "",
1772 pub_oof_note ? pub_oof_note : "");
1774 send_presence_publish(sipe_private, publications);
1775 g_free(publications);
1778 g_free(pub_cal_working_hours);
1779 g_free(pub_cal_free_busy);
1780 g_free(pub_calendar);
1781 g_free(pub_calendar2);
1782 g_free(pub_oof_note);
1784 /* repeat scheduling */
1785 schedule_publish_update(sipe_private, time(NULL));
1788 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
1789 gboolean force_publish)
1791 GString *publications = g_string_new("");
1792 gchar *tmp;
1794 if (force_publish || sipe_private->status_set_by_user) {
1795 tmp = sipe_publish_get_category_state_user(sipe_private,
1796 force_publish);
1797 if (tmp) {
1798 g_string_append(publications, tmp);
1799 g_free(tmp);
1803 tmp = sipe_publish_get_category_state_machine(sipe_private,
1804 force_publish);
1805 if (tmp) {
1806 g_string_append(publications, tmp);
1807 g_free(tmp);
1810 tmp = sipe_publish_get_category_note(sipe_private,
1811 sipe_private->note,
1812 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1815 force_publish);
1816 if (tmp) {
1817 g_string_append(publications, tmp);
1818 g_free(tmp);
1821 if (publications->len)
1822 send_presence_publish(sipe_private, publications->str);
1823 else
1824 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1826 g_string_free(publications, TRUE);
1829 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1831 gchar *publications = NULL;
1832 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1834 /* key is <category><instance><container> */
1835 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1836 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1837 struct sipe_publication *publication_2 =
1838 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1839 struct sipe_publication *publication_3 =
1840 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1841 g_free(key_2);
1842 g_free(key_3);
1844 #ifdef HAVE_VV
1845 if (g_hash_table_size(sipe_private->media_calls)) {
1846 guint availability = 0;
1847 const gchar *token = NULL;
1848 GList *calls = g_hash_table_get_values(sipe_private->media_calls);
1849 GList *i;
1851 if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) {
1852 availability = 6500;
1853 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1856 for (i = calls; i; i = i->next) {
1857 if (sipe_media_is_conference_call(i->data)) {
1858 availability = 7000;
1859 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1860 break;
1864 g_list_free(calls);
1866 if (token) {
1867 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1868 instance, publication_2 ? publication_2->version : 0,
1869 availability, token, availability,
1870 instance, publication_3 ? publication_3->version : 0,
1871 availability, token, availability);
1873 } else
1874 #endif
1876 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1877 instance, publication_2 ? publication_2->version : 0,
1878 instance, publication_3 ? publication_3->version : 0);
1881 if (publications) {
1882 send_presence_publish(sipe_private, publications);
1883 g_free(publications);
1887 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1888 gpointer value,
1889 GString* str)
1891 struct sipe_publication *publication = value;
1893 g_string_append_printf( str,
1894 SIPE_PUB_XML_PUBLICATION_CLEAR,
1895 publication->category,
1896 publication->instance,
1897 publication->container,
1898 publication->version,
1899 "static");
1902 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1904 GString* str;
1905 gchar *publications;
1907 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1908 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1909 return;
1912 str = g_string_new(NULL);
1913 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1914 publications = g_string_free(str, FALSE);
1916 send_presence_publish(sipe_private, publications);
1917 g_free(publications);
1920 /* key is <category><instance><container> */
1921 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1922 const gchar *key)
1924 GSList *entry;
1926 /* filling keys for our publications if not yet cached */
1927 if (!sipe_private->our_publication_keys) {
1928 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1929 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1930 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1931 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1932 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1933 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1934 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1935 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1937 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1938 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1939 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1940 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1941 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1942 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1943 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1944 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1945 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1946 SIPE_DEBUG_INFO("\tNote : %u", 0);
1947 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1949 /* device */
1950 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1951 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1953 /* state:machineState */
1954 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1955 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1956 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1957 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1959 /* state:userState */
1960 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1961 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1962 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1963 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1965 /* state:calendarState */
1966 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1967 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1968 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1969 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1971 /* state:calendarState OOF */
1972 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1973 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1974 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1975 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1977 /* state:phoneState */
1978 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1979 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1980 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1981 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1983 /* note */
1984 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1985 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1986 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1987 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1988 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1989 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1991 /* note OOF */
1992 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1993 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1994 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1995 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1996 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1997 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1999 /* calendarData:WorkingHours */
2000 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2001 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2002 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2003 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2004 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2005 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2006 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2007 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2008 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2009 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2010 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2011 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2013 /* calendarData:FreeBusy */
2014 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2015 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
2016 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2017 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
2018 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2019 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
2020 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2021 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
2022 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2023 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
2024 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2025 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
2027 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
2028 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
2031 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2033 entry = sipe_private->our_publication_keys;
2034 while (entry) {
2035 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2036 if (sipe_strequal(entry->data, key)) {
2037 return TRUE;
2039 entry = entry->next;
2041 return FALSE;
2044 static void sipe_refresh_blocked_status_cb(char *buddy_name,
2045 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
2046 struct sipe_core_private *sipe_private)
2048 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2049 gboolean blocked = (container_id == 32000);
2050 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2052 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2053 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2055 if (blocked != blocked_in_blist) {
2056 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2060 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2062 sipe_buddy_foreach(sipe_private,
2063 (GHFunc) sipe_refresh_blocked_status_cb,
2064 sipe_private);
2068 * When we receive some self (BE) NOTIFY with a new subscriber
2069 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2072 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2073 struct sipmsg *msg)
2075 gchar *contact;
2076 gchar *to;
2077 sipe_xml *xml;
2078 const sipe_xml *node;
2079 const sipe_xml *node2;
2080 char *display_name = NULL;
2081 char *uri;
2082 GSList *category_names = NULL;
2083 int aggreg_avail = 0;
2084 gchar *activity_token = NULL;
2085 gboolean do_update_status = FALSE;
2086 gboolean has_note_cleaned = FALSE;
2087 GHashTable *devices;
2089 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2091 xml = sipe_xml_parse(msg->body, msg->bodylen);
2092 if (!xml) return;
2094 contact = get_contact(sipe_private);
2095 to = sip_uri_self(sipe_private);
2097 /* categories */
2098 /* set list of categories participating in this XML */
2099 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2100 const gchar *name = sipe_xml_attribute(node, "name");
2101 category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2102 (gchar *)name,
2103 (GCompareFunc)strcmp,
2104 NULL);
2106 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2107 category_names ? (int) g_slist_length(category_names) : -1);
2108 /* drop category information */
2109 if (category_names) {
2110 GSList *entry = category_names;
2111 while (entry) {
2112 GHashTable *cat_publications;
2113 const gchar *category = entry->data;
2114 entry = entry->next;
2115 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2116 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2117 if (cat_publications) {
2118 g_hash_table_remove(sipe_private->our_publications, category);
2119 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2123 g_slist_free(category_names);
2125 /* filling our categories reflected in roaming data */
2126 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2127 g_free, NULL);
2128 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2129 const char *tmp;
2130 const gchar *name = sipe_xml_attribute(node, "name");
2131 guint container = sipe_xml_int_attribute(node, "container", -1);
2132 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2133 guint version = sipe_xml_int_attribute(node, "version", 0);
2134 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2135 sipe_utils_str_to_time(tmp) : 0;
2136 gchar *key;
2137 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2139 /* Ex. clear note: <category name="note"/> */
2140 if (container == (guint)-1) {
2141 g_free(sipe_private->note);
2142 sipe_private->note = NULL;
2143 do_update_status = TRUE;
2144 continue;
2147 /* Ex. clear note: <category name="note" container="200"/> */
2148 if (instance == (guint)-1) {
2149 if (container == 200) {
2150 g_free(sipe_private->note);
2151 sipe_private->note = NULL;
2152 do_update_status = TRUE;
2154 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2155 sipe_remove_category_container_publications(
2156 sipe_private->our_publications, name, container);
2157 continue;
2160 /* key is <category><instance><container> */
2161 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2162 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2164 /* capture all userState publication for later clean up if required */
2165 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2166 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2168 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2169 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2170 publication->category = g_strdup(name);
2171 publication->instance = instance;
2172 publication->container = container;
2173 publication->version = version;
2175 if (!sipe_private->user_state_publications) {
2176 sipe_private->user_state_publications = g_hash_table_new_full(
2177 g_str_hash, g_str_equal,
2178 g_free, (GDestroyNotify)free_publication);
2180 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2181 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2182 key, version);
2186 /* count each client instance only once */
2187 if (sipe_strequal(name, "device"))
2188 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2190 if (sipe_is_our_publication(sipe_private, key)) {
2191 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2193 publication->category = g_strdup(name);
2194 publication->instance = instance;
2195 publication->container = container;
2196 publication->version = version;
2198 /* filling publication->availability */
2199 if (sipe_strequal(name, "state")) {
2200 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2201 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2203 if (xn_avail) {
2204 gchar *avail_str = sipe_xml_data(xn_avail);
2205 if (avail_str) {
2206 publication->availability = atoi(avail_str);
2208 g_free(avail_str);
2210 /* for calendarState */
2211 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2212 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2213 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2215 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2216 if (xn_activity) {
2217 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2218 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2220 event->is_meeting = TRUE;
2223 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2224 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2226 publication->cal_event_hash = sipe_cal_event_hash(event);
2227 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2228 publication->cal_event_hash);
2229 sipe_cal_event_free(event);
2232 /* filling publication->note */
2233 if (sipe_strequal(name, "note")) {
2234 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2236 if (!has_note_cleaned) {
2237 has_note_cleaned = TRUE;
2239 g_free(sipe_private->note);
2240 sipe_private->note = NULL;
2241 sipe_private->note_since = publish_time;
2243 do_update_status = TRUE;
2246 g_free(publication->note);
2247 publication->note = NULL;
2248 if (xn_body) {
2249 char *tmp;
2251 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2252 g_free(tmp);
2253 if (publish_time >= sipe_private->note_since) {
2254 g_free(sipe_private->note);
2255 sipe_private->note = g_strdup(publication->note);
2256 sipe_private->note_since = publish_time;
2257 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2258 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2259 else
2260 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2262 do_update_status = TRUE;
2267 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2268 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2269 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2270 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2271 if (xn_free_busy) {
2272 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2273 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2275 if (xn_working_hours) {
2276 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2280 if (!cat_publications) {
2281 cat_publications = g_hash_table_new_full(
2282 g_str_hash, g_str_equal,
2283 g_free, (GDestroyNotify)free_publication);
2284 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2285 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2287 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2288 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2290 g_free(key);
2292 /* aggregateState (not an our publication) from 2-nd container */
2293 if (sipe_strequal(name, "state") && container == 2) {
2294 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2295 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2297 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2298 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2300 if (xn_avail) {
2301 gchar *avail_str = sipe_xml_data(xn_avail);
2302 if (avail_str) {
2303 aggreg_avail = atoi(avail_str);
2305 g_free(avail_str);
2308 do_update_status = TRUE;
2311 if (xn_activity) {
2312 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2316 /* userProperties published by server from AD */
2317 if (!sipe_private->csta &&
2318 sipe_strequal(name, "userProperties")) {
2319 const sipe_xml *line;
2320 /* line, for Remote Call Control (RCC) or external Lync/Communicator call */
2321 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2322 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2323 gchar *line_uri = sipe_xml_data(line);
2324 if (!line_uri) {
2325 continue;
2328 if (sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual")) {
2329 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2330 if (line_server) {
2331 gchar *tmp = g_strstrip(line_uri);
2332 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s",
2333 tmp, line_server);
2334 sip_csta_open(sipe_private, tmp, line_server);
2337 #ifdef HAVE_VV
2338 else if (sipe_strequal(line_type, "Uc")) {
2340 if (!sipe_private->uc_line_uri) {
2341 sipe_private->uc_line_uri = g_strdup(g_strstrip(line_uri));
2342 } else {
2343 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: "
2344 "sipe_private->uc_line_uri is already set.");
2347 #endif
2349 g_free(line_uri);
2351 break;
2355 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2356 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2358 /* active clients for user account */
2359 if (g_hash_table_size(devices) == 0) {
2360 /* updated roaming information without device information - no need to update MPOP flag */
2361 } else if (g_hash_table_size(devices) > 1) {
2362 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2363 SIPE_LOG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2364 g_hash_table_size(devices));
2365 } else {
2366 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2367 SIPE_LOG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2369 g_hash_table_destroy(devices);
2371 /* containers */
2372 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2373 guint id = sipe_xml_int_attribute(node, "id", 0);
2374 struct sipe_container *container = sipe_find_container(sipe_private, id);
2376 if (container) {
2377 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2378 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2379 sipe_ocs2007_free_container(container);
2381 container = g_new0(struct sipe_container, 1);
2382 container->id = id;
2383 container->version = sipe_xml_int_attribute(node, "version", 0);
2384 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2385 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2387 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2388 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2389 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2390 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2391 container->members = g_slist_append(container->members, member);
2392 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2393 member->type, member->value ? member->value : "");
2397 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2398 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2399 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2400 char *container_xmls = NULL;
2401 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2402 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2404 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2405 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2406 /* initial set-up to let counterparties see your status */
2407 if (sameEnterpriseAL < 0) {
2408 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2409 guint version = container ? container->version : 0;
2410 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2412 if (federatedAL < 0) {
2413 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2414 guint version = container ? container->version : 0;
2415 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2417 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2419 if (container_xmls) {
2420 sipe_send_set_container_members(sipe_private, container_xmls);
2422 g_free(container_xmls);
2425 /* Refresh contacts' blocked status */
2426 sipe_refresh_blocked_status(sipe_private);
2428 /* subscribers */
2429 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2430 const char *user;
2431 const char *acknowledged;
2432 gchar *hdr;
2433 gchar *body;
2435 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2436 if (!user) continue;
2437 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2438 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2439 uri = sip_uri_from_name(user);
2441 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2442 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2444 acknowledged= sipe_xml_attribute(node, "acknowledged");
2445 if(sipe_strcase_equal(acknowledged,"false")){
2446 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2447 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2448 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2451 hdr = g_strdup_printf(
2452 "Contact: %s\r\n"
2453 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2455 body = g_strdup_printf(
2456 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2457 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2458 "</setSubscribers>", user);
2460 sip_transport_service(sipe_private,
2462 hdr,
2463 body,
2464 NULL);
2465 g_free(body);
2466 g_free(hdr);
2468 g_free(display_name);
2469 g_free(uri);
2472 g_free(contact);
2473 sipe_xml_free(xml);
2475 /* Publish initial state if not yet.
2476 * Assuming this happens on initial responce to subscription to roaming-self
2477 * so we've already updated our roaming data in full.
2478 * Only for 2007+
2480 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2481 send_publish_category_initial(sipe_private);
2482 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2483 /* dalayed run */
2484 sipe_cal_delayed_calendar_update(sipe_private);
2485 do_update_status = FALSE;
2486 } else if (aggreg_avail) {
2488 if (aggreg_avail &&
2489 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2490 /* not offline */
2491 sipe_status_set_token(sipe_private,
2492 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2493 } else {
2494 /* do not let offline status switch us off */
2495 sipe_status_set_activity(sipe_private,
2496 SIPE_ACTIVITY_INVISIBLE);
2500 if (do_update_status) {
2501 sipe_status_and_note(sipe_private, NULL);
2504 g_free(to);
2505 g_free(activity_token);
2509 * for Access levels menu
2511 #define INDENT_FMT " %s"
2514 * Member is indirectly belong to access level container.
2515 * For example 'sameEnterprise' is in the container and user
2516 * belongs to that same enterprise.
2518 #define INDENT_MARKED_INHERITED_FMT "= %s"
2520 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2521 struct sipe_backend_buddy_menu *menu,
2522 const gchar *member_type,
2523 const gchar *member_value,
2524 const gboolean extra_menu)
2526 unsigned int i;
2527 gboolean is_group_access = FALSE;
2528 int container_id;
2530 if (!menu)
2531 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2533 container_id = sipe_ocs2007_find_access_level(sipe_private,
2534 member_type,
2535 member_value,
2536 &is_group_access);
2538 for (i = 1; i <= CONTAINERS_LEN; i++) {
2540 * Blocked should remain in the first place
2541 * in the containers[] array.
2543 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2544 int container_j = containers[j];
2545 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2546 struct sipe_container *container = create_container(j,
2547 member_type,
2548 member_value,
2549 FALSE);
2550 gchar *label;
2552 /* libpurple memory leak workaround */
2553 blist_menu_remember_container(sipe_private, container);
2555 /* current container/access level */
2556 if (container_j == container_id) {
2557 label = is_group_access ?
2558 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2559 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2560 } else {
2561 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2564 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2565 menu,
2566 label,
2567 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2568 container);
2569 g_free(label);
2572 if (extra_menu && (container_id >= 0) && !is_group_access) {
2573 struct sipe_container *container = create_container(0,
2574 member_type,
2575 member_value,
2576 TRUE);
2577 gchar *label;
2579 /* separator */
2580 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2581 menu,
2582 " --------------");
2585 /* libpurple memory leak workaround */
2586 blist_menu_remember_container(sipe_private, container);
2588 /* Translators: remove (clear) previously assigned access level */
2589 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2590 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2591 menu,
2592 label,
2593 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2594 container);
2595 g_free(label);
2598 return(menu);
2601 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2603 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2604 GSList *access_domains, *entry;
2606 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2607 menu,
2608 _("People in my company"),
2609 access_levels_menu(sipe_private,
2610 NULL,
2611 "sameEnterprise",
2612 NULL,
2613 FALSE));
2615 /* this is original name, don't edit */
2616 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2617 menu,
2618 _("People in domains connected with my company"),
2619 access_levels_menu(sipe_private,
2620 NULL,
2621 "federated",
2622 NULL,
2623 FALSE));
2625 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2626 menu,
2627 _("People in public domains"),
2628 access_levels_menu(sipe_private,
2629 NULL,
2630 "publicCloud",
2631 NULL,
2632 TRUE));
2634 entry = access_domains = get_access_domains(sipe_private);
2635 while (entry) {
2636 gchar *domain = entry->data;
2637 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2639 /* takes over ownership of entry->data (= domain) */
2640 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2641 menu,
2642 menu_name,
2643 access_levels_menu(sipe_private,
2644 NULL,
2645 "domain",
2646 domain,
2647 TRUE));
2648 g_free(menu_name);
2650 entry = entry->next;
2652 g_slist_free(access_domains);
2654 /* separator */
2655 /* People in domains connected with my company */
2656 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2657 menu,
2658 "-------------------------------------------");
2660 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2661 menu,
2662 _("Add new domain..."),
2663 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2664 NULL);
2666 return(menu);
2669 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2670 const gchar *buddy_name)
2672 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2673 gchar *label;
2676 * Workaround for missing libpurple API to release resources allocated
2677 * during blist_node_menu() callback. See also:
2679 * <http://developer.pidgin.im/ticket/12597>
2681 * We remember all memory blocks in a list and deallocate them when
2683 * - the next time we enter the callback, or
2684 * - the account is disconnected
2686 * That means that after the buddy menu has been closed we have unused
2687 * resources but at least we don't leak them anymore...
2689 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2691 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2692 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2693 menu,
2694 label,
2695 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2696 NULL);
2697 g_free(label);
2699 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2700 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2701 menu,
2702 label,
2703 access_groups_menu(sipe_private));
2704 g_free(label);
2706 menu = access_levels_menu(sipe_private,
2707 menu,
2708 "user",
2709 sipe_get_no_sip_uri(buddy_name),
2710 TRUE);
2712 return(menu);
2716 Local Variables:
2717 mode: c
2718 c-file-style: "bsd"
2719 indent-tabs-mode: t
2720 tab-width: 8
2721 End: