utils: add g_slist_free_full() replacement
[siplcs.git] / src / core / sipe-ocs2007.c
blob71e9b96ce9a76e50220724236b67f06f2eecda84
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * OCS2007+ specific code
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
36 #include <glib.h>
38 #include "sipe-common.h"
39 #include "sipmsg.h"
40 #include "sip-csta.h"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-buddy.h"
44 #include "sipe-cal.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
47 #include "sipe-ews.h"
48 #include "sipe-groupchat.h"
49 #include "sipe-media.h"
50 #include "sipe-nls.h"
51 #include "sipe-ocs2007.h"
52 #include "sipe-schedule.h"
53 #include "sipe-status.h"
54 #include "sipe-utils.h"
55 #include "sipe-xml.h"
57 /** MS-PRES publication */
58 struct sipe_publication {
59 gchar *category;
60 guint instance;
61 guint container;
62 guint version;
63 /** for 'state' category */
64 int availability;
65 /** for 'state:calendarState' category */
66 char *cal_event_hash;
67 /** for 'note' category */
68 gchar *note;
69 /** for 'calendarData' category; 300(Team) container */
70 char *working_hours_xml_str;
71 char *fb_start_str;
72 char *free_busy_base64;
75 /**
76 * 2007-style Activity and Availability.
78 * [MS-PRES] 3.7.5.5
80 * Conversion of legacyInterop availability ranges and activity tokens into
81 * SIPE activity tokens. The descriptions of availability ranges are defined at:
83 * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
85 * The values define the starting point of a range.
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000
94 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
96 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
97 const gchar *activity)
99 guint type;
101 if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
102 type = SIPE_ACTIVITY_OFFLINE;
103 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
104 type = SIPE_ACTIVITY_AVAILABLE;
105 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
106 type = SIPE_ACTIVITY_INACTIVE;
107 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
108 type = sipe_status_token_to_activity(activity);
109 if ((type != SIPE_ACTIVITY_ON_PHONE) &&
110 (type != SIPE_ACTIVITY_IN_CONF))
111 type = SIPE_ACTIVITY_BUSY;
112 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
113 type = SIPE_ACTIVITY_BUSYIDLE;
114 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
115 type = SIPE_ACTIVITY_DND;
116 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
117 type = SIPE_ACTIVITY_BRB;
118 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
119 type = SIPE_ACTIVITY_AWAY;
120 } else {
121 type = SIPE_ACTIVITY_OFFLINE;
124 return sipe_status_activity_to_token(type);
127 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
129 if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
130 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
131 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
132 } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
133 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
134 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
135 } else {
136 return(NULL);
141 * @param sipe_status_id (in)
142 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
143 * requests this token]
145 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
146 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
147 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
148 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
149 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
150 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
151 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
152 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
153 const gchar **activity_token)
155 guint availability;
156 guint activity;
158 if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
159 availability = SIPE_OCS2007_AVAILABILITY_AWAY;
160 activity = SIPE_ACTIVITY_AWAY;
161 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
162 availability = SIPE_OCS2007_AVAILABILITY_BRB;
163 activity = SIPE_ACTIVITY_BRB;
164 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
165 availability = SIPE_OCS2007_AVAILABILITY_DND;
166 activity = SIPE_ACTIVITY_DND;
167 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
168 availability = SIPE_OCS2007_AVAILABILITY_BUSY;
169 activity = SIPE_ACTIVITY_BUSY;
170 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
171 availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
172 activity = SIPE_ACTIVITY_ONLINE;
173 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
174 availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
175 activity = SIPE_ACTIVITY_UNSET;
176 } else {
177 /* Offline or invisible */
178 availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
179 activity = SIPE_ACTIVITY_OFFLINE;
182 if (activity_token) {
183 *activity_token = sipe_status_activity_to_token(activity);
186 return(availability);
189 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
191 return(SIPE_OCS2007_AVAILABILITY_BUSY >=
192 sipe_ocs2007_availability_from_status(status_id, NULL));
196 gboolean sipe_ocs2007_availability_is_away(guint availability)
198 return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
201 static void send_presence_publish(struct sipe_core_private *sipe_private,
202 const char *publications);
204 static void free_publication(struct sipe_publication *publication)
206 g_free(publication->category);
207 g_free(publication->cal_event_hash);
208 g_free(publication->note);
210 g_free(publication->working_hours_xml_str);
211 g_free(publication->fb_start_str);
212 g_free(publication->free_busy_base64);
214 g_free(publication);
217 struct hash_table_delete_payload {
218 GHashTable *hash_table;
219 guint container;
222 static void sipe_remove_category_container_publications_cb(const gchar *name,
223 struct sipe_publication *publication,
224 struct hash_table_delete_payload *payload)
226 if (publication->container == payload->container) {
227 g_hash_table_remove(payload->hash_table, name);
231 static void sipe_remove_category_container_publications(GHashTable *our_publications,
232 const gchar *category,
233 guint container)
235 struct hash_table_delete_payload payload;
236 payload.hash_table = g_hash_table_lookup(our_publications, category);
238 if (!payload.hash_table) return;
240 payload.container = container;
241 g_hash_table_foreach(payload.hash_table,
242 (GHFunc)sipe_remove_category_container_publications_cb,
243 &payload);
246 /** MS-PRES container */
247 struct sipe_container {
248 guint id;
249 guint version;
250 GSList *members;
253 /** MS-PRES container member */
254 struct sipe_container_member {
255 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
256 gchar *type;
257 gchar *value;
260 static const guint containers[] = {32000, 400, 300, 200, 100};
261 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
263 static void free_container_member(struct sipe_container_member *member)
265 if (!member) return;
267 g_free(member->type);
268 g_free(member->value);
269 g_free(member);
272 static void sipe_ocs2007_free_container(struct sipe_container *container)
274 GSList *entry;
276 if (!container) return;
278 entry = container->members;
279 while (entry) {
280 void *data = entry->data;
281 entry = g_slist_remove(entry, data);
282 free_container_member((struct sipe_container_member *)data);
284 g_free(container);
287 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
289 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
290 sipe_utils_slist_free_full(sipe_private->blist_menu_containers,
291 (GDestroyNotify) sipe_ocs2007_free_container);
292 sipe_private->blist_menu_containers = NULL;
295 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
296 struct sipe_container *container)
298 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
299 container);
302 static struct sipe_container *create_container(guint index,
303 const gchar *member_type,
304 const gchar *member_value,
305 gboolean is_group)
307 struct sipe_container *container = g_new0(struct sipe_container, 1);
308 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
310 container->id = is_group ? (guint) -1 : containers[index];
311 container->members = g_slist_append(container->members, member);
312 member->type = g_strdup(member_type);
313 member->value = g_strdup(member_value);
315 return(container);
318 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
320 sipe_utils_slist_free_full(sipe_private->containers,
321 (GDestroyNotify) sipe_ocs2007_free_container);
325 * Finds locally stored MS-PRES container member
327 static struct sipe_container_member *
328 sipe_find_container_member(struct sipe_container *container,
329 const gchar *type,
330 const gchar *value)
332 struct sipe_container_member *member;
333 GSList *entry;
335 if (container == NULL || type == NULL) {
336 return NULL;
339 entry = container->members;
340 while (entry) {
341 member = entry->data;
342 if (sipe_strcase_equal(member->type, type) &&
343 sipe_strcase_equal(member->value, value))
345 return member;
347 entry = entry->next;
349 return NULL;
353 * Finds locally stored MS-PRES container by id
355 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
356 guint id)
358 GSList *entry = sipe_private->containers;
359 while (entry) {
360 struct sipe_container *container = entry->data;
361 if (id == container->id) {
362 return container;
364 entry = entry->next;
366 return NULL;
369 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
370 const gchar *type,
371 const gchar *value)
373 unsigned int i = 0;
374 const gchar *value_mod = value;
376 if (!type) return -1;
378 if (sipe_strequal("user", type)) {
379 value_mod = sipe_get_no_sip_uri(value);
382 for (i = 0; i < CONTAINERS_LEN; i++) {
383 struct sipe_container_member *member;
384 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
385 if (!container) continue;
387 member = sipe_find_container_member(container, type, value_mod);
388 if (member) return containers[i];
391 return -1;
395 * Returns pointer to domain part in provided Email URL
397 * @param email an email URL. Example: first.last@hq.company.com
398 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
400 * Doesn't allocate memory
402 static const gchar *sipe_get_domain(const gchar *email)
404 gchar *tmp;
406 if (!email) return NULL;
408 tmp = strstr(email, "@");
410 if (tmp && ((tmp+1) < (email + strlen(email)))) {
411 return tmp+1;
412 } else {
413 return NULL;
417 /* @TODO: replace with binary search for faster access? */
418 /** source: http://support.microsoft.com/kb/897567 */
419 static const gchar * const public_domains[] = {
420 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
421 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
422 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
423 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
424 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
425 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
426 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
427 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
428 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
429 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
430 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
431 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
432 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
433 "yahoo.com",
434 NULL};
436 static gboolean sipe_is_public_domain(const gchar *domain)
438 int i = 0;
439 while (public_domains[i]) {
440 if (sipe_strcase_equal(public_domains[i], domain)) {
441 return TRUE;
443 i++;
445 return FALSE;
449 * Access Levels
450 * 32000 - Blocked
451 * 400 - Personal
452 * 300 - Team
453 * 200 - Company
454 * 100 - Public
456 const gchar *sipe_ocs2007_access_level_name(guint id)
458 switch (id) {
459 case 32000: return _("Blocked");
460 case 400: return _("Personal");
461 case 300: return _("Team");
462 case 200: return _("Company");
463 case 100: return _("Public");
465 return _("Unknown");
468 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
469 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
470 const gchar *type,
471 const gchar *value,
472 gboolean *is_group_access)
474 int container_id = -1;
476 if (sipe_strequal("user", type)) {
477 const char *domain;
478 const char *no_sip_uri = sipe_get_no_sip_uri(value);
480 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
481 if (container_id >= 0) {
482 if (is_group_access) *is_group_access = FALSE;
483 return container_id;
486 domain = sipe_get_domain(no_sip_uri);
487 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
488 if (container_id >= 0) {
489 if (is_group_access) *is_group_access = TRUE;
490 return container_id;
493 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
494 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
495 if (is_group_access) *is_group_access = TRUE;
496 return container_id;
499 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
500 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
501 if (is_group_access) *is_group_access = TRUE;
502 return container_id;
505 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
506 if ((container_id >= 0)) {
507 if (is_group_access) *is_group_access = TRUE;
508 return container_id;
510 } else {
511 container_id = sipe_find_member_access_level(sipe_private, type, value);
512 if (is_group_access) *is_group_access = FALSE;
515 return container_id;
518 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
520 struct sipe_container *container;
521 struct sipe_container_member *member;
522 GSList *entry;
523 GSList *entry2;
524 GSList *res = NULL;
526 entry = sipe_private->containers;
527 while (entry) {
528 container = entry->data;
530 entry2 = container->members;
531 while (entry2) {
532 member = entry2->data;
533 if (sipe_strcase_equal(member->type, "domain"))
535 res = sipe_utils_slist_insert_unique_sorted(res,
536 g_strdup(member->value),
537 (GCompareFunc)g_ascii_strcasecmp,
538 g_free);
540 entry2 = entry2->next;
542 entry = entry->next;
544 return res;
547 static void sipe_send_container_members_prepare(const guint container_id,
548 const guint container_version,
549 const gchar *action,
550 const gchar *type,
551 const gchar *value,
552 char **container_xmls)
554 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
555 gchar *body;
557 if (!container_xmls) return;
559 body = g_strdup_printf(
560 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
561 container_id,
562 container_version,
563 action,
564 type,
565 value_str);
566 g_free(value_str);
568 if ((*container_xmls) == NULL) {
569 *container_xmls = body;
570 } else {
571 char *tmp = *container_xmls;
573 *container_xmls = g_strconcat(*container_xmls, body, NULL);
574 g_free(tmp);
575 g_free(body);
579 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
580 char *container_xmls)
582 gchar *self;
583 gchar *contact;
584 gchar *hdr;
585 gchar *body;
587 if (!container_xmls) return;
589 self = sip_uri_self(sipe_private);
590 body = g_strdup_printf(
591 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
592 "%s"
593 "</setContainerMembers>",
594 container_xmls);
596 contact = get_contact(sipe_private);
597 hdr = g_strdup_printf("Contact: %s\r\n"
598 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
599 g_free(contact);
601 sip_transport_service(sipe_private,
602 self,
603 hdr,
604 body,
605 NULL);
607 g_free(hdr);
608 g_free(body);
609 g_free(self);
613 * @param container_id a new access level. If -1 then current access level
614 * is just removed (I.e. the member is removed from all containers).
615 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
616 * @param value a value for member. E.g. SIP URI for "user" member type.
618 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
619 const int container_id,
620 const gchar *type,
621 const gchar *value)
623 unsigned int i;
624 int current_container_id = -1;
625 char *container_xmls = NULL;
627 /* for each container: find/delete */
628 for (i = 0; i < CONTAINERS_LEN; i++) {
629 struct sipe_container_member *member;
630 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
632 if (!container) continue;
634 member = sipe_find_container_member(container, type, value);
635 if (member) {
636 current_container_id = containers[i];
637 /* delete/publish current access level */
638 if (container_id < 0 || container_id != current_container_id) {
639 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
640 /* remove member from our cache, to be able to recalculate AL below */
641 container->members = g_slist_remove(container->members, member);
642 current_container_id = -1;
647 /* recalculate AL below */
648 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
650 /* assign/publish new access level */
651 if (container_id != current_container_id && container_id >= 0) {
652 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
653 guint version = container ? container->version : 0;
655 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
658 if (container_xmls) {
659 sipe_send_set_container_members(sipe_private, container_xmls);
661 g_free(container_xmls);
664 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
665 gpointer parameter)
667 struct sipe_container *container = parameter;
668 struct sipe_container_member *member;
670 if (!container || !container->members) return;
672 member = ((struct sipe_container_member *)container->members->data);
674 if (!member->type) return;
676 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
677 container->id, member->type, member->value ? member->value : "");
679 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
680 container->id,
681 member->type,
682 member->value);
686 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
687 const gchar *domain,
688 guint index)
690 /* move Blocked first */
691 guint i = (index == 4) ? 0 : index + 1;
692 guint container_id = containers[i];
694 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
695 domain ? domain : "", index, container_id);
697 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
698 container_id,
699 "domain",
700 domain);
704 * Schedules process of self status publish
705 * based on own calendar information.
706 * Should be scheduled to the beginning of every
707 * 15 min interval, like:
708 * 13:00, 13:15, 13:30, 13:45, etc.
711 static void schedule_publish_update(struct sipe_core_private *sipe_private,
712 time_t calculate_from)
714 int interval = 5*60;
715 /** start of the beginning of closest 5 min interval. */
716 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
718 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
719 asctime(localtime(&calculate_from)));
720 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
721 asctime(localtime(&next_start)));
723 sipe_schedule_seconds(sipe_private,
724 "<+2007-cal-status>",
725 NULL,
726 next_start - time(NULL),
727 sipe_ocs2007_presence_publish,
728 NULL);
732 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
733 * @param availability (%d) Ex.: 6500
735 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
736 "<availability>%d</availability>"
738 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
739 * @param token (%s) Ex.: in-a-meeting
740 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
741 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
743 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
744 "<activity token=\"%s\" %s %s></activity>"
746 * Publishes 'calendarState' category.
747 * @param instance (%u) Ex.: 1339299275
748 * @param version (%u) Ex.: 1
749 * @param uri (%s) Ex.: john@contoso.com
750 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
751 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
752 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
753 * @param meeting_subject (%s) Ex.: Customer Meeting
754 * @param meeting_location (%s) Ex.: Conf Room 100
756 * @param instance (%u) Ex.: 1339299275
757 * @param version (%u) Ex.: 1
758 * @param uri (%s) Ex.: john@contoso.com
759 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
760 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
761 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
762 * @param meeting_subject (%s) Ex.: Customer Meeting
763 * @param meeting_location (%s) Ex.: Conf Room 100
765 #define SIPE_PUB_XML_STATE_CALENDAR \
766 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
767 "<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\">"\
768 "%s"\
769 "%s"\
770 "<endpointLocation/>"\
771 "<meetingSubject>%s</meetingSubject>"\
772 "<meetingLocation>%s</meetingLocation>"\
773 "</state>"\
774 "</publication>"\
775 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
776 "<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\">"\
777 "%s"\
778 "%s"\
779 "<endpointLocation/>"\
780 "<meetingSubject>%s</meetingSubject>"\
781 "<meetingLocation>%s</meetingLocation>"\
782 "</state>"\
783 "</publication>"
785 * Publishes to clear 'calendarState' and 'phoneState' category
786 * @param instance (%u) Ex.: 1251210982
787 * @param version (%u) Ex.: 1
789 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
790 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
791 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
794 * Publishes to clear any category
795 * @param category_name (%s) Ex.: state
796 * @param instance (%u) Ex.: 536870912
797 * @param container (%u) Ex.: 3
798 * @param version (%u) Ex.: 1
799 * @param expireType (%s) Ex.: static
801 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
802 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
805 * Publishes 'note' category.
806 * @param instance (%u) Ex.: 2135971629; 0 for personal
807 * @param container (%u) Ex.: 200
808 * @param version (%u) Ex.: 2
809 * @param type (%s) Ex.: personal or OOF
810 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
811 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
812 * @param body (%s) Ex.: In the office
814 #define SIPE_PUB_XML_NOTE \
815 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
816 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
817 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
818 "</note>"\
819 "</publication>"
821 * Publishes 'phoneState' category.
822 * @param instance (%u) Ex.: 1339299275
823 * @param version (%u) Ex.: 1
824 * @param availability (%u) Ex.: 6500
825 * @param token (%s) Ex.: on-the-phone
826 * @param minAvailability (%u) generally same as availability
828 * @param instance (%u) Ex.: 1339299275
829 * @param version (%u) Ex.: 1
830 * @param availability (%u) Ex.: 6500
831 * @param token (%s) Ex.: on-the-phone
832 * @param minAvailability (%u) generally same as availability
834 #define SIPE_PUB_XML_STATE_PHONE \
835 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
836 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
837 "<availability>%u</availability>"\
838 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
839 "</state>"\
840 "</publication>"\
841 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
842 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
843 "<availability>%u</availability>"\
844 "<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"8999\"/>"\
845 "</state>"\
846 "</publication>"
849 * Only Busy and OOF calendar event are published.
850 * Different instances are used for that.
852 * Must be g_free'd after use.
854 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
855 struct sipe_cal_event *event,
856 const char *uri,
857 int cal_satus)
859 gchar *start_time_str;
860 int availability = 0;
861 gchar *res;
862 gchar *tmp = NULL;
863 guint instance = (cal_satus == SIPE_CAL_OOF) ?
864 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
865 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
867 /* key is <category><instance><container> */
868 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
869 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
870 struct sipe_publication *publication_2 =
871 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
872 struct sipe_publication *publication_3 =
873 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
875 g_free(key_2);
876 g_free(key_3);
878 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
879 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
880 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
881 return NULL;
884 if (event &&
885 publication_3 &&
886 (publication_3->availability == availability) &&
887 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
889 g_free(tmp);
890 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
891 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
892 return NULL; /* nothing to update */
894 g_free(tmp);
896 if (event &&
897 (event->cal_status == SIPE_CAL_BUSY ||
898 event->cal_status == SIPE_CAL_OOF))
900 gchar *availability_xml_str = NULL;
901 gchar *activity_xml_str = NULL;
902 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
903 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
905 if (event->cal_status == SIPE_CAL_BUSY) {
906 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
907 SIPE_OCS2007_AVAILABILITY_BUSY);
910 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
911 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
912 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
913 "minAvailability=\"6500\"",
914 "maxAvailability=\"8999\"");
915 } else if (event->cal_status == SIPE_CAL_OOF) {
916 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
917 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
918 "minAvailability=\"12000\"",
919 "");
921 start_time_str = sipe_utils_time_to_str(event->start_time);
923 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
924 instance,
925 publication_2 ? publication_2->version : 0,
926 uri,
927 start_time_str,
928 availability_xml_str ? availability_xml_str : "",
929 activity_xml_str ? activity_xml_str : "",
930 escaped_subject ? escaped_subject : "",
931 escaped_location ? escaped_location : "",
933 instance,
934 publication_3 ? publication_3->version : 0,
935 uri,
936 start_time_str,
937 availability_xml_str ? availability_xml_str : "",
938 activity_xml_str ? activity_xml_str : "",
939 escaped_subject ? escaped_subject : "",
940 escaped_location ? escaped_location : ""
942 g_free(escaped_location);
943 g_free(escaped_subject);
944 g_free(start_time_str);
945 g_free(availability_xml_str);
946 g_free(activity_xml_str);
949 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
951 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
952 instance,
953 publication_2 ? publication_2->version : 0,
955 instance,
956 publication_3 ? publication_3->version : 0
960 return res;
964 * Returns 'note' XML part for publication.
965 * Must be g_free'd after use.
967 * Protocol format for Note is plain text.
969 * @param note a note in Sipe internal HTML format
970 * @param note_type either personal or OOF
972 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
973 const char *note, /* html */
974 const char *note_type,
975 time_t note_start,
976 time_t note_end)
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 (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 is_user_state)
1464 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1465 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1466 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1467 /* key is <category><instance><container> */
1468 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1469 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1470 struct sipe_publication *publication_2 =
1471 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1472 struct sipe_publication *publication_3 =
1473 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1475 g_free(key_2);
1476 g_free(key_3);
1478 if (publication_2 && (publication_2->availability == availability))
1480 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1481 return NULL; /* nothing to update */
1484 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1485 instance,
1486 publication_2 ? publication_2->version : 0,
1487 availability,
1488 instance,
1489 publication_3 ? publication_3->version : 0,
1490 availability);
1494 * Returns 'machineState' XML part for publication.
1495 * Must be g_free'd after use.
1497 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
1499 return sipe_publish_get_category_state(sipe_private, FALSE);
1503 * Returns 'userState' XML part for publication.
1504 * Must be g_free'd after use.
1506 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
1508 return sipe_publish_get_category_state(sipe_private, TRUE);
1511 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1513 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1514 gchar *pub_machine;
1515 gchar *publications;
1517 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1519 pub_machine = sipe_publish_get_category_state_machine(sipe_private);
1520 publications = g_strdup_printf("%s%s",
1521 pub_device,
1522 pub_machine ? pub_machine : "");
1523 g_free(pub_device);
1524 g_free(pub_machine);
1526 send_presence_publish(sipe_private, publications);
1527 g_free(publications);
1530 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1531 struct sipmsg *msg,
1532 struct transaction *trans)
1534 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1536 if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1537 sipe_xml *xml;
1538 const sipe_xml *node;
1539 gchar *fault_code;
1540 GHashTable *faults;
1541 int index_our;
1542 gboolean has_device_publication = FALSE;
1544 xml = sipe_xml_parse(msg->body, msg->bodylen);
1546 /* test if version mismatch fault */
1547 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1548 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1549 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1550 g_free(fault_code);
1551 sipe_xml_free(xml);
1552 return TRUE;
1554 g_free(fault_code);
1556 /* accumulating information about faulty versions */
1557 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1558 for (node = sipe_xml_child(xml, "details/operation");
1559 node;
1560 node = sipe_xml_twin(node))
1562 const gchar *index = sipe_xml_attribute(node, "index");
1563 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1565 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1566 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1568 sipe_xml_free(xml);
1570 /* here we are parsing our own request to figure out what publication
1571 * referenced here only by index went wrong
1573 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1575 /* publication */
1576 for (node = sipe_xml_child(xml, "publications/publication"),
1577 index_our = 1; /* starts with 1 - our first publication */
1578 node;
1579 node = sipe_xml_twin(node), index_our++)
1581 gchar *idx = g_strdup_printf("%d", index_our);
1582 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1583 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1584 g_free(idx);
1586 if (sipe_strequal("device", categoryName)) {
1587 has_device_publication = TRUE;
1590 if (curVersion) { /* fault exist on this index */
1591 const gchar *container = sipe_xml_attribute(node, "container");
1592 const gchar *instance = sipe_xml_attribute(node, "instance");
1593 /* key is <category><instance><container> */
1594 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1595 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1597 if (category) {
1598 struct sipe_publication *publication =
1599 g_hash_table_lookup(category, key);
1601 SIPE_DEBUG_INFO("key is %s", key);
1603 if (publication) {
1604 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1605 key, curVersion, publication->version);
1606 /* updating publication's version to the correct one */
1607 publication->version = atoi(curVersion);
1609 } else {
1610 /* We somehow lost this category from our publications... */
1611 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1612 publication->category = g_strdup(categoryName);
1613 publication->instance = atoi(instance);
1614 publication->container = atoi(container);
1615 publication->version = atoi(curVersion);
1616 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1617 g_free, (GDestroyNotify)free_publication);
1618 g_hash_table_insert(category, g_strdup(key), publication);
1619 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1620 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1622 g_free(key);
1625 sipe_xml_free(xml);
1626 g_hash_table_destroy(faults);
1628 /* rebublishing with right versions */
1629 if (has_device_publication) {
1630 send_publish_category_initial(sipe_private);
1631 } else {
1632 sipe_status_update(sipe_private, NULL);
1635 return TRUE;
1639 * Publishes categories.
1640 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1641 * @param publications (%s) XML publications
1643 #define SIPE_SEND_PRESENCE \
1644 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1645 "<publications uri=\"%s\">"\
1646 "%s"\
1647 "</publications>"\
1648 "</publish>"
1650 static void send_presence_publish(struct sipe_core_private *sipe_private,
1651 const char *publications)
1653 gchar *uri;
1654 gchar *doc;
1655 gchar *tmp;
1656 gchar *hdr;
1658 uri = sip_uri_self(sipe_private);
1659 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1660 uri,
1661 publications);
1663 tmp = get_contact(sipe_private);
1664 hdr = g_strdup_printf("Contact: %s\r\n"
1665 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1667 sip_transport_service(sipe_private,
1668 uri,
1669 hdr,
1670 doc,
1671 process_send_presence_category_publish_response);
1673 g_free(tmp);
1674 g_free(hdr);
1675 g_free(uri);
1676 g_free(doc);
1680 * Publishes self status
1681 * based on own calendar information.
1683 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1684 SIPE_UNUSED_PARAMETER void *unused)
1686 struct sipe_calendar* cal = sipe_private->calendar;
1687 struct sipe_cal_event* event = NULL;
1688 gchar *pub_cal_working_hours = NULL;
1689 gchar *pub_cal_free_busy = NULL;
1690 gchar *pub_calendar = NULL;
1691 gchar *pub_calendar2 = NULL;
1692 gchar *pub_oof_note = NULL;
1693 const gchar *oof_note;
1694 time_t oof_start = 0;
1695 time_t oof_end = 0;
1697 if (!cal) {
1698 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1699 return;
1702 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1703 if (cal->cal_events) {
1704 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1707 if (!event) {
1708 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1709 } else {
1710 char *desc = sipe_cal_event_describe(event);
1711 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
1712 g_free(desc);
1715 /* Logic
1716 if OOF
1717 OOF publish, Busy clean
1718 ilse if Busy
1719 OOF clean, Busy publish
1720 else
1721 OOF clean, Busy clean
1723 if (event && event->cal_status == SIPE_CAL_OOF) {
1724 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1725 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1726 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1727 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1728 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1729 } else {
1730 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1731 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1734 oof_note = sipe_ews_get_oof_note(cal);
1735 if (sipe_strequal("Scheduled", cal->oof_state)) {
1736 oof_start = cal->oof_start;
1737 oof_end = cal->oof_end;
1739 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
1741 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1742 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1744 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1745 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1746 } else {
1747 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1748 pub_cal_working_hours ? pub_cal_working_hours : "",
1749 pub_cal_free_busy ? pub_cal_free_busy : "",
1750 pub_calendar ? pub_calendar : "",
1751 pub_calendar2 ? pub_calendar2 : "",
1752 pub_oof_note ? pub_oof_note : "");
1754 send_presence_publish(sipe_private, publications);
1755 g_free(publications);
1758 g_free(pub_cal_working_hours);
1759 g_free(pub_cal_free_busy);
1760 g_free(pub_calendar);
1761 g_free(pub_calendar2);
1762 g_free(pub_oof_note);
1764 /* repeat scheduling */
1765 schedule_publish_update(sipe_private, time(NULL));
1768 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private)
1770 gchar *pub_state = sipe_status_changed_by_user(sipe_private) ?
1771 sipe_publish_get_category_state_user(sipe_private) :
1772 sipe_publish_get_category_state_machine(sipe_private);
1773 gchar *pub_note = sipe_publish_get_category_note(sipe_private,
1774 sipe_private->note,
1775 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1778 gchar *publications;
1780 if (!pub_state && !pub_note) {
1781 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1782 return;
1785 publications = g_strdup_printf("%s%s",
1786 pub_state ? pub_state : "",
1787 pub_note ? pub_note : "");
1789 g_free(pub_state);
1790 g_free(pub_note);
1792 send_presence_publish(sipe_private, publications);
1793 g_free(publications);
1796 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1798 gchar *publications = NULL;
1799 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1801 /* key is <category><instance><container> */
1802 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1803 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1804 struct sipe_publication *publication_2 =
1805 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1806 struct sipe_publication *publication_3 =
1807 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1808 g_free(key_2);
1809 g_free(key_3);
1811 #ifdef HAVE_VV
1812 if (sipe_private->media_call) {
1813 guint availability;
1814 const gchar *token;
1815 if (sipe_media_is_conference_call(sipe_private->media_call)) {
1816 availability = 7000;
1817 token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1818 } else {
1819 availability = 6500;
1820 token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1823 publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1824 instance, publication_2 ? publication_2->version : 0,
1825 availability, token, availability,
1826 instance, publication_3 ? publication_3->version : 0,
1827 availability, token, availability);
1828 } else
1829 #endif
1831 publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1832 instance, publication_2 ? publication_2->version : 0,
1833 instance, publication_3 ? publication_3->version : 0);
1836 send_presence_publish(sipe_private, publications);
1837 g_free(publications);
1840 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1841 gpointer value,
1842 GString* str)
1844 struct sipe_publication *publication = value;
1846 g_string_append_printf( str,
1847 SIPE_PUB_XML_PUBLICATION_CLEAR,
1848 publication->category,
1849 publication->instance,
1850 publication->container,
1851 publication->version,
1852 "static");
1855 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1857 GString* str;
1858 gchar *publications;
1860 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1861 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1862 return;
1865 str = g_string_new(NULL);
1866 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1867 publications = g_string_free(str, FALSE);
1869 send_presence_publish(sipe_private, publications);
1870 g_free(publications);
1873 /* key is <category><instance><container> */
1874 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1875 const gchar *key)
1877 GSList *entry;
1879 /* filling keys for our publications if not yet cached */
1880 if (!sipe_private->our_publication_keys) {
1881 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1882 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1883 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1884 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1885 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1886 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1887 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1888 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1890 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1891 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1892 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1893 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1894 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1895 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1896 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1897 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1898 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1899 SIPE_DEBUG_INFO("\tNote : %u", 0);
1900 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1902 /* device */
1903 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1904 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1906 /* state:machineState */
1907 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1908 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1909 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1910 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1912 /* state:userState */
1913 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1914 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1915 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1916 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1918 /* state:calendarState */
1919 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1920 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1921 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1922 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1924 /* state:calendarState OOF */
1925 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1926 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1927 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1928 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1930 /* state:phoneState */
1931 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1932 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1933 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1934 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1936 /* note */
1937 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1938 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1939 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1940 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1941 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1942 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1944 /* note OOF */
1945 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1946 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1947 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1948 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1949 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1950 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1952 /* calendarData:WorkingHours */
1953 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1954 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1955 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1956 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1957 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1958 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1959 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1960 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1961 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1962 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1963 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1964 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1966 /* calendarData:FreeBusy */
1967 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1968 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
1969 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1970 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
1971 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1972 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
1973 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1974 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
1975 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1976 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
1977 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1978 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
1980 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
1981 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
1984 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1986 entry = sipe_private->our_publication_keys;
1987 while (entry) {
1988 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1989 if (sipe_strequal(entry->data, key)) {
1990 return TRUE;
1992 entry = entry->next;
1994 return FALSE;
1997 static void sipe_refresh_blocked_status_cb(char *buddy_name,
1998 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1999 struct sipe_core_private *sipe_private)
2001 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2002 gboolean blocked = (container_id == 32000);
2003 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2005 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2006 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2008 if (blocked != blocked_in_blist) {
2009 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2013 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2015 g_hash_table_foreach(sipe_private->buddies,
2016 (GHFunc) sipe_refresh_blocked_status_cb,
2017 sipe_private);
2021 * When we receive some self (BE) NOTIFY with a new subscriber
2022 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2025 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2026 struct sipmsg *msg)
2028 gchar *contact;
2029 gchar *to;
2030 sipe_xml *xml;
2031 const sipe_xml *node;
2032 const sipe_xml *node2;
2033 char *display_name = NULL;
2034 char *uri;
2035 GSList *category_names = NULL;
2036 int aggreg_avail = 0;
2037 gchar *activity_token = NULL;
2038 gboolean do_update_status = FALSE;
2039 gboolean has_note_cleaned = FALSE;
2040 GHashTable *devices;
2042 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2044 xml = sipe_xml_parse(msg->body, msg->bodylen);
2045 if (!xml) return;
2047 contact = get_contact(sipe_private);
2048 to = sip_uri_self(sipe_private);
2050 /* categories */
2051 /* set list of categories participating in this XML */
2052 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2053 const gchar *name = sipe_xml_attribute(node, "name");
2054 category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2055 (gchar *)name,
2056 (GCompareFunc)strcmp,
2057 NULL);
2059 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2060 category_names ? (int) g_slist_length(category_names) : -1);
2061 /* drop category information */
2062 if (category_names) {
2063 GSList *entry = category_names;
2064 while (entry) {
2065 GHashTable *cat_publications;
2066 const gchar *category = entry->data;
2067 entry = entry->next;
2068 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2069 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2070 if (cat_publications) {
2071 g_hash_table_remove(sipe_private->our_publications, category);
2072 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2076 g_slist_free(category_names);
2078 /* filling our categories reflected in roaming data */
2079 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2080 g_free, NULL);
2081 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2082 const char *tmp;
2083 const gchar *name = sipe_xml_attribute(node, "name");
2084 guint container = sipe_xml_int_attribute(node, "container", -1);
2085 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2086 guint version = sipe_xml_int_attribute(node, "version", 0);
2087 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2088 sipe_utils_str_to_time(tmp) : 0;
2089 gchar *key;
2090 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2092 /* Ex. clear note: <category name="note"/> */
2093 if (container == (guint)-1) {
2094 g_free(sipe_private->note);
2095 sipe_private->note = NULL;
2096 do_update_status = TRUE;
2097 continue;
2100 /* Ex. clear note: <category name="note" container="200"/> */
2101 if (instance == (guint)-1) {
2102 if (container == 200) {
2103 g_free(sipe_private->note);
2104 sipe_private->note = NULL;
2105 do_update_status = TRUE;
2107 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2108 sipe_remove_category_container_publications(
2109 sipe_private->our_publications, name, container);
2110 continue;
2113 /* key is <category><instance><container> */
2114 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2115 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2117 /* capture all userState publication for later clean up if required */
2118 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2119 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2121 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2122 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2123 publication->category = g_strdup(name);
2124 publication->instance = instance;
2125 publication->container = container;
2126 publication->version = version;
2128 if (!sipe_private->user_state_publications) {
2129 sipe_private->user_state_publications = g_hash_table_new_full(
2130 g_str_hash, g_str_equal,
2131 g_free, (GDestroyNotify)free_publication);
2133 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2134 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2135 key, version);
2139 /* count each client instance only once */
2140 if (sipe_strequal(name, "device"))
2141 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2143 if (sipe_is_our_publication(sipe_private, key)) {
2144 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2146 publication->category = g_strdup(name);
2147 publication->instance = instance;
2148 publication->container = container;
2149 publication->version = version;
2151 /* filling publication->availability */
2152 if (sipe_strequal(name, "state")) {
2153 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2154 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2156 if (xn_avail) {
2157 gchar *avail_str = sipe_xml_data(xn_avail);
2158 if (avail_str) {
2159 publication->availability = atoi(avail_str);
2161 g_free(avail_str);
2163 /* for calendarState */
2164 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2165 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2166 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2168 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2169 if (xn_activity) {
2170 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2171 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2173 event->is_meeting = TRUE;
2176 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2177 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2179 publication->cal_event_hash = sipe_cal_event_hash(event);
2180 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2181 publication->cal_event_hash);
2182 sipe_cal_event_free(event);
2185 /* filling publication->note */
2186 if (sipe_strequal(name, "note")) {
2187 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2189 if (!has_note_cleaned) {
2190 has_note_cleaned = TRUE;
2192 g_free(sipe_private->note);
2193 sipe_private->note = NULL;
2194 sipe_private->note_since = publish_time;
2196 do_update_status = TRUE;
2199 g_free(publication->note);
2200 publication->note = NULL;
2201 if (xn_body) {
2202 char *tmp;
2204 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2205 g_free(tmp);
2206 if (publish_time >= sipe_private->note_since) {
2207 g_free(sipe_private->note);
2208 sipe_private->note = g_strdup(publication->note);
2209 sipe_private->note_since = publish_time;
2210 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2211 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2212 else
2213 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2215 do_update_status = TRUE;
2220 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2221 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2222 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2223 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2224 if (xn_free_busy) {
2225 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2226 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2228 if (xn_working_hours) {
2229 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2233 if (!cat_publications) {
2234 cat_publications = g_hash_table_new_full(
2235 g_str_hash, g_str_equal,
2236 g_free, (GDestroyNotify)free_publication);
2237 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2238 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2240 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2241 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2243 g_free(key);
2245 /* aggregateState (not an our publication) from 2-nd container */
2246 if (sipe_strequal(name, "state") && container == 2) {
2247 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2248 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2250 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2251 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2253 if (xn_avail) {
2254 gchar *avail_str = sipe_xml_data(xn_avail);
2255 if (avail_str) {
2256 aggreg_avail = atoi(avail_str);
2258 g_free(avail_str);
2261 do_update_status = TRUE;
2264 if (xn_activity) {
2265 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2269 /* userProperties published by server from AD */
2270 if (!sipe_private->csta &&
2271 sipe_strequal(name, "userProperties")) {
2272 const sipe_xml *line;
2273 /* line, for Remote Call Control (RCC) */
2274 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2275 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2276 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2277 gchar *line_uri;
2279 if (!line_server || !(sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual"))) continue;
2281 line_uri = sipe_xml_data(line);
2282 if (line_uri) {
2283 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s", line_uri, line_server);
2284 sip_csta_open(sipe_private, line_uri, line_server);
2286 g_free(line_uri);
2288 break;
2292 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2293 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2295 /* active clients for user account */
2296 if (g_hash_table_size(devices) > 1) {
2297 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2298 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2299 g_hash_table_size(devices));
2300 } else {
2301 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2302 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2304 g_hash_table_destroy(devices);
2306 /* containers */
2307 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2308 guint id = sipe_xml_int_attribute(node, "id", 0);
2309 struct sipe_container *container = sipe_find_container(sipe_private, id);
2311 if (container) {
2312 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2313 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2314 sipe_ocs2007_free_container(container);
2316 container = g_new0(struct sipe_container, 1);
2317 container->id = id;
2318 container->version = sipe_xml_int_attribute(node, "version", 0);
2319 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2320 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2322 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2323 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2324 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2325 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2326 container->members = g_slist_append(container->members, member);
2327 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2328 member->type, member->value ? member->value : "");
2332 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2333 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2334 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2335 char *container_xmls = NULL;
2336 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2337 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2339 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2340 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2341 /* initial set-up to let counterparties see your status */
2342 if (sameEnterpriseAL < 0) {
2343 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2344 guint version = container ? container->version : 0;
2345 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2347 if (federatedAL < 0) {
2348 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2349 guint version = container ? container->version : 0;
2350 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2352 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2354 if (container_xmls) {
2355 sipe_send_set_container_members(sipe_private, container_xmls);
2357 g_free(container_xmls);
2360 /* Refresh contacts' blocked status */
2361 sipe_refresh_blocked_status(sipe_private);
2363 /* subscribers */
2364 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2365 const char *user;
2366 const char *acknowledged;
2367 gchar *hdr;
2368 gchar *body;
2370 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2371 if (!user) continue;
2372 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2373 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2374 uri = sip_uri_from_name(user);
2376 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2377 sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2379 acknowledged= sipe_xml_attribute(node, "acknowledged");
2380 if(sipe_strcase_equal(acknowledged,"false")){
2381 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2382 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2383 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2386 hdr = g_strdup_printf(
2387 "Contact: %s\r\n"
2388 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2390 body = g_strdup_printf(
2391 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2392 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2393 "</setSubscribers>", user);
2395 sip_transport_service(sipe_private,
2397 hdr,
2398 body,
2399 NULL);
2400 g_free(body);
2401 g_free(hdr);
2403 g_free(display_name);
2404 g_free(uri);
2407 g_free(contact);
2408 sipe_xml_free(xml);
2410 /* Publish initial state if not yet.
2411 * Assuming this happens on initial responce to subscription to roaming-self
2412 * so we've already updated our roaming data in full.
2413 * Only for 2007+
2415 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2416 send_publish_category_initial(sipe_private);
2417 sipe_groupchat_init(sipe_private);
2418 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2419 /* dalayed run */
2420 sipe_cal_delayed_calendar_update(sipe_private);
2421 do_update_status = FALSE;
2422 } else if (aggreg_avail) {
2424 if (aggreg_avail &&
2425 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2426 /* not offline */
2427 sipe_status_set_token(sipe_private,
2428 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2429 } else {
2430 /* do not let offline status switch us off */
2431 sipe_status_set_activity(sipe_private,
2432 SIPE_ACTIVITY_INVISIBLE);
2436 if (do_update_status) {
2437 sipe_status_and_note(sipe_private, NULL);
2440 g_free(to);
2441 g_free(activity_token);
2445 * for Access levels menu
2447 #define INDENT_FMT " %s"
2450 * Member is indirectly belong to access level container.
2451 * For example 'sameEnterprise' is in the container and user
2452 * belongs to that same enterprise.
2454 #define INDENT_MARKED_INHERITED_FMT "= %s"
2456 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2457 struct sipe_backend_buddy_menu *menu,
2458 const gchar *member_type,
2459 const gchar *member_value,
2460 const gboolean extra_menu)
2462 unsigned int i;
2463 gboolean is_group_access = FALSE;
2464 int container_id;
2466 if (!menu)
2467 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2469 container_id = sipe_ocs2007_find_access_level(sipe_private,
2470 member_type,
2471 member_value,
2472 &is_group_access);
2474 for (i = 1; i <= CONTAINERS_LEN; i++) {
2476 * Blocked should remain in the first place
2477 * in the containers[] array.
2479 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2480 int container_j = containers[j];
2481 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2482 struct sipe_container *container = create_container(j,
2483 member_type,
2484 member_value,
2485 FALSE);
2486 gchar *label;
2488 /* libpurple memory leak workaround */
2489 blist_menu_remember_container(sipe_private, container);
2491 /* current container/access level */
2492 if (container_j == container_id) {
2493 label = is_group_access ?
2494 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2495 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2496 } else {
2497 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2500 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2501 menu,
2502 label,
2503 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2504 container);
2505 g_free(label);
2508 if (extra_menu && (container_id >= 0) && !is_group_access) {
2509 struct sipe_container *container = create_container(0,
2510 member_type,
2511 member_value,
2512 TRUE);
2513 gchar *label;
2515 /* separator */
2516 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2517 menu,
2518 " --------------");
2521 /* libpurple memory leak workaround */
2522 blist_menu_remember_container(sipe_private, container);
2524 /* Translators: remove (clear) previously assigned access level */
2525 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2526 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2527 menu,
2528 label,
2529 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2530 container);
2531 g_free(label);
2534 return(menu);
2537 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2539 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2540 GSList *access_domains, *entry;
2542 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2543 menu,
2544 _("People in my company"),
2545 access_levels_menu(sipe_private,
2546 NULL,
2547 "sameEnterprise",
2548 NULL,
2549 FALSE));
2551 /* this is original name, don't edit */
2552 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2553 menu,
2554 _("People in domains connected with my company"),
2555 access_levels_menu(sipe_private,
2556 NULL,
2557 "federated",
2558 NULL,
2559 FALSE));
2561 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2562 menu,
2563 _("People in public domains"),
2564 access_levels_menu(sipe_private,
2565 NULL,
2566 "publicCloud",
2567 NULL,
2568 TRUE));
2570 entry = access_domains = get_access_domains(sipe_private);
2571 while (entry) {
2572 gchar *domain = entry->data;
2573 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2575 /* takes over ownership of entry->data (= domain) */
2576 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2577 menu,
2578 menu_name,
2579 access_levels_menu(sipe_private,
2580 NULL,
2581 "domain",
2582 domain,
2583 TRUE));
2584 g_free(menu_name);
2586 entry = entry->next;
2588 g_slist_free(access_domains);
2590 /* separator */
2591 /* People in domains connected with my company */
2592 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2593 menu,
2594 "-------------------------------------------");
2596 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2597 menu,
2598 _("Add new domain..."),
2599 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2600 NULL);
2602 return(menu);
2605 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2606 const gchar *buddy_name)
2608 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2609 gchar *label;
2612 * Workaround for missing libpurple API to release resources allocated
2613 * during blist_node_menu() callback. See also:
2615 * <http://developer.pidgin.im/ticket/12597>
2617 * We remember all memory blocks in a list and deallocate them when
2619 * - the next time we enter the callback, or
2620 * - the account is disconnected
2622 * That means that after the buddy menu has been closed we have unused
2623 * resources but at least we don't leak them anymore...
2625 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2627 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2628 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2629 menu,
2630 label,
2631 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2632 NULL);
2633 g_free(label);
2635 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2636 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2637 menu,
2638 label,
2639 access_groups_menu(sipe_private));
2640 g_free(label);
2642 menu = access_levels_menu(sipe_private,
2643 menu,
2644 "user",
2645 sipe_get_no_sip_uri(buddy_name),
2646 TRUE);
2648 return(menu);
2652 Local Variables:
2653 mode: c
2654 c-file-style: "bsd"
2655 indent-tabs-mode: t
2656 tab-width: 8
2657 End: