media: fix relay-info with Farstream 0.2
[siplcs.git] / src / core / sipe-ocs2007.c
blob74a66e35710942627b1758a8a66d3ccf2b46beae
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 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);
641 current_container_id = -1;
646 /* recalculate AL below */
647 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
649 /* assign/publish new access level */
650 if (container_id != current_container_id && container_id >= 0) {
651 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
652 guint version = container ? container->version : 0;
654 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
657 if (container_xmls) {
658 sipe_send_set_container_members(sipe_private, container_xmls);
660 g_free(container_xmls);
663 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
664 gpointer parameter)
666 struct sipe_container *container = parameter;
667 struct sipe_container_member *member;
669 if (!container || !container->members) return;
671 member = ((struct sipe_container_member *)container->members->data);
673 if (!member->type) return;
675 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
676 container->id, member->type, member->value ? member->value : "");
678 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
679 container->id,
680 member->type,
681 member->value);
685 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
686 const gchar *domain,
687 guint index)
689 /* move Blocked first */
690 guint i = (index == 4) ? 0 : index + 1;
691 guint container_id = containers[i];
693 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
694 domain ? domain : "", index, container_id);
696 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
697 container_id,
698 "domain",
699 domain);
703 * Schedules process of self status publish
704 * based on own calendar information.
705 * Should be scheduled to the beginning of every
706 * 15 min interval, like:
707 * 13:00, 13:15, 13:30, 13:45, etc.
710 static void schedule_publish_update(struct sipe_core_private *sipe_private,
711 time_t calculate_from)
713 int interval = 5*60;
714 /** start of the beginning of closest 5 min interval. */
715 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
717 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
718 asctime(localtime(&calculate_from)));
719 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
720 asctime(localtime(&next_start)));
722 sipe_schedule_seconds(sipe_private,
723 "<+2007-cal-status>",
724 NULL,
725 next_start - time(NULL),
726 sipe_ocs2007_presence_publish,
727 NULL);
731 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
732 * @param availability (%d) Ex.: 6500
734 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
735 "<availability>%d</availability>"
737 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
738 * @param token (%s) Ex.: in-a-meeting
739 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
740 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
742 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
743 "<activity token=\"%s\" %s %s></activity>"
745 * Publishes 'calendarState' category.
746 * @param instance (%u) Ex.: 1339299275
747 * @param version (%u) Ex.: 1
748 * @param uri (%s) Ex.: john@contoso.com
749 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
750 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
751 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
752 * @param meeting_subject (%s) Ex.: Customer Meeting
753 * @param meeting_location (%s) Ex.: Conf Room 100
755 * @param instance (%u) Ex.: 1339299275
756 * @param version (%u) Ex.: 1
757 * @param uri (%s) Ex.: john@contoso.com
758 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
759 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
760 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
761 * @param meeting_subject (%s) Ex.: Customer Meeting
762 * @param meeting_location (%s) Ex.: Conf Room 100
764 #define SIPE_PUB_XML_STATE_CALENDAR \
765 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
766 "<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\">"\
767 "%s"\
768 "%s"\
769 "<endpointLocation/>"\
770 "<meetingSubject>%s</meetingSubject>"\
771 "<meetingLocation>%s</meetingLocation>"\
772 "</state>"\
773 "</publication>"\
774 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
775 "<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\">"\
776 "%s"\
777 "%s"\
778 "<endpointLocation/>"\
779 "<meetingSubject>%s</meetingSubject>"\
780 "<meetingLocation>%s</meetingLocation>"\
781 "</state>"\
782 "</publication>"
784 * Publishes to clear 'calendarState' and 'phoneState' category
785 * @param instance (%u) Ex.: 1251210982
786 * @param version (%u) Ex.: 1
788 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
789 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
790 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
793 * Publishes to clear any category
794 * @param category_name (%s) Ex.: state
795 * @param instance (%u) Ex.: 536870912
796 * @param container (%u) Ex.: 3
797 * @param version (%u) Ex.: 1
798 * @param expireType (%s) Ex.: static
800 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
801 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
804 * Publishes 'note' category.
805 * @param instance (%u) Ex.: 2135971629; 0 for personal
806 * @param container (%u) Ex.: 200
807 * @param version (%u) Ex.: 2
808 * @param type (%s) Ex.: personal or OOF
809 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
810 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
811 * @param body (%s) Ex.: In the office
813 #define SIPE_PUB_XML_NOTE \
814 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
815 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
816 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
817 "</note>"\
818 "</publication>"
820 * Publishes 'phoneState' category.
821 * @param instance (%u) Ex.: 1339299275
822 * @param version (%u) Ex.: 1
823 * @param availability (%u) Ex.: 6500
824 * @param token (%s) Ex.: on-the-phone
825 * @param minAvailability (%u) generally same as availability
827 * @param instance (%u) Ex.: 1339299275
828 * @param version (%u) Ex.: 1
829 * @param availability (%u) Ex.: 6500
830 * @param token (%s) Ex.: on-the-phone
831 * @param minAvailability (%u) generally same as availability
833 #define SIPE_PUB_XML_STATE_PHONE \
834 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
835 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
836 "<availability>%u</availability>"\
837 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
838 "</state>"\
839 "</publication>"\
840 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
841 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
842 "<availability>%u</availability>"\
843 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
844 "</state>"\
845 "</publication>"
848 * Only Busy and OOF calendar event are published.
849 * Different instances are used for that.
851 * Must be g_free'd after use.
853 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
854 struct sipe_cal_event *event,
855 const char *uri,
856 int cal_satus)
858 gchar *start_time_str;
859 int availability = 0;
860 gchar *res;
861 gchar *tmp = NULL;
862 guint instance = (cal_satus == SIPE_CAL_OOF) ?
863 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
864 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
866 /* key is <category><instance><container> */
867 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
868 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
869 struct sipe_publication *publication_2 =
870 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
871 struct sipe_publication *publication_3 =
872 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
874 g_free(key_2);
875 g_free(key_3);
877 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
878 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
879 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
880 return NULL;
883 if (event &&
884 publication_3 &&
885 (publication_3->availability == availability) &&
886 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
888 g_free(tmp);
889 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
890 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
891 return NULL; /* nothing to update */
893 g_free(tmp);
895 if (event &&
896 (event->cal_status == SIPE_CAL_BUSY ||
897 event->cal_status == SIPE_CAL_OOF))
899 gchar *availability_xml_str = NULL;
900 gchar *activity_xml_str = NULL;
901 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
902 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
904 if (event->cal_status == SIPE_CAL_BUSY) {
905 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
906 SIPE_OCS2007_AVAILABILITY_BUSY);
909 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
910 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
911 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
912 "minAvailability=\"6500\"",
913 "maxAvailability=\"8999\"");
914 } else if (event->cal_status == SIPE_CAL_OOF) {
915 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
916 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
917 "minAvailability=\"12000\"",
918 "");
920 start_time_str = sipe_utils_time_to_str(event->start_time);
922 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
923 instance,
924 publication_2 ? publication_2->version : 0,
925 uri,
926 start_time_str,
927 availability_xml_str ? availability_xml_str : "",
928 activity_xml_str ? activity_xml_str : "",
929 escaped_subject ? escaped_subject : "",
930 escaped_location ? escaped_location : "",
932 instance,
933 publication_3 ? publication_3->version : 0,
934 uri,
935 start_time_str,
936 availability_xml_str ? availability_xml_str : "",
937 activity_xml_str ? activity_xml_str : "",
938 escaped_subject ? escaped_subject : "",
939 escaped_location ? escaped_location : ""
941 g_free(escaped_location);
942 g_free(escaped_subject);
943 g_free(start_time_str);
944 g_free(availability_xml_str);
945 g_free(activity_xml_str);
948 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
950 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
951 instance,
952 publication_2 ? publication_2->version : 0,
954 instance,
955 publication_3 ? publication_3->version : 0
959 return res;
963 * Returns 'note' XML part for publication.
964 * Must be g_free'd after use.
966 * Protocol format for Note is plain text.
968 * @param note a note in Sipe internal HTML format
969 * @param note_type either personal or OOF
971 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
972 const char *note, /* html */
973 const char *note_type,
974 time_t note_start,
975 time_t note_end)
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 (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;
1354 * Publishes 'device' category.
1355 * @param instance (%u) Ex.: 1938468728
1356 * @param version (%u) Ex.: 1
1357 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1358 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1359 * @param timezone (%s) Ex.: 00:00:00+01:00
1360 * @param machineName (%s) Ex.: BOSTON-OCS07
1362 #define SIPE_PUB_XML_DEVICE \
1363 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1364 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1365 "<capabilities preferred=\"false\" uri=\"%s\">"\
1366 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1367 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1368 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1369 "</capabilities>"\
1370 "<timezone>%s</timezone>"\
1371 "<machineName>%s</machineName>"\
1372 "</device>"\
1373 "</publication>"
1376 * Returns 'device' XML part for publication.
1377 * Must be g_free'd after use.
1379 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1381 gchar *uri;
1382 gchar *doc;
1383 gchar *uuid = get_uuid(sipe_private);
1384 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1385 /* key is <category><instance><container> */
1386 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1387 struct sipe_publication *publication =
1388 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "device"), key);
1390 g_free(key);
1392 uri = sip_uri_self(sipe_private);
1393 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1394 device_instance,
1395 publication ? publication->version : 0,
1396 uuid,
1397 uri,
1398 "00:00:00+01:00", /* @TODO make timezone real*/
1399 g_get_host_name()
1402 g_free(uri);
1403 g_free(uuid);
1405 return doc;
1409 * Publishes 'machineState' category.
1410 * @param instance (%u) Ex.: 926460663
1411 * @param version (%u) Ex.: 22
1412 * @param availability (%d) Ex.: 3500
1413 * @param instance (%u) Ex.: 926460663
1414 * @param version (%u) Ex.: 22
1415 * @param availability (%d) Ex.: 3500
1417 #define SIPE_PUB_XML_STATE_MACHINE \
1418 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1419 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1420 "<availability>%d</availability>"\
1421 "<endpointLocation/>"\
1422 "</state>"\
1423 "</publication>"\
1424 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1425 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1426 "<availability>%d</availability>"\
1427 "<endpointLocation/>"\
1428 "</state>"\
1429 "</publication>"
1432 * Publishes 'userState' category.
1433 * @param instance (%u) User. Ex.: 536870912
1434 * @param version (%u) User Container 2. Ex.: 22
1435 * @param availability (%d) User Container 2. Ex.: 15500
1436 * @param instance (%u) User. Ex.: 536870912
1437 * @param version (%u) User Container 3.Ex.: 22
1438 * @param availability (%d) User Container 3. Ex.: 15500
1440 #define SIPE_PUB_XML_STATE_USER \
1441 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1442 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1443 "<availability>%d</availability>"\
1444 "<endpointLocation/>"\
1445 "</state>"\
1446 "</publication>"\
1447 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1448 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1449 "<availability>%d</availability>"\
1450 "<endpointLocation/>"\
1451 "</state>"\
1452 "</publication>"
1455 * A service method - use
1456 * - send_publish_get_category_state_machine and
1457 * - send_publish_get_category_state_user instead.
1458 * Must be g_free'd after use.
1460 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1461 gboolean is_user_state)
1463 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1464 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1465 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1466 /* key is <category><instance><container> */
1467 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1468 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1469 struct sipe_publication *publication_2 =
1470 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1471 struct sipe_publication *publication_3 =
1472 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1474 g_free(key_2);
1475 g_free(key_3);
1477 if (publication_2 && (publication_2->availability == availability))
1479 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1480 return NULL; /* nothing to update */
1483 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1484 instance,
1485 publication_2 ? publication_2->version : 0,
1486 availability,
1487 instance,
1488 publication_3 ? publication_3->version : 0,
1489 availability);
1493 * Returns 'machineState' XML part for publication.
1494 * Must be g_free'd after use.
1496 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
1498 return sipe_publish_get_category_state(sipe_private, FALSE);
1502 * Returns 'userState' XML part for publication.
1503 * Must be g_free'd after use.
1505 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
1507 return sipe_publish_get_category_state(sipe_private, TRUE);
1510 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1512 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1513 gchar *pub_machine;
1514 gchar *publications;
1516 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1518 pub_machine = sipe_publish_get_category_state_machine(sipe_private);
1519 publications = g_strdup_printf("%s%s",
1520 pub_device,
1521 pub_machine ? pub_machine : "");
1522 g_free(pub_device);
1523 g_free(pub_machine);
1525 send_presence_publish(sipe_private, publications);
1526 g_free(publications);
1529 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1530 struct sipmsg *msg,
1531 struct transaction *trans)
1533 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1535 if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1536 sipe_xml *xml;
1537 const sipe_xml *node;
1538 gchar *fault_code;
1539 GHashTable *faults;
1540 int index_our;
1541 gboolean has_device_publication = FALSE;
1543 xml = sipe_xml_parse(msg->body, msg->bodylen);
1545 /* test if version mismatch fault */
1546 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1547 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1548 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1549 g_free(fault_code);
1550 sipe_xml_free(xml);
1551 return TRUE;
1553 g_free(fault_code);
1555 /* accumulating information about faulty versions */
1556 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1557 for (node = sipe_xml_child(xml, "details/operation");
1558 node;
1559 node = sipe_xml_twin(node))
1561 const gchar *index = sipe_xml_attribute(node, "index");
1562 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1564 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1565 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1567 sipe_xml_free(xml);
1569 /* here we are parsing our own request to figure out what publication
1570 * referenced here only by index went wrong
1572 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1574 /* publication */
1575 for (node = sipe_xml_child(xml, "publications/publication"),
1576 index_our = 1; /* starts with 1 - our first publication */
1577 node;
1578 node = sipe_xml_twin(node), index_our++)
1580 gchar *idx = g_strdup_printf("%d", index_our);
1581 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1582 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1583 g_free(idx);
1585 if (sipe_strequal("device", categoryName)) {
1586 has_device_publication = TRUE;
1589 if (curVersion) { /* fault exist on this index */
1590 const gchar *container = sipe_xml_attribute(node, "container");
1591 const gchar *instance = sipe_xml_attribute(node, "instance");
1592 /* key is <category><instance><container> */
1593 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1594 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1596 if (category) {
1597 struct sipe_publication *publication =
1598 g_hash_table_lookup(category, key);
1600 SIPE_DEBUG_INFO("key is %s", key);
1602 if (publication) {
1603 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1604 key, curVersion, publication->version);
1605 /* updating publication's version to the correct one */
1606 publication->version = atoi(curVersion);
1608 } else {
1609 /* We somehow lost this category from our publications... */
1610 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1611 publication->category = g_strdup(categoryName);
1612 publication->instance = atoi(instance);
1613 publication->container = atoi(container);
1614 publication->version = atoi(curVersion);
1615 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1616 g_free, (GDestroyNotify)free_publication);
1617 g_hash_table_insert(category, g_strdup(key), publication);
1618 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1619 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1621 g_free(key);
1624 sipe_xml_free(xml);
1625 g_hash_table_destroy(faults);
1627 /* rebublishing with right versions */
1628 if (has_device_publication) {
1629 send_publish_category_initial(sipe_private);
1630 } else {
1631 sipe_status_update(sipe_private, NULL);
1634 return TRUE;
1638 * Publishes categories.
1639 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1640 * @param publications (%s) XML publications
1642 #define SIPE_SEND_PRESENCE \
1643 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1644 "<publications uri=\"%s\">"\
1645 "%s"\
1646 "</publications>"\
1647 "</publish>"
1649 static void send_presence_publish(struct sipe_core_private *sipe_private,
1650 const char *publications)
1652 gchar *uri;
1653 gchar *doc;
1654 gchar *tmp;
1655 gchar *hdr;
1657 uri = sip_uri_self(sipe_private);
1658 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1659 uri,
1660 publications);
1662 tmp = get_contact(sipe_private);
1663 hdr = g_strdup_printf("Contact: %s\r\n"
1664 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1666 sip_transport_service(sipe_private,
1667 uri,
1668 hdr,
1669 doc,
1670 process_send_presence_category_publish_response);
1672 g_free(tmp);
1673 g_free(hdr);
1674 g_free(uri);
1675 g_free(doc);
1679 * Publishes self status
1680 * based on own calendar information.
1682 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1683 SIPE_UNUSED_PARAMETER void *unused)
1685 struct sipe_calendar* cal = sipe_private->calendar;
1686 struct sipe_cal_event* event = NULL;
1687 gchar *pub_cal_working_hours = NULL;
1688 gchar *pub_cal_free_busy = NULL;
1689 gchar *pub_calendar = NULL;
1690 gchar *pub_calendar2 = NULL;
1691 gchar *pub_oof_note = NULL;
1692 const gchar *oof_note;
1693 time_t oof_start = 0;
1694 time_t oof_end = 0;
1696 if (!cal) {
1697 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1698 return;
1701 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1702 if (cal->cal_events) {
1703 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1706 if (!event) {
1707 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1708 } else {
1709 char *desc = sipe_cal_event_describe(event);
1710 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
1711 g_free(desc);
1714 /* Logic
1715 if OOF
1716 OOF publish, Busy clean
1717 ilse if Busy
1718 OOF clean, Busy publish
1719 else
1720 OOF clean, Busy clean
1722 if (event && event->cal_status == SIPE_CAL_OOF) {
1723 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1724 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1725 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1726 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1727 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1728 } else {
1729 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1730 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1733 oof_note = sipe_ews_get_oof_note(cal);
1734 if (sipe_strequal("Scheduled", cal->oof_state)) {
1735 oof_start = cal->oof_start;
1736 oof_end = cal->oof_end;
1738 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
1740 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1741 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1743 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1744 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1745 } else {
1746 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1747 pub_cal_working_hours ? pub_cal_working_hours : "",
1748 pub_cal_free_busy ? pub_cal_free_busy : "",
1749 pub_calendar ? pub_calendar : "",
1750 pub_calendar2 ? pub_calendar2 : "",
1751 pub_oof_note ? pub_oof_note : "");
1753 send_presence_publish(sipe_private, publications);
1754 g_free(publications);
1757 g_free(pub_cal_working_hours);
1758 g_free(pub_cal_free_busy);
1759 g_free(pub_calendar);
1760 g_free(pub_calendar2);
1761 g_free(pub_oof_note);
1763 /* repeat scheduling */
1764 schedule_publish_update(sipe_private, time(NULL));
1767 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private)
1769 gchar *pub_state = sipe_status_changed_by_user(sipe_private) ?
1770 sipe_publish_get_category_state_user(sipe_private) :
1771 sipe_publish_get_category_state_machine(sipe_private);
1772 gchar *pub_note = sipe_publish_get_category_note(sipe_private,
1773 sipe_private->note,
1774 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1777 gchar *publications;
1779 if (!pub_state && !pub_note) {
1780 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1781 return;
1784 publications = g_strdup_printf("%s%s",
1785 pub_state ? pub_state : "",
1786 pub_note ? pub_note : "");
1788 g_free(pub_state);
1789 g_free(pub_note);
1791 send_presence_publish(sipe_private, publications);
1792 g_free(publications);
1795 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1797 gchar *publications = NULL;
1798 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1800 /* key is <category><instance><container> */
1801 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1802 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1803 struct sipe_publication *publication_2 =
1804 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1805 struct sipe_publication *publication_3 =
1806 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1807 g_free(key_2);
1808 g_free(key_3);
1810 #ifdef HAVE_VV
1811 if (sipe_private->media_call) {
1812 guint availability;
1813 const gchar *token;
1814 if (sipe_media_is_conference_call(sipe_private->media_call)) {
1815 availability = 7000;
1816 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1817 } else {
1818 availability = 6500;
1819 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1822 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1823 instance, publication_2 ? publication_2->version : 0,
1824 availability, token, availability,
1825 instance, publication_3 ? publication_3->version : 0,
1826 availability, token, availability);
1827 } else
1828 #endif
1830 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1831 instance, publication_2 ? publication_2->version : 0,
1832 instance, publication_3 ? publication_3->version : 0);
1835 send_presence_publish(sipe_private, publications);
1836 g_free(publications);
1839 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1840 gpointer value,
1841 GString* str)
1843 struct sipe_publication *publication = value;
1845 g_string_append_printf( str,
1846 SIPE_PUB_XML_PUBLICATION_CLEAR,
1847 publication->category,
1848 publication->instance,
1849 publication->container,
1850 publication->version,
1851 "static");
1854 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1856 GString* str;
1857 gchar *publications;
1859 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1860 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1861 return;
1864 str = g_string_new(NULL);
1865 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1866 publications = g_string_free(str, FALSE);
1868 send_presence_publish(sipe_private, publications);
1869 g_free(publications);
1872 /* key is <category><instance><container> */
1873 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1874 const gchar *key)
1876 GSList *entry;
1878 /* filling keys for our publications if not yet cached */
1879 if (!sipe_private->our_publication_keys) {
1880 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1881 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1882 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1883 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1884 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1885 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1886 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1887 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1889 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1890 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1891 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1892 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1893 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1894 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1895 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1896 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1897 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1898 SIPE_DEBUG_INFO("\tNote : %u", 0);
1899 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1901 /* device */
1902 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1903 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1905 /* state:machineState */
1906 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1907 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1908 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1909 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1911 /* state:userState */
1912 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1913 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1914 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1915 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1917 /* state:calendarState */
1918 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1919 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1920 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1921 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1923 /* state:calendarState OOF */
1924 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1925 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1926 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1927 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1929 /* state:phoneState */
1930 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1931 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1932 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1933 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1935 /* note */
1936 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1937 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1938 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1939 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1940 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1941 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1943 /* note OOF */
1944 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1945 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1946 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1947 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1948 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1949 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1951 /* calendarData:WorkingHours */
1952 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1953 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1954 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1955 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1956 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1957 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1958 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1959 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1960 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1961 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1962 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1963 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1965 /* calendarData:FreeBusy */
1966 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1967 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
1968 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1969 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
1970 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1971 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
1972 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1973 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
1974 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1975 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
1976 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1977 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
1979 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
1980 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
1983 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1985 entry = sipe_private->our_publication_keys;
1986 while (entry) {
1987 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1988 if (sipe_strequal(entry->data, key)) {
1989 return TRUE;
1991 entry = entry->next;
1993 return FALSE;
1996 static void sipe_refresh_blocked_status_cb(char *buddy_name,
1997 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1998 struct sipe_core_private *sipe_private)
2000 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2001 gboolean blocked = (container_id == 32000);
2002 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2004 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2005 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2007 if (blocked != blocked_in_blist) {
2008 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2012 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2014 sipe_buddy_foreach(sipe_private,
2015 (GHFunc) sipe_refresh_blocked_status_cb,
2016 sipe_private);
2020 * When we receive some self (BE) NOTIFY with a new subscriber
2021 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2024 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2025 struct sipmsg *msg)
2027 gchar *contact;
2028 gchar *to;
2029 sipe_xml *xml;
2030 const sipe_xml *node;
2031 const sipe_xml *node2;
2032 char *display_name = NULL;
2033 char *uri;
2034 GSList *category_names = NULL;
2035 int aggreg_avail = 0;
2036 gchar *activity_token = NULL;
2037 gboolean do_update_status = FALSE;
2038 gboolean has_note_cleaned = FALSE;
2039 GHashTable *devices;
2041 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2043 xml = sipe_xml_parse(msg->body, msg->bodylen);
2044 if (!xml) return;
2046 contact = get_contact(sipe_private);
2047 to = sip_uri_self(sipe_private);
2049 /* categories */
2050 /* set list of categories participating in this XML */
2051 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2052 const gchar *name = sipe_xml_attribute(node, "name");
2053 category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2054 (gchar *)name,
2055 (GCompareFunc)strcmp,
2056 NULL);
2058 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2059 category_names ? (int) g_slist_length(category_names) : -1);
2060 /* drop category information */
2061 if (category_names) {
2062 GSList *entry = category_names;
2063 while (entry) {
2064 GHashTable *cat_publications;
2065 const gchar *category = entry->data;
2066 entry = entry->next;
2067 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2068 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2069 if (cat_publications) {
2070 g_hash_table_remove(sipe_private->our_publications, category);
2071 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2075 g_slist_free(category_names);
2077 /* filling our categories reflected in roaming data */
2078 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2079 g_free, NULL);
2080 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2081 const char *tmp;
2082 const gchar *name = sipe_xml_attribute(node, "name");
2083 guint container = sipe_xml_int_attribute(node, "container", -1);
2084 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2085 guint version = sipe_xml_int_attribute(node, "version", 0);
2086 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2087 sipe_utils_str_to_time(tmp) : 0;
2088 gchar *key;
2089 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2091 /* Ex. clear note: <category name="note"/> */
2092 if (container == (guint)-1) {
2093 g_free(sipe_private->note);
2094 sipe_private->note = NULL;
2095 do_update_status = TRUE;
2096 continue;
2099 /* Ex. clear note: <category name="note" container="200"/> */
2100 if (instance == (guint)-1) {
2101 if (container == 200) {
2102 g_free(sipe_private->note);
2103 sipe_private->note = NULL;
2104 do_update_status = TRUE;
2106 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2107 sipe_remove_category_container_publications(
2108 sipe_private->our_publications, name, container);
2109 continue;
2112 /* key is <category><instance><container> */
2113 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2114 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2116 /* capture all userState publication for later clean up if required */
2117 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2118 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2120 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2121 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2122 publication->category = g_strdup(name);
2123 publication->instance = instance;
2124 publication->container = container;
2125 publication->version = version;
2127 if (!sipe_private->user_state_publications) {
2128 sipe_private->user_state_publications = g_hash_table_new_full(
2129 g_str_hash, g_str_equal,
2130 g_free, (GDestroyNotify)free_publication);
2132 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2133 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2134 key, version);
2138 /* count each client instance only once */
2139 if (sipe_strequal(name, "device"))
2140 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2142 if (sipe_is_our_publication(sipe_private, key)) {
2143 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2145 publication->category = g_strdup(name);
2146 publication->instance = instance;
2147 publication->container = container;
2148 publication->version = version;
2150 /* filling publication->availability */
2151 if (sipe_strequal(name, "state")) {
2152 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2153 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2155 if (xn_avail) {
2156 gchar *avail_str = sipe_xml_data(xn_avail);
2157 if (avail_str) {
2158 publication->availability = atoi(avail_str);
2160 g_free(avail_str);
2162 /* for calendarState */
2163 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2164 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2165 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2167 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2168 if (xn_activity) {
2169 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2170 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2172 event->is_meeting = TRUE;
2175 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2176 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2178 publication->cal_event_hash = sipe_cal_event_hash(event);
2179 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2180 publication->cal_event_hash);
2181 sipe_cal_event_free(event);
2184 /* filling publication->note */
2185 if (sipe_strequal(name, "note")) {
2186 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2188 if (!has_note_cleaned) {
2189 has_note_cleaned = TRUE;
2191 g_free(sipe_private->note);
2192 sipe_private->note = NULL;
2193 sipe_private->note_since = publish_time;
2195 do_update_status = TRUE;
2198 g_free(publication->note);
2199 publication->note = NULL;
2200 if (xn_body) {
2201 char *tmp;
2203 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2204 g_free(tmp);
2205 if (publish_time >= sipe_private->note_since) {
2206 g_free(sipe_private->note);
2207 sipe_private->note = g_strdup(publication->note);
2208 sipe_private->note_since = publish_time;
2209 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2210 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2211 else
2212 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2214 do_update_status = TRUE;
2219 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2220 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2221 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2222 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2223 if (xn_free_busy) {
2224 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2225 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2227 if (xn_working_hours) {
2228 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2232 if (!cat_publications) {
2233 cat_publications = g_hash_table_new_full(
2234 g_str_hash, g_str_equal,
2235 g_free, (GDestroyNotify)free_publication);
2236 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2237 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2239 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2240 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2242 g_free(key);
2244 /* aggregateState (not an our publication) from 2-nd container */
2245 if (sipe_strequal(name, "state") && container == 2) {
2246 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2247 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2249 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2250 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2252 if (xn_avail) {
2253 gchar *avail_str = sipe_xml_data(xn_avail);
2254 if (avail_str) {
2255 aggreg_avail = atoi(avail_str);
2257 g_free(avail_str);
2260 do_update_status = TRUE;
2263 if (xn_activity) {
2264 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2268 /* userProperties published by server from AD */
2269 if (!sipe_private->csta &&
2270 sipe_strequal(name, "userProperties")) {
2271 const sipe_xml *line;
2272 /* line, for Remote Call Control (RCC) or external Lync/Communicator call */
2273 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2274 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2275 gchar *line_uri = sipe_xml_data(line);
2276 if (!line_uri) {
2277 continue;
2280 if (sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual")) {
2281 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2282 if (line_server) {
2283 gchar *tmp = g_strstrip(line_uri);
2284 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s",
2285 tmp, line_server);
2286 sip_csta_open(sipe_private, tmp, line_server);
2289 #ifdef HAVE_VV
2290 else if (sipe_strequal(line_type, "Uc")) {
2292 if (!sipe_private->uc_line_uri) {
2293 sipe_private->uc_line_uri = g_strdup(g_strstrip(line_uri));
2294 } else {
2295 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: "
2296 "sipe_private->uc_line_uri is already set.");
2299 #endif
2301 g_free(line_uri);
2303 break;
2307 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2308 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2310 /* active clients for user account */
2311 if (g_hash_table_size(devices) > 1) {
2312 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2313 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2314 g_hash_table_size(devices));
2315 } else {
2316 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2317 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2319 g_hash_table_destroy(devices);
2321 /* containers */
2322 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2323 guint id = sipe_xml_int_attribute(node, "id", 0);
2324 struct sipe_container *container = sipe_find_container(sipe_private, id);
2326 if (container) {
2327 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2328 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2329 sipe_ocs2007_free_container(container);
2331 container = g_new0(struct sipe_container, 1);
2332 container->id = id;
2333 container->version = sipe_xml_int_attribute(node, "version", 0);
2334 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2335 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2337 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2338 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2339 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2340 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2341 container->members = g_slist_append(container->members, member);
2342 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2343 member->type, member->value ? member->value : "");
2347 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2348 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2349 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2350 char *container_xmls = NULL;
2351 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2352 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2354 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2355 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2356 /* initial set-up to let counterparties see your status */
2357 if (sameEnterpriseAL < 0) {
2358 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2359 guint version = container ? container->version : 0;
2360 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2362 if (federatedAL < 0) {
2363 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2364 guint version = container ? container->version : 0;
2365 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2367 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2369 if (container_xmls) {
2370 sipe_send_set_container_members(sipe_private, container_xmls);
2372 g_free(container_xmls);
2375 /* Refresh contacts' blocked status */
2376 sipe_refresh_blocked_status(sipe_private);
2378 /* subscribers */
2379 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2380 const char *user;
2381 const char *acknowledged;
2382 gchar *hdr;
2383 gchar *body;
2385 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2386 if (!user) continue;
2387 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2388 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2389 uri = sip_uri_from_name(user);
2391 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2392 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2394 acknowledged= sipe_xml_attribute(node, "acknowledged");
2395 if(sipe_strcase_equal(acknowledged,"false")){
2396 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2397 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2398 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2401 hdr = g_strdup_printf(
2402 "Contact: %s\r\n"
2403 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2405 body = g_strdup_printf(
2406 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2407 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2408 "</setSubscribers>", user);
2410 sip_transport_service(sipe_private,
2412 hdr,
2413 body,
2414 NULL);
2415 g_free(body);
2416 g_free(hdr);
2418 g_free(display_name);
2419 g_free(uri);
2422 g_free(contact);
2423 sipe_xml_free(xml);
2425 /* Publish initial state if not yet.
2426 * Assuming this happens on initial responce to subscription to roaming-self
2427 * so we've already updated our roaming data in full.
2428 * Only for 2007+
2430 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2431 send_publish_category_initial(sipe_private);
2432 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2433 /* dalayed run */
2434 sipe_cal_delayed_calendar_update(sipe_private);
2435 do_update_status = FALSE;
2436 } else if (aggreg_avail) {
2438 if (aggreg_avail &&
2439 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2440 /* not offline */
2441 sipe_status_set_token(sipe_private,
2442 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2443 } else {
2444 /* do not let offline status switch us off */
2445 sipe_status_set_activity(sipe_private,
2446 SIPE_ACTIVITY_INVISIBLE);
2450 if (do_update_status) {
2451 sipe_status_and_note(sipe_private, NULL);
2454 g_free(to);
2455 g_free(activity_token);
2459 * for Access levels menu
2461 #define INDENT_FMT " %s"
2464 * Member is indirectly belong to access level container.
2465 * For example 'sameEnterprise' is in the container and user
2466 * belongs to that same enterprise.
2468 #define INDENT_MARKED_INHERITED_FMT "= %s"
2470 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2471 struct sipe_backend_buddy_menu *menu,
2472 const gchar *member_type,
2473 const gchar *member_value,
2474 const gboolean extra_menu)
2476 unsigned int i;
2477 gboolean is_group_access = FALSE;
2478 int container_id;
2480 if (!menu)
2481 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2483 container_id = sipe_ocs2007_find_access_level(sipe_private,
2484 member_type,
2485 member_value,
2486 &is_group_access);
2488 for (i = 1; i <= CONTAINERS_LEN; i++) {
2490 * Blocked should remain in the first place
2491 * in the containers[] array.
2493 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2494 int container_j = containers[j];
2495 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2496 struct sipe_container *container = create_container(j,
2497 member_type,
2498 member_value,
2499 FALSE);
2500 gchar *label;
2502 /* libpurple memory leak workaround */
2503 blist_menu_remember_container(sipe_private, container);
2505 /* current container/access level */
2506 if (container_j == container_id) {
2507 label = is_group_access ?
2508 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2509 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2510 } else {
2511 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2514 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2515 menu,
2516 label,
2517 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2518 container);
2519 g_free(label);
2522 if (extra_menu && (container_id >= 0) && !is_group_access) {
2523 struct sipe_container *container = create_container(0,
2524 member_type,
2525 member_value,
2526 TRUE);
2527 gchar *label;
2529 /* separator */
2530 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2531 menu,
2532 " --------------");
2535 /* libpurple memory leak workaround */
2536 blist_menu_remember_container(sipe_private, container);
2538 /* Translators: remove (clear) previously assigned access level */
2539 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2540 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2541 menu,
2542 label,
2543 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2544 container);
2545 g_free(label);
2548 return(menu);
2551 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2553 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2554 GSList *access_domains, *entry;
2556 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2557 menu,
2558 _("People in my company"),
2559 access_levels_menu(sipe_private,
2560 NULL,
2561 "sameEnterprise",
2562 NULL,
2563 FALSE));
2565 /* this is original name, don't edit */
2566 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2567 menu,
2568 _("People in domains connected with my company"),
2569 access_levels_menu(sipe_private,
2570 NULL,
2571 "federated",
2572 NULL,
2573 FALSE));
2575 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2576 menu,
2577 _("People in public domains"),
2578 access_levels_menu(sipe_private,
2579 NULL,
2580 "publicCloud",
2581 NULL,
2582 TRUE));
2584 entry = access_domains = get_access_domains(sipe_private);
2585 while (entry) {
2586 gchar *domain = entry->data;
2587 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2589 /* takes over ownership of entry->data (= domain) */
2590 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2591 menu,
2592 menu_name,
2593 access_levels_menu(sipe_private,
2594 NULL,
2595 "domain",
2596 domain,
2597 TRUE));
2598 g_free(menu_name);
2600 entry = entry->next;
2602 g_slist_free(access_domains);
2604 /* separator */
2605 /* People in domains connected with my company */
2606 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2607 menu,
2608 "-------------------------------------------");
2610 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2611 menu,
2612 _("Add new domain..."),
2613 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2614 NULL);
2616 return(menu);
2619 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2620 const gchar *buddy_name)
2622 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2623 gchar *label;
2626 * Workaround for missing libpurple API to release resources allocated
2627 * during blist_node_menu() callback. See also:
2629 * <http://developer.pidgin.im/ticket/12597>
2631 * We remember all memory blocks in a list and deallocate them when
2633 * - the next time we enter the callback, or
2634 * - the account is disconnected
2636 * That means that after the buddy menu has been closed we have unused
2637 * resources but at least we don't leak them anymore...
2639 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2641 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2642 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2643 menu,
2644 label,
2645 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2646 NULL);
2647 g_free(label);
2649 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2650 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2651 menu,
2652 label,
2653 access_groups_menu(sipe_private));
2654 g_free(label);
2656 menu = access_levels_menu(sipe_private,
2657 menu,
2658 "user",
2659 sipe_get_no_sip_uri(buddy_name),
2660 TRUE);
2662 return(menu);
2666 Local Variables:
2667 mode: c
2668 c-file-style: "bsd"
2669 indent-tabs-mode: t
2670 tab-width: 8
2671 End: