ucs: move GetUserPhoto operation to buddy
[siplcs.git] / src / core / sipe-ocs2007.c
blobce908e357004214305746a91f736b60493cc3eb7
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2015 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 sipe_utils_time_to_debug_str(localtime(&calculate_from)));
719 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
720 sipe_utils_time_to_debug_str(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,
976 gboolean force_publish)
978 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
979 /* key is <category><instance><container> */
980 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
981 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
982 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
984 struct sipe_publication *publication_note_200 =
985 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_200);
986 struct sipe_publication *publication_note_300 =
987 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_300);
988 struct sipe_publication *publication_note_400 =
989 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_400);
991 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
992 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
993 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
994 char *res, *tmp1, *tmp2, *tmp3;
995 char *start_time_attr;
996 char *end_time_attr;
998 g_free(tmp);
999 tmp = NULL;
1000 g_free(key_note_200);
1001 g_free(key_note_300);
1002 g_free(key_note_400);
1004 /* we even need to republish empty note */
1005 if (!force_publish && sipe_strequal(n1, n2))
1007 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1008 g_free(n1);
1009 return NULL; /* nothing to update */
1012 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1013 g_free(tmp);
1014 tmp = NULL;
1015 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1016 g_free(tmp);
1018 if (n1) {
1019 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1020 instance,
1021 200,
1022 publication_note_200 ? publication_note_200->version : 0,
1023 note_type,
1024 start_time_attr ? start_time_attr : "",
1025 end_time_attr ? end_time_attr : "",
1026 n1);
1028 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1029 instance,
1030 300,
1031 publication_note_300 ? publication_note_300->version : 0,
1032 note_type,
1033 start_time_attr ? start_time_attr : "",
1034 end_time_attr ? end_time_attr : "",
1035 n1);
1037 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1038 instance,
1039 400,
1040 publication_note_400 ? publication_note_400->version : 0,
1041 note_type,
1042 start_time_attr ? start_time_attr : "",
1043 end_time_attr ? end_time_attr : "",
1044 n1);
1045 } else {
1046 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1047 "note",
1048 instance,
1049 200,
1050 publication_note_200 ? publication_note_200->version : 0,
1051 "static");
1052 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1053 "note",
1054 instance,
1055 300,
1056 publication_note_200 ? publication_note_200->version : 0,
1057 "static");
1058 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1059 "note",
1060 instance,
1061 400,
1062 publication_note_200 ? publication_note_200->version : 0,
1063 "static");
1065 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
1067 g_free(start_time_attr);
1068 g_free(end_time_attr);
1069 g_free(tmp1);
1070 g_free(tmp2);
1071 g_free(tmp3);
1072 g_free(n1);
1074 return res;
1078 * Publishes 'calendarData' category's WorkingHours.
1080 * @param version (%u) Ex.: 1
1081 * @param email (%s) Ex.: alice@cosmo.local
1082 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1084 * @param version (%u)
1086 * @param version (%u)
1087 * @param email (%s)
1088 * @param working_hours_xml_str (%s)
1090 * @param version (%u)
1091 * @param email (%s)
1092 * @param working_hours_xml_str (%s)
1094 * @param version (%u)
1095 * @param email (%s)
1096 * @param working_hours_xml_str (%s)
1098 * @param version (%u)
1100 #define SIPE_PUB_XML_WORKING_HOURS \
1101 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1102 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1103 "</calendarData>"\
1104 "</publication>"\
1105 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1106 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1107 "</publication>"\
1108 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1109 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1110 "</calendarData>"\
1111 "</publication>"\
1112 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1113 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1114 "</calendarData>"\
1115 "</publication>"\
1116 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1117 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1118 "</calendarData>"\
1119 "</publication>"\
1120 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1121 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1122 "</publication>"
1125 * Returns 'calendarData' XML part with WorkingHours for publication.
1126 * Must be g_free'd after use.
1128 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1130 struct sipe_calendar* cal = sipe_private->calendar;
1132 /* key is <category><instance><container> */
1133 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1134 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1135 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1136 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1137 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1138 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1140 struct sipe_publication *publication_cal_1 =
1141 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1142 struct sipe_publication *publication_cal_100 =
1143 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1144 struct sipe_publication *publication_cal_200 =
1145 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1146 struct sipe_publication *publication_cal_300 =
1147 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1148 struct sipe_publication *publication_cal_400 =
1149 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1150 struct sipe_publication *publication_cal_32000 =
1151 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1153 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1154 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1156 g_free(key_cal_1);
1157 g_free(key_cal_100);
1158 g_free(key_cal_200);
1159 g_free(key_cal_300);
1160 g_free(key_cal_400);
1161 g_free(key_cal_32000);
1163 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1164 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1165 return NULL;
1168 if (sipe_strequal(n1, n2))
1170 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1171 return NULL; /* nothing to update */
1174 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1175 /* 1 */
1176 publication_cal_1 ? publication_cal_1->version : 0,
1177 cal->email,
1178 cal->working_hours_xml_str,
1179 /* 100 - Public */
1180 publication_cal_100 ? publication_cal_100->version : 0,
1181 /* 200 - Company */
1182 publication_cal_200 ? publication_cal_200->version : 0,
1183 cal->email,
1184 cal->working_hours_xml_str,
1185 /* 300 - Team */
1186 publication_cal_300 ? publication_cal_300->version : 0,
1187 cal->email,
1188 cal->working_hours_xml_str,
1189 /* 400 - Personal */
1190 publication_cal_400 ? publication_cal_400->version : 0,
1191 cal->email,
1192 cal->working_hours_xml_str,
1193 /* 32000 - Blocked */
1194 publication_cal_32000 ? publication_cal_32000->version : 0
1199 * Publishes 'calendarData' category's FreeBusy.
1201 * @param instance (%u) Ex.: 1300372959
1202 * @param version (%u) Ex.: 1
1204 * @param instance (%u) Ex.: 1300372959
1205 * @param version (%u) Ex.: 1
1207 * @param instance (%u) Ex.: 1300372959
1208 * @param version (%u) Ex.: 1
1209 * @param email (%s) Ex.: alice@cosmo.local
1210 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1211 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1213 * @param instance (%u) Ex.: 1300372959
1214 * @param version (%u) Ex.: 1
1215 * @param email (%s) Ex.: alice@cosmo.local
1216 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1217 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1219 * @param instance (%u) Ex.: 1300372959
1220 * @param version (%u) Ex.: 1
1221 * @param email (%s) Ex.: alice@cosmo.local
1222 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1223 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1225 * @param instance (%u) Ex.: 1300372959
1226 * @param version (%u) Ex.: 1
1228 #define SIPE_PUB_XML_FREE_BUSY \
1229 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1230 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1231 "</publication>"\
1232 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1233 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1234 "</publication>"\
1235 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1236 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1237 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1238 "</calendarData>"\
1239 "</publication>"\
1240 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1241 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1242 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1243 "</calendarData>"\
1244 "</publication>"\
1245 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1246 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1247 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1248 "</calendarData>"\
1249 "</publication>"\
1250 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1251 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1252 "</publication>"
1255 * Returns 'calendarData' XML part with FreeBusy for publication.
1256 * Must be g_free'd after use.
1258 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1260 struct sipe_calendar* cal = sipe_private->calendar;
1261 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1262 char *fb_start_str;
1263 char *free_busy_base64;
1264 /* const char *st; */
1265 /* const char *fb; */
1266 char *res;
1268 /* key is <category><instance><container> */
1269 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1270 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1271 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1272 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1273 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1274 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1276 struct sipe_publication *publication_cal_1 =
1277 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1278 struct sipe_publication *publication_cal_100 =
1279 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1280 struct sipe_publication *publication_cal_200 =
1281 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1282 struct sipe_publication *publication_cal_300 =
1283 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1284 struct sipe_publication *publication_cal_400 =
1285 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1286 struct sipe_publication *publication_cal_32000 =
1287 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1289 g_free(key_cal_1);
1290 g_free(key_cal_100);
1291 g_free(key_cal_200);
1292 g_free(key_cal_300);
1293 g_free(key_cal_400);
1294 g_free(key_cal_32000);
1296 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1297 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1298 return NULL;
1301 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1302 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1304 /* we will rebuplish the same data to refresh publication time,
1305 * so if data from multiple sources, most recent will be choosen
1307 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1308 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1310 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1312 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1313 // g_free(fb_start_str);
1314 // g_free(free_busy_base64);
1315 // return NULL; /* nothing to update */
1318 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1319 /* 1 */
1320 cal_data_instance,
1321 publication_cal_1 ? publication_cal_1->version : 0,
1322 /* 100 - Public */
1323 cal_data_instance,
1324 publication_cal_100 ? publication_cal_100->version : 0,
1325 /* 200 - Company */
1326 cal_data_instance,
1327 publication_cal_200 ? publication_cal_200->version : 0,
1328 cal->email,
1329 fb_start_str,
1330 free_busy_base64,
1331 /* 300 - Team */
1332 cal_data_instance,
1333 publication_cal_300 ? publication_cal_300->version : 0,
1334 cal->email,
1335 fb_start_str,
1336 free_busy_base64,
1337 /* 400 - Personal */
1338 cal_data_instance,
1339 publication_cal_400 ? publication_cal_400->version : 0,
1340 cal->email,
1341 fb_start_str,
1342 free_busy_base64,
1343 /* 32000 - Blocked */
1344 cal_data_instance,
1345 publication_cal_32000 ? publication_cal_32000->version : 0
1348 g_free(fb_start_str);
1349 g_free(free_busy_base64);
1350 return res;
1355 * Publishes 'device' category.
1356 * @param instance (%u) Ex.: 1938468728
1357 * @param version (%u) Ex.: 1
1358 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1359 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1360 * @param timezone (%s) Ex.: 00:00:00+01:00
1361 * @param machineName (%s) Ex.: BOSTON-OCS07
1363 #define SIPE_PUB_XML_DEVICE \
1364 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1365 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1366 "<capabilities preferred=\"false\" uri=\"%s\">"\
1367 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1368 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1369 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1370 "</capabilities>"\
1371 "<timezone>%s</timezone>"\
1372 "<machineName>%s</machineName>"\
1373 "</device>"\
1374 "</publication>"
1377 * Returns 'device' XML part for publication.
1378 * Must be g_free'd after use.
1380 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1382 gchar *uri;
1383 gchar *doc;
1384 gchar *uuid = get_uuid(sipe_private);
1385 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1386 /* key is <category><instance><container> */
1387 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1388 struct sipe_publication *publication =
1389 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "device"), key);
1391 g_free(key);
1393 uri = sip_uri_self(sipe_private);
1394 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1395 device_instance,
1396 publication ? publication->version : 0,
1397 uuid,
1398 uri,
1399 "00:00:00+01:00", /* @TODO make timezone real*/
1400 g_get_host_name()
1403 g_free(uri);
1404 g_free(uuid);
1406 return doc;
1410 * Publishes 'machineState' category.
1411 * @param instance (%u) Ex.: 926460663
1412 * @param version (%u) Ex.: 22
1413 * @param availability (%d) Ex.: 3500
1414 * @param instance (%u) Ex.: 926460663
1415 * @param version (%u) Ex.: 22
1416 * @param availability (%d) Ex.: 3500
1418 #define SIPE_PUB_XML_STATE_MACHINE \
1419 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1420 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1421 "<availability>%d</availability>"\
1422 "<endpointLocation/>"\
1423 "</state>"\
1424 "</publication>"\
1425 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1426 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1427 "<availability>%d</availability>"\
1428 "<endpointLocation/>"\
1429 "</state>"\
1430 "</publication>"
1433 * Publishes 'userState' category.
1434 * @param instance (%u) User. Ex.: 536870912
1435 * @param version (%u) User Container 2. Ex.: 22
1436 * @param availability (%d) User Container 2. Ex.: 15500
1437 * @param instance (%u) User. Ex.: 536870912
1438 * @param version (%u) User Container 3.Ex.: 22
1439 * @param availability (%d) User Container 3. Ex.: 15500
1441 #define SIPE_PUB_XML_STATE_USER \
1442 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1443 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1444 "<availability>%d</availability>"\
1445 "<endpointLocation/>"\
1446 "</state>"\
1447 "</publication>"\
1448 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1449 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1450 "<availability>%d</availability>"\
1451 "<endpointLocation/>"\
1452 "</state>"\
1453 "</publication>"
1456 * A service method - use
1457 * - send_publish_get_category_state_machine and
1458 * - send_publish_get_category_state_user instead.
1459 * Must be g_free'd after use.
1461 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1462 gboolean force_publish,
1463 gboolean is_user_state)
1465 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1466 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1467 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1468 /* key is <category><instance><container> */
1469 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1470 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1471 struct sipe_publication *publication_2 =
1472 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1473 struct sipe_publication *publication_3 =
1474 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1476 g_free(key_2);
1477 g_free(key_3);
1479 if (!force_publish && publication_2 && (publication_2->availability == availability))
1481 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1482 return NULL; /* nothing to update */
1485 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1486 instance,
1487 publication_2 ? publication_2->version : 0,
1488 availability,
1489 instance,
1490 publication_3 ? publication_3->version : 0,
1491 availability);
1495 * Returns 'machineState' XML part for publication.
1496 * Must be g_free'd after use.
1498 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private,
1499 gboolean force_publish)
1501 return sipe_publish_get_category_state(sipe_private, force_publish, FALSE);
1505 * Returns 'userState' XML part for publication.
1506 * Must be g_free'd after use.
1508 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private,
1509 gboolean force_publish)
1511 return sipe_publish_get_category_state(sipe_private, force_publish, TRUE);
1514 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1516 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1517 gchar *pub_machine;
1518 gchar *publications;
1520 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1522 pub_machine = sipe_publish_get_category_state_machine(sipe_private,
1523 TRUE);
1524 publications = g_strdup_printf("%s%s",
1525 pub_device,
1526 pub_machine ? pub_machine : "");
1527 g_free(pub_device);
1528 g_free(pub_machine);
1530 send_presence_publish(sipe_private, publications);
1531 g_free(publications);
1534 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1535 struct sipmsg *msg,
1536 struct transaction *trans)
1538 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1540 if (msg->response == 200 && g_str_has_prefix(contenttype, "application/vnd-microsoft-roaming-self+xml")) {
1541 sipe_ocs2007_process_roaming_self(sipe_private, msg);
1542 } else if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1543 sipe_xml *xml;
1544 const sipe_xml *node;
1545 gchar *fault_code;
1546 GHashTable *faults;
1547 int index_our;
1548 gboolean has_device_publication = FALSE;
1550 xml = sipe_xml_parse(msg->body, msg->bodylen);
1552 /* test if version mismatch fault */
1553 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1554 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1555 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1556 g_free(fault_code);
1557 sipe_xml_free(xml);
1558 return TRUE;
1560 g_free(fault_code);
1562 /* accumulating information about faulty versions */
1563 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1564 for (node = sipe_xml_child(xml, "details/operation");
1565 node;
1566 node = sipe_xml_twin(node))
1568 const gchar *index = sipe_xml_attribute(node, "index");
1569 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1571 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1572 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1574 sipe_xml_free(xml);
1576 /* here we are parsing our own request to figure out what publication
1577 * referenced here only by index went wrong
1579 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1581 /* publication */
1582 for (node = sipe_xml_child(xml, "publications/publication"),
1583 index_our = 1; /* starts with 1 - our first publication */
1584 node;
1585 node = sipe_xml_twin(node), index_our++)
1587 gchar *idx = g_strdup_printf("%d", index_our);
1588 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1589 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1590 g_free(idx);
1592 if (sipe_strequal("device", categoryName)) {
1593 has_device_publication = TRUE;
1596 if (curVersion) { /* fault exist on this index */
1597 const gchar *container = sipe_xml_attribute(node, "container");
1598 const gchar *instance = sipe_xml_attribute(node, "instance");
1599 /* key is <category><instance><container> */
1600 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1601 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1603 if (category) {
1604 struct sipe_publication *publication =
1605 g_hash_table_lookup(category, key);
1607 SIPE_DEBUG_INFO("key is %s", key);
1609 if (publication) {
1610 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1611 key, curVersion, publication->version);
1612 /* updating publication's version to the correct one */
1613 publication->version = atoi(curVersion);
1615 } else {
1616 /* We somehow lost this category from our publications... */
1617 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1618 publication->category = g_strdup(categoryName);
1619 publication->instance = atoi(instance);
1620 publication->container = atoi(container);
1621 publication->version = atoi(curVersion);
1622 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1623 g_free, (GDestroyNotify)free_publication);
1624 g_hash_table_insert(category, g_strdup(key), publication);
1625 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1626 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1628 g_free(key);
1631 sipe_xml_free(xml);
1632 g_hash_table_destroy(faults);
1634 /* rebublishing with right versions */
1635 if (has_device_publication) {
1636 send_publish_category_initial(sipe_private);
1637 } else {
1638 sipe_ocs2007_category_publish(sipe_private, TRUE);
1641 return TRUE;
1645 * Publishes categories.
1646 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1647 * @param publications (%s) XML publications
1649 #define SIPE_SEND_PRESENCE \
1650 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1651 "<publications uri=\"%s\">"\
1652 "%s"\
1653 "</publications>"\
1654 "</publish>"
1656 static void send_presence_publish(struct sipe_core_private *sipe_private,
1657 const char *publications)
1659 gchar *uri;
1660 gchar *doc;
1661 gchar *tmp;
1662 gchar *hdr;
1664 uri = sip_uri_self(sipe_private);
1665 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1666 uri,
1667 publications);
1669 tmp = get_contact(sipe_private);
1670 hdr = g_strdup_printf("Contact: %s\r\n"
1671 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1673 sip_transport_service(sipe_private,
1674 uri,
1675 hdr,
1676 doc,
1677 process_send_presence_category_publish_response);
1679 g_free(tmp);
1680 g_free(hdr);
1681 g_free(uri);
1682 g_free(doc);
1686 * Publishes self status
1687 * based on own calendar information.
1689 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1690 SIPE_UNUSED_PARAMETER void *unused)
1692 struct sipe_calendar* cal = sipe_private->calendar;
1693 struct sipe_cal_event* event = NULL;
1694 gchar *pub_cal_working_hours = NULL;
1695 gchar *pub_cal_free_busy = NULL;
1696 gchar *pub_calendar = NULL;
1697 gchar *pub_calendar2 = NULL;
1698 gchar *pub_oof_note = NULL;
1699 const gchar *oof_note;
1700 time_t oof_start = 0;
1701 time_t oof_end = 0;
1703 if (!cal) {
1704 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1705 return;
1708 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1709 if (cal->cal_events) {
1710 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1713 if (event) {
1714 sipe_cal_event_debug(event, "publish_calendar_status_self: current event is:\n");
1715 } else {
1716 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1719 /* Logic
1720 if OOF
1721 OOF publish, Busy clean
1722 ilse if Busy
1723 OOF clean, Busy publish
1724 else
1725 OOF clean, Busy clean
1727 if (event && event->cal_status == SIPE_CAL_OOF) {
1728 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1729 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1730 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1731 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1732 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1733 } else {
1734 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1735 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1738 oof_note = sipe_ews_get_oof_note(cal);
1739 if (sipe_strequal("Scheduled", cal->oof_state)) {
1740 oof_start = cal->oof_start;
1741 oof_end = cal->oof_end;
1743 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end, FALSE);
1745 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1746 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1748 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1749 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1750 } else {
1751 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1752 pub_cal_working_hours ? pub_cal_working_hours : "",
1753 pub_cal_free_busy ? pub_cal_free_busy : "",
1754 pub_calendar ? pub_calendar : "",
1755 pub_calendar2 ? pub_calendar2 : "",
1756 pub_oof_note ? pub_oof_note : "");
1758 send_presence_publish(sipe_private, publications);
1759 g_free(publications);
1762 g_free(pub_cal_working_hours);
1763 g_free(pub_cal_free_busy);
1764 g_free(pub_calendar);
1765 g_free(pub_calendar2);
1766 g_free(pub_oof_note);
1768 /* repeat scheduling */
1769 schedule_publish_update(sipe_private, time(NULL));
1772 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
1773 gboolean force_publish)
1775 GString *publications = g_string_new("");
1776 gchar *tmp;
1778 if (force_publish || sipe_private->status_set_by_user) {
1779 tmp = sipe_publish_get_category_state_user(sipe_private,
1780 force_publish);
1781 if (tmp) {
1782 g_string_append(publications, tmp);
1783 g_free(tmp);
1787 tmp = sipe_publish_get_category_state_machine(sipe_private,
1788 force_publish);
1789 if (tmp) {
1790 g_string_append(publications, tmp);
1791 g_free(tmp);
1794 tmp = sipe_publish_get_category_note(sipe_private,
1795 sipe_private->note,
1796 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1799 force_publish);
1800 if (tmp) {
1801 g_string_append(publications, tmp);
1802 g_free(tmp);
1805 if (publications->len)
1806 send_presence_publish(sipe_private, publications->str);
1807 else
1808 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1810 g_string_free(publications, TRUE);
1813 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1815 gchar *publications = NULL;
1816 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1818 /* key is <category><instance><container> */
1819 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1820 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1821 struct sipe_publication *publication_2 =
1822 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1823 struct sipe_publication *publication_3 =
1824 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1825 g_free(key_2);
1826 g_free(key_3);
1828 #ifdef HAVE_VV
1829 if (g_hash_table_size(sipe_private->media_calls)) {
1830 guint availability = 0;
1831 const gchar *token = NULL;
1832 GList *calls = g_hash_table_get_values(sipe_private->media_calls);
1833 GList *i;
1835 if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) {
1836 availability = 6500;
1837 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1840 for (i = calls; i; i = i->next) {
1841 if (sipe_media_is_conference_call(i->data)) {
1842 availability = 7000;
1843 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1844 break;
1848 g_list_free(calls);
1850 if (token) {
1851 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1852 instance, publication_2 ? publication_2->version : 0,
1853 availability, token, availability,
1854 instance, publication_3 ? publication_3->version : 0,
1855 availability, token, availability);
1857 } else
1858 #endif
1860 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1861 instance, publication_2 ? publication_2->version : 0,
1862 instance, publication_3 ? publication_3->version : 0);
1865 if (publications) {
1866 send_presence_publish(sipe_private, publications);
1867 g_free(publications);
1871 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1872 gpointer value,
1873 GString* str)
1875 struct sipe_publication *publication = value;
1877 g_string_append_printf( str,
1878 SIPE_PUB_XML_PUBLICATION_CLEAR,
1879 publication->category,
1880 publication->instance,
1881 publication->container,
1882 publication->version,
1883 "static");
1886 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1888 GString* str;
1889 gchar *publications;
1891 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1892 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1893 return;
1896 str = g_string_new(NULL);
1897 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1898 publications = g_string_free(str, FALSE);
1900 send_presence_publish(sipe_private, publications);
1901 g_free(publications);
1904 /* key is <category><instance><container> */
1905 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1906 const gchar *key)
1908 GSList *entry;
1910 /* filling keys for our publications if not yet cached */
1911 if (!sipe_private->our_publication_keys) {
1912 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1913 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1914 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1915 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1916 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1917 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1918 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1919 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1921 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1922 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1923 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1924 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1925 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1926 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1927 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1928 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1929 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1930 SIPE_DEBUG_INFO("\tNote : %u", 0);
1931 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1933 /* device */
1934 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1935 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1937 /* state:machineState */
1938 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1939 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1940 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1941 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1943 /* state:userState */
1944 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1945 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1946 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1947 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1949 /* state:calendarState */
1950 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1951 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1952 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1953 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1955 /* state:calendarState OOF */
1956 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1957 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1958 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1959 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1961 /* state:phoneState */
1962 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1963 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1964 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1965 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1967 /* note */
1968 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1969 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1970 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1971 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1972 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1973 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1975 /* note OOF */
1976 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1977 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1978 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1979 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1980 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1981 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1983 /* calendarData:WorkingHours */
1984 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1985 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1986 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1987 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1988 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1989 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1990 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1991 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1992 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1993 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1994 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1995 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1997 /* calendarData:FreeBusy */
1998 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1999 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
2000 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2001 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
2002 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2003 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
2004 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2005 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
2006 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2007 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
2008 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2009 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
2011 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
2012 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
2015 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2017 entry = sipe_private->our_publication_keys;
2018 while (entry) {
2019 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2020 if (sipe_strequal(entry->data, key)) {
2021 return TRUE;
2023 entry = entry->next;
2025 return FALSE;
2028 static void sipe_refresh_blocked_status_cb(char *buddy_name,
2029 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
2030 struct sipe_core_private *sipe_private)
2032 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2033 gboolean blocked = (container_id == 32000);
2034 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2036 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2037 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2039 if (blocked != blocked_in_blist) {
2040 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2044 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2046 sipe_buddy_foreach(sipe_private,
2047 (GHFunc) sipe_refresh_blocked_status_cb,
2048 sipe_private);
2052 * When we receive some self (BE) NOTIFY with a new subscriber
2053 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2056 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2057 struct sipmsg *msg)
2059 gchar *contact;
2060 gchar *to;
2061 sipe_xml *xml;
2062 const sipe_xml *node;
2063 const sipe_xml *node2;
2064 char *display_name = NULL;
2065 char *uri;
2066 GSList *category_names = NULL;
2067 int aggreg_avail = 0;
2068 gchar *activity_token = NULL;
2069 gboolean do_update_status = FALSE;
2070 gboolean has_note_cleaned = FALSE;
2071 GHashTable *devices;
2073 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2075 xml = sipe_xml_parse(msg->body, msg->bodylen);
2076 if (!xml) return;
2078 contact = get_contact(sipe_private);
2079 to = sip_uri_self(sipe_private);
2081 /* categories */
2082 /* set list of categories participating in this XML */
2083 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2084 const gchar *name = sipe_xml_attribute(node, "name");
2085 category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2086 (gchar *)name,
2087 (GCompareFunc)strcmp,
2088 NULL);
2090 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2091 category_names ? (int) g_slist_length(category_names) : -1);
2092 /* drop category information */
2093 if (category_names) {
2094 GSList *entry = category_names;
2095 while (entry) {
2096 GHashTable *cat_publications;
2097 const gchar *category = entry->data;
2098 entry = entry->next;
2099 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2100 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2101 if (cat_publications) {
2102 g_hash_table_remove(sipe_private->our_publications, category);
2103 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2107 g_slist_free(category_names);
2109 /* filling our categories reflected in roaming data */
2110 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2111 g_free, NULL);
2112 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2113 const char *tmp;
2114 const gchar *name = sipe_xml_attribute(node, "name");
2115 guint container = sipe_xml_int_attribute(node, "container", -1);
2116 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2117 guint version = sipe_xml_int_attribute(node, "version", 0);
2118 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2119 sipe_utils_str_to_time(tmp) : 0;
2120 gchar *key;
2121 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2123 /* Ex. clear note: <category name="note"/> */
2124 if (container == (guint)-1) {
2125 g_free(sipe_private->note);
2126 sipe_private->note = NULL;
2127 do_update_status = TRUE;
2128 continue;
2131 /* Ex. clear note: <category name="note" container="200"/> */
2132 if (instance == (guint)-1) {
2133 if (container == 200) {
2134 g_free(sipe_private->note);
2135 sipe_private->note = NULL;
2136 do_update_status = TRUE;
2138 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2139 sipe_remove_category_container_publications(
2140 sipe_private->our_publications, name, container);
2141 continue;
2144 /* key is <category><instance><container> */
2145 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2146 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2148 /* capture all userState publication for later clean up if required */
2149 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2150 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2152 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2153 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2154 publication->category = g_strdup(name);
2155 publication->instance = instance;
2156 publication->container = container;
2157 publication->version = version;
2159 if (!sipe_private->user_state_publications) {
2160 sipe_private->user_state_publications = g_hash_table_new_full(
2161 g_str_hash, g_str_equal,
2162 g_free, (GDestroyNotify)free_publication);
2164 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2165 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2166 key, version);
2170 /* count each client instance only once */
2171 if (sipe_strequal(name, "device"))
2172 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2174 if (sipe_is_our_publication(sipe_private, key)) {
2175 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2177 publication->category = g_strdup(name);
2178 publication->instance = instance;
2179 publication->container = container;
2180 publication->version = version;
2182 /* filling publication->availability */
2183 if (sipe_strequal(name, "state")) {
2184 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2185 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2187 if (xn_avail) {
2188 gchar *avail_str = sipe_xml_data(xn_avail);
2189 if (avail_str) {
2190 publication->availability = atoi(avail_str);
2192 g_free(avail_str);
2194 /* for calendarState */
2195 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2196 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2197 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2199 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2200 if (xn_activity) {
2201 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2202 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2204 event->is_meeting = TRUE;
2207 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2208 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2210 publication->cal_event_hash = sipe_cal_event_hash(event);
2211 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2212 publication->cal_event_hash);
2213 sipe_cal_event_free(event);
2216 /* filling publication->note */
2217 if (sipe_strequal(name, "note")) {
2218 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2220 if (!has_note_cleaned) {
2221 has_note_cleaned = TRUE;
2223 g_free(sipe_private->note);
2224 sipe_private->note = NULL;
2225 sipe_private->note_since = publish_time;
2227 do_update_status = TRUE;
2230 g_free(publication->note);
2231 publication->note = NULL;
2232 if (xn_body) {
2233 char *tmp;
2235 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2236 g_free(tmp);
2237 if (publish_time >= sipe_private->note_since) {
2238 g_free(sipe_private->note);
2239 sipe_private->note = g_strdup(publication->note);
2240 sipe_private->note_since = publish_time;
2241 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2242 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2243 else
2244 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2246 do_update_status = TRUE;
2251 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2252 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2253 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2254 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2255 if (xn_free_busy) {
2256 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2257 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2259 if (xn_working_hours) {
2260 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2264 if (!cat_publications) {
2265 cat_publications = g_hash_table_new_full(
2266 g_str_hash, g_str_equal,
2267 g_free, (GDestroyNotify)free_publication);
2268 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2269 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2271 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2272 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2274 g_free(key);
2276 /* aggregateState (not an our publication) from 2-nd container */
2277 if (sipe_strequal(name, "state") && container == 2) {
2278 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2279 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2281 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2282 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2284 if (xn_avail) {
2285 gchar *avail_str = sipe_xml_data(xn_avail);
2286 if (avail_str) {
2287 aggreg_avail = atoi(avail_str);
2289 g_free(avail_str);
2292 do_update_status = TRUE;
2295 if (xn_activity) {
2296 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2300 /* userProperties published by server from AD */
2301 if (!sipe_private->csta &&
2302 sipe_strequal(name, "userProperties")) {
2303 const sipe_xml *line;
2304 /* line, for Remote Call Control (RCC) or external Lync/Communicator call */
2305 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2306 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2307 gchar *line_uri = sipe_xml_data(line);
2308 if (!line_uri) {
2309 continue;
2312 if (sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual")) {
2313 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2314 if (line_server) {
2315 gchar *tmp = g_strstrip(line_uri);
2316 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s",
2317 tmp, line_server);
2318 sip_csta_open(sipe_private, tmp, line_server);
2321 #ifdef HAVE_VV
2322 else if (sipe_strequal(line_type, "Uc")) {
2324 if (!sipe_private->uc_line_uri) {
2325 sipe_private->uc_line_uri = g_strdup(g_strstrip(line_uri));
2326 } else {
2327 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: "
2328 "sipe_private->uc_line_uri is already set.");
2331 #endif
2333 g_free(line_uri);
2335 break;
2339 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2340 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2342 /* active clients for user account */
2343 if (g_hash_table_size(devices) > 1) {
2344 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2345 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2346 g_hash_table_size(devices));
2347 } else {
2348 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2349 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2351 g_hash_table_destroy(devices);
2353 /* containers */
2354 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2355 guint id = sipe_xml_int_attribute(node, "id", 0);
2356 struct sipe_container *container = sipe_find_container(sipe_private, id);
2358 if (container) {
2359 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2360 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2361 sipe_ocs2007_free_container(container);
2363 container = g_new0(struct sipe_container, 1);
2364 container->id = id;
2365 container->version = sipe_xml_int_attribute(node, "version", 0);
2366 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2367 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2369 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2370 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2371 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2372 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2373 container->members = g_slist_append(container->members, member);
2374 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2375 member->type, member->value ? member->value : "");
2379 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2380 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2381 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2382 char *container_xmls = NULL;
2383 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2384 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2386 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2387 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2388 /* initial set-up to let counterparties see your status */
2389 if (sameEnterpriseAL < 0) {
2390 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2391 guint version = container ? container->version : 0;
2392 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2394 if (federatedAL < 0) {
2395 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2396 guint version = container ? container->version : 0;
2397 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2399 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2401 if (container_xmls) {
2402 sipe_send_set_container_members(sipe_private, container_xmls);
2404 g_free(container_xmls);
2407 /* Refresh contacts' blocked status */
2408 sipe_refresh_blocked_status(sipe_private);
2410 /* subscribers */
2411 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2412 const char *user;
2413 const char *acknowledged;
2414 gchar *hdr;
2415 gchar *body;
2417 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2418 if (!user) continue;
2419 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2420 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2421 uri = sip_uri_from_name(user);
2423 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2424 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2426 acknowledged= sipe_xml_attribute(node, "acknowledged");
2427 if(sipe_strcase_equal(acknowledged,"false")){
2428 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2429 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2430 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2433 hdr = g_strdup_printf(
2434 "Contact: %s\r\n"
2435 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2437 body = g_strdup_printf(
2438 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2439 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2440 "</setSubscribers>", user);
2442 sip_transport_service(sipe_private,
2444 hdr,
2445 body,
2446 NULL);
2447 g_free(body);
2448 g_free(hdr);
2450 g_free(display_name);
2451 g_free(uri);
2454 g_free(contact);
2455 sipe_xml_free(xml);
2457 /* Publish initial state if not yet.
2458 * Assuming this happens on initial responce to subscription to roaming-self
2459 * so we've already updated our roaming data in full.
2460 * Only for 2007+
2462 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2463 send_publish_category_initial(sipe_private);
2464 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2465 /* dalayed run */
2466 sipe_cal_delayed_calendar_update(sipe_private);
2467 do_update_status = FALSE;
2468 } else if (aggreg_avail) {
2470 if (aggreg_avail &&
2471 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2472 /* not offline */
2473 sipe_status_set_token(sipe_private,
2474 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2475 } else {
2476 /* do not let offline status switch us off */
2477 sipe_status_set_activity(sipe_private,
2478 SIPE_ACTIVITY_INVISIBLE);
2482 if (do_update_status) {
2483 sipe_status_and_note(sipe_private, NULL);
2486 g_free(to);
2487 g_free(activity_token);
2491 * for Access levels menu
2493 #define INDENT_FMT " %s"
2496 * Member is indirectly belong to access level container.
2497 * For example 'sameEnterprise' is in the container and user
2498 * belongs to that same enterprise.
2500 #define INDENT_MARKED_INHERITED_FMT "= %s"
2502 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2503 struct sipe_backend_buddy_menu *menu,
2504 const gchar *member_type,
2505 const gchar *member_value,
2506 const gboolean extra_menu)
2508 unsigned int i;
2509 gboolean is_group_access = FALSE;
2510 int container_id;
2512 if (!menu)
2513 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2515 container_id = sipe_ocs2007_find_access_level(sipe_private,
2516 member_type,
2517 member_value,
2518 &is_group_access);
2520 for (i = 1; i <= CONTAINERS_LEN; i++) {
2522 * Blocked should remain in the first place
2523 * in the containers[] array.
2525 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2526 int container_j = containers[j];
2527 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2528 struct sipe_container *container = create_container(j,
2529 member_type,
2530 member_value,
2531 FALSE);
2532 gchar *label;
2534 /* libpurple memory leak workaround */
2535 blist_menu_remember_container(sipe_private, container);
2537 /* current container/access level */
2538 if (container_j == container_id) {
2539 label = is_group_access ?
2540 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2541 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2542 } else {
2543 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2546 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2547 menu,
2548 label,
2549 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2550 container);
2551 g_free(label);
2554 if (extra_menu && (container_id >= 0) && !is_group_access) {
2555 struct sipe_container *container = create_container(0,
2556 member_type,
2557 member_value,
2558 TRUE);
2559 gchar *label;
2561 /* separator */
2562 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2563 menu,
2564 " --------------");
2567 /* libpurple memory leak workaround */
2568 blist_menu_remember_container(sipe_private, container);
2570 /* Translators: remove (clear) previously assigned access level */
2571 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2572 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2573 menu,
2574 label,
2575 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2576 container);
2577 g_free(label);
2580 return(menu);
2583 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2585 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2586 GSList *access_domains, *entry;
2588 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2589 menu,
2590 _("People in my company"),
2591 access_levels_menu(sipe_private,
2592 NULL,
2593 "sameEnterprise",
2594 NULL,
2595 FALSE));
2597 /* this is original name, don't edit */
2598 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2599 menu,
2600 _("People in domains connected with my company"),
2601 access_levels_menu(sipe_private,
2602 NULL,
2603 "federated",
2604 NULL,
2605 FALSE));
2607 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2608 menu,
2609 _("People in public domains"),
2610 access_levels_menu(sipe_private,
2611 NULL,
2612 "publicCloud",
2613 NULL,
2614 TRUE));
2616 entry = access_domains = get_access_domains(sipe_private);
2617 while (entry) {
2618 gchar *domain = entry->data;
2619 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2621 /* takes over ownership of entry->data (= domain) */
2622 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2623 menu,
2624 menu_name,
2625 access_levels_menu(sipe_private,
2626 NULL,
2627 "domain",
2628 domain,
2629 TRUE));
2630 g_free(menu_name);
2632 entry = entry->next;
2634 g_slist_free(access_domains);
2636 /* separator */
2637 /* People in domains connected with my company */
2638 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2639 menu,
2640 "-------------------------------------------");
2642 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2643 menu,
2644 _("Add new domain..."),
2645 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2646 NULL);
2648 return(menu);
2651 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2652 const gchar *buddy_name)
2654 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2655 gchar *label;
2658 * Workaround for missing libpurple API to release resources allocated
2659 * during blist_node_menu() callback. See also:
2661 * <http://developer.pidgin.im/ticket/12597>
2663 * We remember all memory blocks in a list and deallocate them when
2665 * - the next time we enter the callback, or
2666 * - the account is disconnected
2668 * That means that after the buddy menu has been closed we have unused
2669 * resources but at least we don't leak them anymore...
2671 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2673 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2674 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2675 menu,
2676 label,
2677 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2678 NULL);
2679 g_free(label);
2681 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2682 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2683 menu,
2684 label,
2685 access_groups_menu(sipe_private));
2686 g_free(label);
2688 menu = access_levels_menu(sipe_private,
2689 menu,
2690 "user",
2691 sipe_get_no_sip_uri(buddy_name),
2692 TRUE);
2694 return(menu);
2698 Local Variables:
2699 mode: c
2700 c-file-style: "bsd"
2701 indent-tabs-mode: t
2702 tab-width: 8
2703 End: