media: set "In a call" status when media call is in progress
[siplcs.git] / src / core / sipe-ocs2007.c
blob71b7d23f2e1f14be11f6706ab73ca4c05afb96bc
1 /**
2 * @file sipe-ocs2007.c
4 * pidgin-sipe
6 * Copyright (C) 2011 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-nls.h"
50 #include "sipe-ocs2007.h"
51 #include "sipe-schedule.h"
52 #include "sipe-status.h"
53 #include "sipe-utils.h"
54 #include "sipe-xml.h"
56 /** MS-PRES publication */
57 struct sipe_publication {
58 gchar *category;
59 guint instance;
60 guint container;
61 guint version;
62 /** for 'state' category */
63 int availability;
64 /** for 'state:calendarState' category */
65 char *cal_event_hash;
66 /** for 'note' category */
67 gchar *note;
68 /** for 'calendarData' category; 300(Team) container */
69 char *working_hours_xml_str;
70 char *fb_start_str;
71 char *free_busy_base64;
74 /**
75 * 2007-style Activity and Availability.
77 * [MS-PRES] 3.7.5.5
79 * Conversion of legacyInterop availability ranges and activity tokens into
80 * SIPE activity tokens. The descriptions of availability ranges are defined at:
82 * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
84 * The values define the starting point of a range.
86 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE 3000
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 6000
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE 7500
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 15000
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
95 #define SIPE_OCS2007_ACTIVITY_ON_PHONE "on-the-phone"
97 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
98 const gchar *activity)
100 guint type;
102 if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
103 type = SIPE_ACTIVITY_OFFLINE;
104 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
105 type = SIPE_ACTIVITY_AVAILABLE;
106 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
107 type = SIPE_ACTIVITY_INACTIVE;
108 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
109 if (sipe_strequal(activity, SIPE_OCS2007_ACTIVITY_ON_PHONE)) {
110 type = SIPE_ACTIVITY_ON_PHONE;
111 } else {
112 type = SIPE_ACTIVITY_BUSY;
114 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
115 type = SIPE_ACTIVITY_BUSYIDLE;
116 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
117 type = SIPE_ACTIVITY_DND;
118 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
119 type = SIPE_ACTIVITY_BRB;
120 } else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
121 type = SIPE_ACTIVITY_AWAY;
122 } else {
123 type = SIPE_ACTIVITY_OFFLINE;
126 return sipe_status_activity_to_token(type);
129 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
131 if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
132 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
133 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
134 } else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
135 (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
136 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
137 } else {
138 return(NULL);
143 * @param sipe_status_id (in)
144 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
145 * requests this token]
147 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
148 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
149 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
150 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
151 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
152 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
153 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
154 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
155 const gchar **activity_token)
157 guint availability;
158 guint activity;
160 if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
161 availability = SIPE_OCS2007_AVAILABILITY_AWAY;
162 activity = SIPE_ACTIVITY_AWAY;
163 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
164 availability = SIPE_OCS2007_AVAILABILITY_BRB;
165 activity = SIPE_ACTIVITY_BRB;
166 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
167 availability = SIPE_OCS2007_AVAILABILITY_DND;
168 activity = SIPE_ACTIVITY_DND;
169 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
170 availability = SIPE_OCS2007_AVAILABILITY_BUSY;
171 activity = SIPE_ACTIVITY_BUSY;
172 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
173 availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
174 activity = SIPE_ACTIVITY_ONLINE;
175 } else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
176 availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
177 activity = SIPE_ACTIVITY_UNSET;
178 } else {
179 /* Offline or invisible */
180 availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
181 activity = SIPE_ACTIVITY_OFFLINE;
184 if (activity_token) {
185 *activity_token = sipe_status_activity_to_token(activity);
188 return(availability);
191 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
193 return(SIPE_OCS2007_AVAILABILITY_BUSY >=
194 sipe_ocs2007_availability_from_status(status_id, NULL));
198 gboolean sipe_ocs2007_availability_is_away(guint availability)
200 return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
203 static void send_presence_publish(struct sipe_core_private *sipe_private,
204 const char *publications);
206 static void free_publication(struct sipe_publication *publication)
208 g_free(publication->category);
209 g_free(publication->cal_event_hash);
210 g_free(publication->note);
212 g_free(publication->working_hours_xml_str);
213 g_free(publication->fb_start_str);
214 g_free(publication->free_busy_base64);
216 g_free(publication);
219 struct hash_table_delete_payload {
220 GHashTable *hash_table;
221 guint container;
224 static void sipe_remove_category_container_publications_cb(const gchar *name,
225 struct sipe_publication *publication,
226 struct hash_table_delete_payload *payload)
228 if (publication->container == payload->container) {
229 g_hash_table_remove(payload->hash_table, name);
233 static void sipe_remove_category_container_publications(GHashTable *our_publications,
234 const gchar *category,
235 guint container)
237 struct hash_table_delete_payload payload;
238 payload.hash_table = g_hash_table_lookup(our_publications, category);
240 if (!payload.hash_table) return;
242 payload.container = container;
243 g_hash_table_foreach(payload.hash_table,
244 (GHFunc)sipe_remove_category_container_publications_cb,
245 &payload);
248 /** MS-PRES container */
249 struct sipe_container {
250 guint id;
251 guint version;
252 GSList *members;
255 /** MS-PRES container member */
256 struct sipe_container_member {
257 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
258 gchar *type;
259 gchar *value;
262 static const guint containers[] = {32000, 400, 300, 200, 100};
263 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
265 static void free_container_member(struct sipe_container_member *member)
267 if (!member) return;
269 g_free(member->type);
270 g_free(member->value);
271 g_free(member);
274 static void sipe_ocs2007_free_container(struct sipe_container *container)
276 GSList *entry;
278 if (!container) return;
280 entry = container->members;
281 while (entry) {
282 void *data = entry->data;
283 entry = g_slist_remove(entry, data);
284 free_container_member((struct sipe_container_member *)data);
286 g_free(container);
289 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
291 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
292 GSList *entry = sipe_private->blist_menu_containers;
293 while (entry) {
294 sipe_ocs2007_free_container(entry->data);
295 entry = entry->next;
297 g_slist_free(sipe_private->blist_menu_containers);
298 sipe_private->blist_menu_containers = NULL;
301 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
302 struct sipe_container *container)
304 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
305 container);
308 static struct sipe_container *create_container(guint index,
309 const gchar *member_type,
310 const gchar *member_value,
311 gboolean is_group)
313 struct sipe_container *container = g_new0(struct sipe_container, 1);
314 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
316 container->id = is_group ? (guint) -1 : containers[index];
317 container->members = g_slist_append(container->members, member);
318 member->type = g_strdup(member_type);
319 member->value = g_strdup(member_value);
321 return(container);
324 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
326 if (sipe_private->containers) {
327 GSList *entry = sipe_private->containers;
328 while (entry) {
329 sipe_ocs2007_free_container((struct sipe_container *)entry->data);
330 entry = entry->next;
333 g_slist_free(sipe_private->containers);
337 * Finds locally stored MS-PRES container member
339 static struct sipe_container_member *
340 sipe_find_container_member(struct sipe_container *container,
341 const gchar *type,
342 const gchar *value)
344 struct sipe_container_member *member;
345 GSList *entry;
347 if (container == NULL || type == NULL) {
348 return NULL;
351 entry = container->members;
352 while (entry) {
353 member = entry->data;
354 if (sipe_strcase_equal(member->type, type) &&
355 sipe_strcase_equal(member->value, value))
357 return member;
359 entry = entry->next;
361 return NULL;
365 * Finds locally stored MS-PRES container by id
367 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
368 guint id)
370 GSList *entry = sipe_private->containers;
371 while (entry) {
372 struct sipe_container *container = entry->data;
373 if (id == container->id) {
374 return container;
376 entry = entry->next;
378 return NULL;
381 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
382 const gchar *type,
383 const gchar *value)
385 unsigned int i = 0;
386 const gchar *value_mod = value;
388 if (!type) return -1;
390 if (sipe_strequal("user", type)) {
391 value_mod = sipe_get_no_sip_uri(value);
394 for (i = 0; i < CONTAINERS_LEN; i++) {
395 struct sipe_container_member *member;
396 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
397 if (!container) continue;
399 member = sipe_find_container_member(container, type, value_mod);
400 if (member) return containers[i];
403 return -1;
407 * Returns pointer to domain part in provided Email URL
409 * @param email an email URL. Example: first.last@hq.company.com
410 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
412 * Doesn't allocate memory
414 static const gchar *sipe_get_domain(const gchar *email)
416 gchar *tmp;
418 if (!email) return NULL;
420 tmp = strstr(email, "@");
422 if (tmp && ((tmp+1) < (email + strlen(email)))) {
423 return tmp+1;
424 } else {
425 return NULL;
429 /* @TODO: replace with binary search for faster access? */
430 /** source: http://support.microsoft.com/kb/897567 */
431 static const gchar * const public_domains[] = {
432 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
433 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
434 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
435 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
436 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
437 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
438 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
439 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
440 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
441 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
442 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
443 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
444 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
445 "yahoo.com",
446 NULL};
448 static gboolean sipe_is_public_domain(const gchar *domain)
450 int i = 0;
451 while (public_domains[i]) {
452 if (sipe_strcase_equal(public_domains[i], domain)) {
453 return TRUE;
455 i++;
457 return FALSE;
461 * Access Levels
462 * 32000 - Blocked
463 * 400 - Personal
464 * 300 - Team
465 * 200 - Company
466 * 100 - Public
468 const gchar *sipe_ocs2007_access_level_name(guint id)
470 switch (id) {
471 case 32000: return _("Blocked");
472 case 400: return _("Personal");
473 case 300: return _("Team");
474 case 200: return _("Company");
475 case 100: return _("Public");
477 return _("Unknown");
480 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
481 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
482 const gchar *type,
483 const gchar *value,
484 gboolean *is_group_access)
486 int container_id = -1;
488 if (sipe_strequal("user", type)) {
489 const char *domain;
490 const char *no_sip_uri = sipe_get_no_sip_uri(value);
492 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
493 if (container_id >= 0) {
494 if (is_group_access) *is_group_access = FALSE;
495 return container_id;
498 domain = sipe_get_domain(no_sip_uri);
499 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
500 if (container_id >= 0) {
501 if (is_group_access) *is_group_access = TRUE;
502 return container_id;
505 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
506 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
507 if (is_group_access) *is_group_access = TRUE;
508 return container_id;
511 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
512 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
513 if (is_group_access) *is_group_access = TRUE;
514 return container_id;
517 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
518 if ((container_id >= 0)) {
519 if (is_group_access) *is_group_access = TRUE;
520 return container_id;
522 } else {
523 container_id = sipe_find_member_access_level(sipe_private, type, value);
524 if (is_group_access) *is_group_access = FALSE;
527 return container_id;
530 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
532 struct sipe_container *container;
533 struct sipe_container_member *member;
534 GSList *entry;
535 GSList *entry2;
536 GSList *res = NULL;
538 entry = sipe_private->containers;
539 while (entry) {
540 container = entry->data;
542 entry2 = container->members;
543 while (entry2) {
544 member = entry2->data;
545 if (sipe_strcase_equal(member->type, "domain"))
547 res = slist_insert_unique_sorted(res, g_strdup(member->value), (GCompareFunc)g_ascii_strcasecmp);
549 entry2 = entry2->next;
551 entry = entry->next;
553 return res;
556 static void sipe_send_container_members_prepare(const guint container_id,
557 const guint container_version,
558 const gchar *action,
559 const gchar *type,
560 const gchar *value,
561 char **container_xmls)
563 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
564 gchar *body;
566 if (!container_xmls) return;
568 body = g_strdup_printf(
569 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
570 container_id,
571 container_version,
572 action,
573 type,
574 value_str);
575 g_free(value_str);
577 if ((*container_xmls) == NULL) {
578 *container_xmls = body;
579 } else {
580 char *tmp = *container_xmls;
582 *container_xmls = g_strconcat(*container_xmls, body, NULL);
583 g_free(tmp);
584 g_free(body);
588 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
589 char *container_xmls)
591 gchar *self;
592 gchar *contact;
593 gchar *hdr;
594 gchar *body;
596 if (!container_xmls) return;
598 self = sip_uri_self(sipe_private);
599 body = g_strdup_printf(
600 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
601 "%s"
602 "</setContainerMembers>",
603 container_xmls);
605 contact = get_contact(sipe_private);
606 hdr = g_strdup_printf("Contact: %s\r\n"
607 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
608 g_free(contact);
610 sip_transport_service(sipe_private,
611 self,
612 hdr,
613 body,
614 NULL);
616 g_free(hdr);
617 g_free(body);
618 g_free(self);
622 * @param container_id a new access level. If -1 then current access level
623 * is just removed (I.e. the member is removed from all containers).
624 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
625 * @param value a value for member. E.g. SIP URI for "user" member type.
627 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
628 const int container_id,
629 const gchar *type,
630 const gchar *value)
632 unsigned int i;
633 int current_container_id = -1;
634 char *container_xmls = NULL;
636 /* for each container: find/delete */
637 for (i = 0; i < CONTAINERS_LEN; i++) {
638 struct sipe_container_member *member;
639 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
641 if (!container) continue;
643 member = sipe_find_container_member(container, type, value);
644 if (member) {
645 current_container_id = containers[i];
646 /* delete/publish current access level */
647 if (container_id < 0 || container_id != current_container_id) {
648 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
649 /* remove member from our cache, to be able to recalculate AL below */
650 container->members = g_slist_remove(container->members, member);
651 current_container_id = -1;
656 /* recalculate AL below */
657 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
659 /* assign/publish new access level */
660 if (container_id != current_container_id && container_id >= 0) {
661 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
662 guint version = container ? container->version : 0;
664 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
667 if (container_xmls) {
668 sipe_send_set_container_members(sipe_private, container_xmls);
670 g_free(container_xmls);
673 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
674 gpointer parameter)
676 struct sipe_container *container = parameter;
677 struct sipe_container_member *member;
679 if (!container || !container->members) return;
681 member = ((struct sipe_container_member *)container->members->data);
683 if (!member->type) return;
685 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
686 container->id, member->type, member->value ? member->value : "");
688 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
689 container->id,
690 member->type,
691 member->value);
695 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
696 const gchar *domain,
697 guint index)
699 /* move Blocked first */
700 guint i = (index == 4) ? 0 : index + 1;
701 guint container_id = containers[i];
703 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
704 domain ? domain : "", index, container_id);
706 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
707 container_id,
708 "domain",
709 domain);
713 * Schedules process of self status publish
714 * based on own calendar information.
715 * Should be scheduled to the beginning of every
716 * 15 min interval, like:
717 * 13:00, 13:15, 13:30, 13:45, etc.
720 static void schedule_publish_update(struct sipe_core_private *sipe_private,
721 time_t calculate_from)
723 int interval = 5*60;
724 /** start of the beginning of closest 5 min interval. */
725 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
727 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
728 asctime(localtime(&calculate_from)));
729 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
730 asctime(localtime(&next_start)));
732 sipe_schedule_seconds(sipe_private,
733 "<+2007-cal-status>",
734 NULL,
735 next_start - time(NULL),
736 sipe_ocs2007_presence_publish,
737 NULL);
741 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
742 * @param availability (%d) Ex.: 6500
744 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
745 "<availability>%d</availability>"
747 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
748 * @param token (%s) Ex.: in-a-meeting
749 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
750 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
752 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
753 "<activity token=\"%s\" %s %s></activity>"
755 * Publishes 'calendarState' category.
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 * @param instance (%u) Ex.: 1339299275
766 * @param version (%u) Ex.: 1
767 * @param uri (%s) Ex.: john@contoso.com
768 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
769 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
770 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
771 * @param meeting_subject (%s) Ex.: Customer Meeting
772 * @param meeting_location (%s) Ex.: Conf Room 100
774 #define SIPE_PUB_XML_STATE_CALENDAR \
775 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" 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>"\
784 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
785 "<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\">"\
786 "%s"\
787 "%s"\
788 "<endpointLocation/>"\
789 "<meetingSubject>%s</meetingSubject>"\
790 "<meetingLocation>%s</meetingLocation>"\
791 "</state>"\
792 "</publication>"
794 * Publishes to clear 'calendarState' and 'phoneState' category
795 * @param instance (%u) Ex.: 1251210982
796 * @param version (%u) Ex.: 1
798 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
799 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
800 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
803 * Publishes to clear any category
804 * @param category_name (%s) Ex.: state
805 * @param instance (%u) Ex.: 536870912
806 * @param container (%u) Ex.: 3
807 * @param version (%u) Ex.: 1
808 * @param expireType (%s) Ex.: static
810 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
811 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
814 * Publishes 'note' category.
815 * @param instance (%u) Ex.: 2135971629; 0 for personal
816 * @param container (%u) Ex.: 200
817 * @param version (%u) Ex.: 2
818 * @param type (%s) Ex.: personal or OOF
819 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
820 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
821 * @param body (%s) Ex.: In the office
823 #define SIPE_PUB_XML_NOTE \
824 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
825 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
826 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
827 "</note>"\
828 "</publication>"
830 * Publishes 'phoneState' category.
831 * @param instance (%u) Ex.: 1339299275
832 * @param version (%u) Ex.: 1
834 * @param instance (%u) Ex.: 1339299275
835 * @param version (%u) Ex.: 1
837 #define SIPE_PUB_XML_STATE_PHONE \
838 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
839 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
840 "<availability>6500</availability>"\
841 "<activity token=\"on-the-phone\" minAvailability=\"6500\" maxAvailability=\"8999\"/>"\
842 "</state>"\
843 "</publication>"\
844 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
845 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
846 "<availability>6500</availability>"\
847 "<activity token=\"on-the-phone\" minAvailability=\"6500\" maxAvailability=\"8999\"/>"\
848 "</state>"\
849 "</publication>"
852 * Only Busy and OOF calendar event are published.
853 * Different instances are used for that.
855 * Must be g_free'd after use.
857 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
858 struct sipe_cal_event *event,
859 const char *uri,
860 int cal_satus)
862 gchar *start_time_str;
863 int availability = 0;
864 gchar *res;
865 gchar *tmp = NULL;
866 guint instance = (cal_satus == SIPE_CAL_OOF) ?
867 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
868 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
870 /* key is <category><instance><container> */
871 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
872 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
873 struct sipe_publication *publication_2 =
874 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
875 struct sipe_publication *publication_3 =
876 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
878 g_free(key_2);
879 g_free(key_3);
881 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
882 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
883 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
884 return NULL;
887 if (event &&
888 publication_3 &&
889 (publication_3->availability == availability) &&
890 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
892 g_free(tmp);
893 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
894 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
895 return NULL; /* nothing to update */
897 g_free(tmp);
899 if (event &&
900 (event->cal_status == SIPE_CAL_BUSY ||
901 event->cal_status == SIPE_CAL_OOF))
903 gchar *availability_xml_str = NULL;
904 gchar *activity_xml_str = NULL;
905 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
906 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
908 if (event->cal_status == SIPE_CAL_BUSY) {
909 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
910 SIPE_OCS2007_AVAILABILITY_BUSY);
913 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
914 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
915 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
916 "minAvailability=\"6500\"",
917 "maxAvailability=\"8999\"");
918 } else if (event->cal_status == SIPE_CAL_OOF) {
919 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
920 sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
921 "minAvailability=\"12000\"",
922 "");
924 start_time_str = sipe_utils_time_to_str(event->start_time);
926 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
927 instance,
928 publication_2 ? publication_2->version : 0,
929 uri,
930 start_time_str,
931 availability_xml_str ? availability_xml_str : "",
932 activity_xml_str ? activity_xml_str : "",
933 escaped_subject ? escaped_subject : "",
934 escaped_location ? escaped_location : "",
936 instance,
937 publication_3 ? publication_3->version : 0,
938 uri,
939 start_time_str,
940 availability_xml_str ? availability_xml_str : "",
941 activity_xml_str ? activity_xml_str : "",
942 escaped_subject ? escaped_subject : "",
943 escaped_location ? escaped_location : ""
945 g_free(escaped_location);
946 g_free(escaped_subject);
947 g_free(start_time_str);
948 g_free(availability_xml_str);
949 g_free(activity_xml_str);
952 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
954 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
955 instance,
956 publication_2 ? publication_2->version : 0,
958 instance,
959 publication_3 ? publication_3->version : 0
963 return res;
967 * Returns 'note' XML part for publication.
968 * Must be g_free'd after use.
970 * Protocol format for Note is plain text.
972 * @param note a note in Sipe internal HTML format
973 * @param note_type either personal or OOF
975 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
976 const char *note, /* html */
977 const char *note_type,
978 time_t note_start,
979 time_t note_end)
981 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
982 /* key is <category><instance><container> */
983 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
984 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
985 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
987 struct sipe_publication *publication_note_200 =
988 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_200);
989 struct sipe_publication *publication_note_300 =
990 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_300);
991 struct sipe_publication *publication_note_400 =
992 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "note"), key_note_400);
994 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
995 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
996 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
997 char *res, *tmp1, *tmp2, *tmp3;
998 char *start_time_attr;
999 char *end_time_attr;
1001 g_free(tmp);
1002 tmp = NULL;
1003 g_free(key_note_200);
1004 g_free(key_note_300);
1005 g_free(key_note_400);
1007 /* we even need to republish empty note */
1008 if (sipe_strequal(n1, n2))
1010 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1011 g_free(n1);
1012 return NULL; /* nothing to update */
1015 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1016 g_free(tmp);
1017 tmp = NULL;
1018 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1019 g_free(tmp);
1021 if (n1) {
1022 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1023 instance,
1024 200,
1025 publication_note_200 ? publication_note_200->version : 0,
1026 note_type,
1027 start_time_attr ? start_time_attr : "",
1028 end_time_attr ? end_time_attr : "",
1029 n1);
1031 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1032 instance,
1033 300,
1034 publication_note_300 ? publication_note_300->version : 0,
1035 note_type,
1036 start_time_attr ? start_time_attr : "",
1037 end_time_attr ? end_time_attr : "",
1038 n1);
1040 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1041 instance,
1042 400,
1043 publication_note_400 ? publication_note_400->version : 0,
1044 note_type,
1045 start_time_attr ? start_time_attr : "",
1046 end_time_attr ? end_time_attr : "",
1047 n1);
1048 } else {
1049 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1050 "note",
1051 instance,
1052 200,
1053 publication_note_200 ? publication_note_200->version : 0,
1054 "static");
1055 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1056 "note",
1057 instance,
1058 300,
1059 publication_note_200 ? publication_note_200->version : 0,
1060 "static");
1061 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1062 "note",
1063 instance,
1064 400,
1065 publication_note_200 ? publication_note_200->version : 0,
1066 "static");
1068 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
1070 g_free(start_time_attr);
1071 g_free(end_time_attr);
1072 g_free(tmp1);
1073 g_free(tmp2);
1074 g_free(tmp3);
1075 g_free(n1);
1077 return res;
1081 * Publishes 'calendarData' category's WorkingHours.
1083 * @param version (%u) Ex.: 1
1084 * @param email (%s) Ex.: alice@cosmo.local
1085 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1087 * @param version (%u)
1089 * @param version (%u)
1090 * @param email (%s)
1091 * @param working_hours_xml_str (%s)
1093 * @param version (%u)
1094 * @param email (%s)
1095 * @param working_hours_xml_str (%s)
1097 * @param version (%u)
1098 * @param email (%s)
1099 * @param working_hours_xml_str (%s)
1101 * @param version (%u)
1103 #define SIPE_PUB_XML_WORKING_HOURS \
1104 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1105 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1106 "</calendarData>"\
1107 "</publication>"\
1108 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1109 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1110 "</publication>"\
1111 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1112 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1113 "</calendarData>"\
1114 "</publication>"\
1115 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1116 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1117 "</calendarData>"\
1118 "</publication>"\
1119 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1120 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1121 "</calendarData>"\
1122 "</publication>"\
1123 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1124 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1125 "</publication>"
1128 * Returns 'calendarData' XML part with WorkingHours for publication.
1129 * Must be g_free'd after use.
1131 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1133 struct sipe_calendar* cal = sipe_private->calendar;
1135 /* key is <category><instance><container> */
1136 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1137 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1138 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1139 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1140 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1141 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1143 struct sipe_publication *publication_cal_1 =
1144 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1145 struct sipe_publication *publication_cal_100 =
1146 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1147 struct sipe_publication *publication_cal_200 =
1148 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1149 struct sipe_publication *publication_cal_300 =
1150 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1151 struct sipe_publication *publication_cal_400 =
1152 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1153 struct sipe_publication *publication_cal_32000 =
1154 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1156 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1157 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1159 g_free(key_cal_1);
1160 g_free(key_cal_100);
1161 g_free(key_cal_200);
1162 g_free(key_cal_300);
1163 g_free(key_cal_400);
1164 g_free(key_cal_32000);
1166 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1167 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1168 return NULL;
1171 if (sipe_strequal(n1, n2))
1173 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1174 return NULL; /* nothing to update */
1177 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1178 /* 1 */
1179 publication_cal_1 ? publication_cal_1->version : 0,
1180 cal->email,
1181 cal->working_hours_xml_str,
1182 /* 100 - Public */
1183 publication_cal_100 ? publication_cal_100->version : 0,
1184 /* 200 - Company */
1185 publication_cal_200 ? publication_cal_200->version : 0,
1186 cal->email,
1187 cal->working_hours_xml_str,
1188 /* 300 - Team */
1189 publication_cal_300 ? publication_cal_300->version : 0,
1190 cal->email,
1191 cal->working_hours_xml_str,
1192 /* 400 - Personal */
1193 publication_cal_400 ? publication_cal_400->version : 0,
1194 cal->email,
1195 cal->working_hours_xml_str,
1196 /* 32000 - Blocked */
1197 publication_cal_32000 ? publication_cal_32000->version : 0
1202 * Publishes 'calendarData' category's FreeBusy.
1204 * @param instance (%u) Ex.: 1300372959
1205 * @param version (%u) Ex.: 1
1207 * @param instance (%u) Ex.: 1300372959
1208 * @param version (%u) Ex.: 1
1210 * @param instance (%u) Ex.: 1300372959
1211 * @param version (%u) Ex.: 1
1212 * @param email (%s) Ex.: alice@cosmo.local
1213 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1214 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1216 * @param instance (%u) Ex.: 1300372959
1217 * @param version (%u) Ex.: 1
1218 * @param email (%s) Ex.: alice@cosmo.local
1219 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1220 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1222 * @param instance (%u) Ex.: 1300372959
1223 * @param version (%u) Ex.: 1
1224 * @param email (%s) Ex.: alice@cosmo.local
1225 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1226 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1228 * @param instance (%u) Ex.: 1300372959
1229 * @param version (%u) Ex.: 1
1231 #define SIPE_PUB_XML_FREE_BUSY \
1232 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1233 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1234 "</publication>"\
1235 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1236 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1237 "</publication>"\
1238 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1239 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1240 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1241 "</calendarData>"\
1242 "</publication>"\
1243 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1244 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1245 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1246 "</calendarData>"\
1247 "</publication>"\
1248 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1249 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1250 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1251 "</calendarData>"\
1252 "</publication>"\
1253 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1254 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1255 "</publication>"
1258 * Returns 'calendarData' XML part with FreeBusy for publication.
1259 * Must be g_free'd after use.
1261 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1263 struct sipe_calendar* cal = sipe_private->calendar;
1264 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1265 char *fb_start_str;
1266 char *free_busy_base64;
1267 /* const char *st; */
1268 /* const char *fb; */
1269 char *res;
1271 /* key is <category><instance><container> */
1272 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1273 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1274 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1275 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1276 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1277 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1279 struct sipe_publication *publication_cal_1 =
1280 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_1);
1281 struct sipe_publication *publication_cal_100 =
1282 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_100);
1283 struct sipe_publication *publication_cal_200 =
1284 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_200);
1285 struct sipe_publication *publication_cal_300 =
1286 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_300);
1287 struct sipe_publication *publication_cal_400 =
1288 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_400);
1289 struct sipe_publication *publication_cal_32000 =
1290 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "calendarData"), key_cal_32000);
1292 g_free(key_cal_1);
1293 g_free(key_cal_100);
1294 g_free(key_cal_200);
1295 g_free(key_cal_300);
1296 g_free(key_cal_400);
1297 g_free(key_cal_32000);
1299 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1300 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1301 return NULL;
1304 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1305 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1307 /* we will rebuplish the same data to refresh publication time,
1308 * so if data from multiple sources, most recent will be choosen
1310 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1311 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1313 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1315 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1316 // g_free(fb_start_str);
1317 // g_free(free_busy_base64);
1318 // return NULL; /* nothing to update */
1321 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1322 /* 1 */
1323 cal_data_instance,
1324 publication_cal_1 ? publication_cal_1->version : 0,
1325 /* 100 - Public */
1326 cal_data_instance,
1327 publication_cal_100 ? publication_cal_100->version : 0,
1328 /* 200 - Company */
1329 cal_data_instance,
1330 publication_cal_200 ? publication_cal_200->version : 0,
1331 cal->email,
1332 fb_start_str,
1333 free_busy_base64,
1334 /* 300 - Team */
1335 cal_data_instance,
1336 publication_cal_300 ? publication_cal_300->version : 0,
1337 cal->email,
1338 fb_start_str,
1339 free_busy_base64,
1340 /* 400 - Personal */
1341 cal_data_instance,
1342 publication_cal_400 ? publication_cal_400->version : 0,
1343 cal->email,
1344 fb_start_str,
1345 free_busy_base64,
1346 /* 32000 - Blocked */
1347 cal_data_instance,
1348 publication_cal_32000 ? publication_cal_32000->version : 0
1351 g_free(fb_start_str);
1352 g_free(free_busy_base64);
1353 return res;
1358 * Publishes 'device' category.
1359 * @param instance (%u) Ex.: 1938468728
1360 * @param version (%u) Ex.: 1
1361 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1362 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1363 * @param timezone (%s) Ex.: 00:00:00+01:00
1364 * @param machineName (%s) Ex.: BOSTON-OCS07
1366 #define SIPE_PUB_XML_DEVICE \
1367 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1368 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1369 "<capabilities preferred=\"false\" uri=\"%s\">"\
1370 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1371 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1372 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1373 "</capabilities>"\
1374 "<timezone>%s</timezone>"\
1375 "<machineName>%s</machineName>"\
1376 "</device>"\
1377 "</publication>"
1380 * Returns 'device' XML part for publication.
1381 * Must be g_free'd after use.
1383 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1385 gchar *uri;
1386 gchar *doc;
1387 gchar *uuid = get_uuid(sipe_private);
1388 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1389 /* key is <category><instance><container> */
1390 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1391 struct sipe_publication *publication =
1392 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "device"), key);
1394 g_free(key);
1396 uri = sip_uri_self(sipe_private);
1397 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1398 device_instance,
1399 publication ? publication->version : 0,
1400 uuid,
1401 uri,
1402 "00:00:00+01:00", /* @TODO make timezone real*/
1403 g_get_host_name()
1406 g_free(uri);
1407 g_free(uuid);
1409 return doc;
1413 * Publishes 'machineState' category.
1414 * @param instance (%u) Ex.: 926460663
1415 * @param version (%u) Ex.: 22
1416 * @param availability (%d) Ex.: 3500
1417 * @param instance (%u) Ex.: 926460663
1418 * @param version (%u) Ex.: 22
1419 * @param availability (%d) Ex.: 3500
1421 #define SIPE_PUB_XML_STATE_MACHINE \
1422 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1423 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1424 "<availability>%d</availability>"\
1425 "<endpointLocation/>"\
1426 "</state>"\
1427 "</publication>"\
1428 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1429 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1430 "<availability>%d</availability>"\
1431 "<endpointLocation/>"\
1432 "</state>"\
1433 "</publication>"
1436 * Publishes 'userState' category.
1437 * @param instance (%u) User. Ex.: 536870912
1438 * @param version (%u) User Container 2. Ex.: 22
1439 * @param availability (%d) User Container 2. Ex.: 15500
1440 * @param instance (%u) User. Ex.: 536870912
1441 * @param version (%u) User Container 3.Ex.: 22
1442 * @param availability (%d) User Container 3. Ex.: 15500
1444 #define SIPE_PUB_XML_STATE_USER \
1445 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1446 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1447 "<availability>%d</availability>"\
1448 "<endpointLocation/>"\
1449 "</state>"\
1450 "</publication>"\
1451 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1452 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1453 "<availability>%d</availability>"\
1454 "<endpointLocation/>"\
1455 "</state>"\
1456 "</publication>"
1459 * A service method - use
1460 * - send_publish_get_category_state_machine and
1461 * - send_publish_get_category_state_user instead.
1462 * Must be g_free'd after use.
1464 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1465 gboolean is_user_state)
1467 int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1468 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1469 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1470 /* key is <category><instance><container> */
1471 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1472 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1473 struct sipe_publication *publication_2 =
1474 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1475 struct sipe_publication *publication_3 =
1476 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1478 g_free(key_2);
1479 g_free(key_3);
1481 if (publication_2 && (publication_2->availability == availability))
1483 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1484 return NULL; /* nothing to update */
1487 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1488 instance,
1489 publication_2 ? publication_2->version : 0,
1490 availability,
1491 instance,
1492 publication_3 ? publication_3->version : 0,
1493 availability);
1497 * Returns 'machineState' XML part for publication.
1498 * Must be g_free'd after use.
1500 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
1502 return sipe_publish_get_category_state(sipe_private, FALSE);
1506 * Returns 'userState' XML part for publication.
1507 * Must be g_free'd after use.
1509 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
1511 return sipe_publish_get_category_state(sipe_private, TRUE);
1514 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1516 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1517 gchar *pub_machine;
1518 gchar *publications;
1520 sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
1522 pub_machine = sipe_publish_get_category_state_machine(sipe_private);
1523 publications = g_strdup_printf("%s%s",
1524 pub_device,
1525 pub_machine ? pub_machine : "");
1526 g_free(pub_device);
1527 g_free(pub_machine);
1529 send_presence_publish(sipe_private, publications);
1530 g_free(publications);
1533 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1534 struct sipmsg *msg,
1535 struct transaction *trans)
1537 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1539 if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1540 sipe_xml *xml;
1541 const sipe_xml *node;
1542 gchar *fault_code;
1543 GHashTable *faults;
1544 int index_our;
1545 gboolean has_device_publication = FALSE;
1547 xml = sipe_xml_parse(msg->body, msg->bodylen);
1549 /* test if version mismatch fault */
1550 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1551 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1552 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1553 g_free(fault_code);
1554 sipe_xml_free(xml);
1555 return TRUE;
1557 g_free(fault_code);
1559 /* accumulating information about faulty versions */
1560 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1561 for (node = sipe_xml_child(xml, "details/operation");
1562 node;
1563 node = sipe_xml_twin(node))
1565 const gchar *index = sipe_xml_attribute(node, "index");
1566 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1568 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1569 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1571 sipe_xml_free(xml);
1573 /* here we are parsing our own request to figure out what publication
1574 * referenced here only by index went wrong
1576 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1578 /* publication */
1579 for (node = sipe_xml_child(xml, "publications/publication"),
1580 index_our = 1; /* starts with 1 - our first publication */
1581 node;
1582 node = sipe_xml_twin(node), index_our++)
1584 gchar *idx = g_strdup_printf("%d", index_our);
1585 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1586 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1587 g_free(idx);
1589 if (sipe_strequal("device", categoryName)) {
1590 has_device_publication = TRUE;
1593 if (curVersion) { /* fault exist on this index */
1594 const gchar *container = sipe_xml_attribute(node, "container");
1595 const gchar *instance = sipe_xml_attribute(node, "instance");
1596 /* key is <category><instance><container> */
1597 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1598 GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1600 if (category) {
1601 struct sipe_publication *publication =
1602 g_hash_table_lookup(category, key);
1604 SIPE_DEBUG_INFO("key is %s", key);
1606 if (publication) {
1607 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1608 key, curVersion, publication->version);
1609 /* updating publication's version to the correct one */
1610 publication->version = atoi(curVersion);
1612 } else {
1613 /* We somehow lost this category from our publications... */
1614 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1615 publication->category = g_strdup(categoryName);
1616 publication->instance = atoi(instance);
1617 publication->container = atoi(container);
1618 publication->version = atoi(curVersion);
1619 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1620 g_free, (GDestroyNotify)free_publication);
1621 g_hash_table_insert(category, g_strdup(key), publication);
1622 g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1623 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1625 g_free(key);
1628 sipe_xml_free(xml);
1629 g_hash_table_destroy(faults);
1631 /* rebublishing with right versions */
1632 if (has_device_publication) {
1633 send_publish_category_initial(sipe_private);
1634 } else {
1635 sipe_status_update(sipe_private, NULL);
1638 return TRUE;
1642 * Publishes categories.
1643 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1644 * @param publications (%s) XML publications
1646 #define SIPE_SEND_PRESENCE \
1647 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1648 "<publications uri=\"%s\">"\
1649 "%s"\
1650 "</publications>"\
1651 "</publish>"
1653 static void send_presence_publish(struct sipe_core_private *sipe_private,
1654 const char *publications)
1656 gchar *uri;
1657 gchar *doc;
1658 gchar *tmp;
1659 gchar *hdr;
1661 uri = sip_uri_self(sipe_private);
1662 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1663 uri,
1664 publications);
1666 tmp = get_contact(sipe_private);
1667 hdr = g_strdup_printf("Contact: %s\r\n"
1668 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1670 sip_transport_service(sipe_private,
1671 uri,
1672 hdr,
1673 doc,
1674 process_send_presence_category_publish_response);
1676 g_free(tmp);
1677 g_free(hdr);
1678 g_free(uri);
1679 g_free(doc);
1683 * Publishes self status
1684 * based on own calendar information.
1686 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1687 SIPE_UNUSED_PARAMETER void *unused)
1689 struct sipe_calendar* cal = sipe_private->calendar;
1690 struct sipe_cal_event* event = NULL;
1691 gchar *pub_cal_working_hours = NULL;
1692 gchar *pub_cal_free_busy = NULL;
1693 gchar *pub_calendar = NULL;
1694 gchar *pub_calendar2 = NULL;
1695 gchar *pub_oof_note = NULL;
1696 const gchar *oof_note;
1697 time_t oof_start = 0;
1698 time_t oof_end = 0;
1700 if (!cal) {
1701 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1702 return;
1705 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1706 if (cal->cal_events) {
1707 event = sipe_cal_get_event(cal->cal_events, time(NULL));
1710 if (!event) {
1711 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1712 } else {
1713 char *desc = sipe_cal_event_describe(event);
1714 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
1715 g_free(desc);
1718 /* Logic
1719 if OOF
1720 OOF publish, Busy clean
1721 ilse if Busy
1722 OOF clean, Busy publish
1723 else
1724 OOF clean, Busy clean
1726 if (event && event->cal_status == SIPE_CAL_OOF) {
1727 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1728 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1729 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
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, event, cal->email, SIPE_CAL_BUSY);
1732 } else {
1733 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_OOF);
1734 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, cal->email, SIPE_CAL_BUSY);
1737 oof_note = sipe_ews_get_oof_note(cal);
1738 if (sipe_strequal("Scheduled", cal->oof_state)) {
1739 oof_start = cal->oof_start;
1740 oof_end = cal->oof_end;
1742 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
1744 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1745 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1747 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1748 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1749 } else {
1750 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1751 pub_cal_working_hours ? pub_cal_working_hours : "",
1752 pub_cal_free_busy ? pub_cal_free_busy : "",
1753 pub_calendar ? pub_calendar : "",
1754 pub_calendar2 ? pub_calendar2 : "",
1755 pub_oof_note ? pub_oof_note : "");
1757 send_presence_publish(sipe_private, publications);
1758 g_free(publications);
1761 g_free(pub_cal_working_hours);
1762 g_free(pub_cal_free_busy);
1763 g_free(pub_calendar);
1764 g_free(pub_calendar2);
1765 g_free(pub_oof_note);
1767 /* repeat scheduling */
1768 schedule_publish_update(sipe_private, time(NULL));
1771 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private)
1773 gchar *pub_state = sipe_status_changed_by_user(sipe_private) ?
1774 sipe_publish_get_category_state_user(sipe_private) :
1775 sipe_publish_get_category_state_machine(sipe_private);
1776 gchar *pub_note = sipe_publish_get_category_note(sipe_private,
1777 sipe_private->note,
1778 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1781 gchar *publications;
1783 if (!pub_state && !pub_note) {
1784 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1785 return;
1788 publications = g_strdup_printf("%s%s",
1789 pub_state ? pub_state : "",
1790 pub_note ? pub_note : "");
1792 g_free(pub_state);
1793 g_free(pub_note);
1795 send_presence_publish(sipe_private, publications);
1796 g_free(publications);
1799 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1801 gchar *publications = NULL;
1802 guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1804 /* key is <category><instance><container> */
1805 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1806 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1807 struct sipe_publication *publication_2 =
1808 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
1809 struct sipe_publication *publication_3 =
1810 g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
1811 g_free(key_2);
1812 g_free(key_3);
1814 publications = g_strdup_printf((sipe_private->media_call) ?
1815 SIPE_PUB_XML_STATE_PHONE : SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1816 instance, publication_2 ? publication_2->version : 0,
1817 instance, publication_3 ? publication_3->version : 0);
1819 send_presence_publish(sipe_private, publications);
1820 g_free(publications);
1823 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1824 gpointer value,
1825 GString* str)
1827 struct sipe_publication *publication = value;
1829 g_string_append_printf( str,
1830 SIPE_PUB_XML_PUBLICATION_CLEAR,
1831 publication->category,
1832 publication->instance,
1833 publication->container,
1834 publication->version,
1835 "static");
1838 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1840 GString* str;
1841 gchar *publications;
1843 if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1844 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1845 return;
1848 str = g_string_new(NULL);
1849 g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1850 publications = g_string_free(str, FALSE);
1852 send_presence_publish(sipe_private, publications);
1853 g_free(publications);
1856 /* key is <category><instance><container> */
1857 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1858 const gchar *key)
1860 GSList *entry;
1862 /* filling keys for our publications if not yet cached */
1863 if (!sipe_private->our_publication_keys) {
1864 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1865 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1866 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1867 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1868 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1869 guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1870 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1871 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1873 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1874 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1875 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1876 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1877 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1878 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1879 SIPE_DEBUG_INFO("\tVOIP Phone State : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1880 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1881 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1882 SIPE_DEBUG_INFO("\tNote : %u", 0);
1883 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1885 /* device */
1886 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1887 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1889 /* state:machineState */
1890 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1891 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1892 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1893 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1895 /* state:userState */
1896 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1897 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1898 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1899 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1901 /* state:calendarState */
1902 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1903 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1904 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1905 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1907 /* state:calendarState OOF */
1908 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1909 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1910 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1911 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1913 /* state:phoneState */
1914 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1915 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1916 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1917 g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1919 /* note */
1920 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1921 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1922 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1923 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1924 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1925 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1927 /* note OOF */
1928 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1929 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1930 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1931 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1932 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1933 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1935 /* calendarData:WorkingHours */
1936 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1937 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1938 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1939 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1940 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1941 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1942 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1943 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1944 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1945 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1946 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1947 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1949 /* calendarData:FreeBusy */
1950 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1951 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
1952 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1953 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
1954 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1955 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
1956 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1957 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
1958 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1959 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
1960 sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1961 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
1963 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
1964 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
1967 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1969 entry = sipe_private->our_publication_keys;
1970 while (entry) {
1971 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1972 if (sipe_strequal(entry->data, key)) {
1973 return TRUE;
1975 entry = entry->next;
1977 return FALSE;
1980 static void sipe_refresh_blocked_status_cb(char *buddy_name,
1981 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1982 struct sipe_core_private *sipe_private)
1984 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
1985 gboolean blocked = (container_id == 32000);
1986 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
1988 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1989 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1991 if (blocked != blocked_in_blist) {
1992 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
1996 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
1998 g_hash_table_foreach(sipe_private->buddies,
1999 (GHFunc) sipe_refresh_blocked_status_cb,
2000 sipe_private);
2004 * When we receive some self (BE) NOTIFY with a new subscriber
2005 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2008 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2009 struct sipmsg *msg)
2011 gchar *contact;
2012 gchar *to;
2013 sipe_xml *xml;
2014 const sipe_xml *node;
2015 const sipe_xml *node2;
2016 char *display_name = NULL;
2017 char *uri;
2018 GSList *category_names = NULL;
2019 int aggreg_avail = 0;
2020 gchar *activity_token = NULL;
2021 gboolean do_update_status = FALSE;
2022 gboolean has_note_cleaned = FALSE;
2023 GHashTable *devices;
2025 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2027 xml = sipe_xml_parse(msg->body, msg->bodylen);
2028 if (!xml) return;
2030 contact = get_contact(sipe_private);
2031 to = sip_uri_self(sipe_private);
2033 /* categories */
2034 /* set list of categories participating in this XML */
2035 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2036 const gchar *name = sipe_xml_attribute(node, "name");
2037 category_names = slist_insert_unique_sorted(category_names, (gchar *)name, (GCompareFunc)strcmp);
2039 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2040 category_names ? (int) g_slist_length(category_names) : -1);
2041 /* drop category information */
2042 if (category_names) {
2043 GSList *entry = category_names;
2044 while (entry) {
2045 GHashTable *cat_publications;
2046 const gchar *category = entry->data;
2047 entry = entry->next;
2048 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2049 cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2050 if (cat_publications) {
2051 g_hash_table_remove(sipe_private->our_publications, category);
2052 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2056 g_slist_free(category_names);
2058 /* filling our categories reflected in roaming data */
2059 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2060 g_free, NULL);
2061 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2062 const char *tmp;
2063 const gchar *name = sipe_xml_attribute(node, "name");
2064 guint container = sipe_xml_int_attribute(node, "container", -1);
2065 guint instance = sipe_xml_int_attribute(node, "instance", -1);
2066 guint version = sipe_xml_int_attribute(node, "version", 0);
2067 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2068 sipe_utils_str_to_time(tmp) : 0;
2069 gchar *key;
2070 GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2072 /* Ex. clear note: <category name="note"/> */
2073 if (container == (guint)-1) {
2074 g_free(sipe_private->note);
2075 sipe_private->note = NULL;
2076 do_update_status = TRUE;
2077 continue;
2080 /* Ex. clear note: <category name="note" container="200"/> */
2081 if (instance == (guint)-1) {
2082 if (container == 200) {
2083 g_free(sipe_private->note);
2084 sipe_private->note = NULL;
2085 do_update_status = TRUE;
2087 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2088 sipe_remove_category_container_publications(
2089 sipe_private->our_publications, name, container);
2090 continue;
2093 /* key is <category><instance><container> */
2094 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2095 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2097 /* capture all userState publication for later clean up if required */
2098 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2099 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2101 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2102 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2103 publication->category = g_strdup(name);
2104 publication->instance = instance;
2105 publication->container = container;
2106 publication->version = version;
2108 if (!sipe_private->user_state_publications) {
2109 sipe_private->user_state_publications = g_hash_table_new_full(
2110 g_str_hash, g_str_equal,
2111 g_free, (GDestroyNotify)free_publication);
2113 g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2114 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2115 key, version);
2119 /* count each client instance only once */
2120 if (sipe_strequal(name, "device"))
2121 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2123 if (sipe_is_our_publication(sipe_private, key)) {
2124 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2126 publication->category = g_strdup(name);
2127 publication->instance = instance;
2128 publication->container = container;
2129 publication->version = version;
2131 /* filling publication->availability */
2132 if (sipe_strequal(name, "state")) {
2133 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2134 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2136 if (xn_avail) {
2137 gchar *avail_str = sipe_xml_data(xn_avail);
2138 if (avail_str) {
2139 publication->availability = atoi(avail_str);
2141 g_free(avail_str);
2143 /* for calendarState */
2144 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2145 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2146 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2148 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2149 if (xn_activity) {
2150 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2151 sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2153 event->is_meeting = TRUE;
2156 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2157 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2159 publication->cal_event_hash = sipe_cal_event_hash(event);
2160 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2161 publication->cal_event_hash);
2162 sipe_cal_event_free(event);
2165 /* filling publication->note */
2166 if (sipe_strequal(name, "note")) {
2167 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2169 if (!has_note_cleaned) {
2170 has_note_cleaned = TRUE;
2172 g_free(sipe_private->note);
2173 sipe_private->note = NULL;
2174 sipe_private->note_since = publish_time;
2176 do_update_status = TRUE;
2179 g_free(publication->note);
2180 publication->note = NULL;
2181 if (xn_body) {
2182 char *tmp;
2184 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2185 g_free(tmp);
2186 if (publish_time >= sipe_private->note_since) {
2187 g_free(sipe_private->note);
2188 sipe_private->note = g_strdup(publication->note);
2189 sipe_private->note_since = publish_time;
2190 if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2191 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2192 else
2193 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2195 do_update_status = TRUE;
2200 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2201 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2202 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2203 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2204 if (xn_free_busy) {
2205 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2206 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2208 if (xn_working_hours) {
2209 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2213 if (!cat_publications) {
2214 cat_publications = g_hash_table_new_full(
2215 g_str_hash, g_str_equal,
2216 g_free, (GDestroyNotify)free_publication);
2217 g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2218 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2220 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2221 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2223 g_free(key);
2225 /* aggregateState (not an our publication) from 2-nd container */
2226 if (sipe_strequal(name, "state") && container == 2) {
2227 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2228 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2230 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2231 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2233 if (xn_avail) {
2234 gchar *avail_str = sipe_xml_data(xn_avail);
2235 if (avail_str) {
2236 aggreg_avail = atoi(avail_str);
2238 g_free(avail_str);
2241 do_update_status = TRUE;
2244 if (xn_activity) {
2245 activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2249 /* userProperties published by server from AD */
2250 if (!sipe_private->csta &&
2251 sipe_strequal(name, "userProperties")) {
2252 const sipe_xml *line;
2253 /* line, for Remote Call Control (RCC) */
2254 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2255 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2256 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2257 gchar *line_uri;
2259 if (!line_server || !(sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual"))) continue;
2261 line_uri = sipe_xml_data(line);
2262 if (line_uri) {
2263 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s", line_uri, line_server);
2264 sip_csta_open(sipe_private, line_uri, line_server);
2266 g_free(line_uri);
2268 break;
2272 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2273 sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2275 /* active clients for user account */
2276 if (g_hash_table_size(devices) > 1) {
2277 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2278 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2279 g_hash_table_size(devices));
2280 } else {
2281 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2282 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2284 g_hash_table_destroy(devices);
2286 /* containers */
2287 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2288 guint id = sipe_xml_int_attribute(node, "id", 0);
2289 struct sipe_container *container = sipe_find_container(sipe_private, id);
2291 if (container) {
2292 sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2293 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2294 sipe_ocs2007_free_container(container);
2296 container = g_new0(struct sipe_container, 1);
2297 container->id = id;
2298 container->version = sipe_xml_int_attribute(node, "version", 0);
2299 sipe_private->containers = g_slist_append(sipe_private->containers, container);
2300 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2302 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2303 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2304 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2305 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2306 container->members = g_slist_append(container->members, member);
2307 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2308 member->type, member->value ? member->value : "");
2312 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2313 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2314 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2315 char *container_xmls = NULL;
2316 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2317 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2319 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2320 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2321 /* initial set-up to let counterparties see your status */
2322 if (sameEnterpriseAL < 0) {
2323 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2324 guint version = container ? container->version : 0;
2325 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2327 if (federatedAL < 0) {
2328 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2329 guint version = container ? container->version : 0;
2330 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2332 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2334 if (container_xmls) {
2335 sipe_send_set_container_members(sipe_private, container_xmls);
2337 g_free(container_xmls);
2340 /* Refresh contacts' blocked status */
2341 sipe_refresh_blocked_status(sipe_private);
2343 /* subscribers */
2344 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2345 const char *user;
2346 const char *acknowledged;
2347 gchar *hdr;
2348 gchar *body;
2350 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2351 if (!user) continue;
2352 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2353 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2354 uri = sip_uri_from_name(user);
2356 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2358 acknowledged= sipe_xml_attribute(node, "acknowledged");
2359 if(sipe_strcase_equal(acknowledged,"false")){
2360 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2361 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2362 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2365 hdr = g_strdup_printf(
2366 "Contact: %s\r\n"
2367 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2369 body = g_strdup_printf(
2370 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2371 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2372 "</setSubscribers>", user);
2374 sip_transport_service(sipe_private,
2376 hdr,
2377 body,
2378 NULL);
2379 g_free(body);
2380 g_free(hdr);
2382 g_free(display_name);
2383 g_free(uri);
2386 g_free(contact);
2387 sipe_xml_free(xml);
2389 /* Publish initial state if not yet.
2390 * Assuming this happens on initial responce to subscription to roaming-self
2391 * so we've already updated our roaming data in full.
2392 * Only for 2007+
2394 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2395 send_publish_category_initial(sipe_private);
2396 sipe_groupchat_init(sipe_private);
2397 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2398 /* dalayed run */
2399 sipe_cal_delayed_calendar_update(sipe_private);
2400 do_update_status = FALSE;
2401 } else if (aggreg_avail) {
2403 if (aggreg_avail &&
2404 (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2405 /* not offline */
2406 sipe_status_set_token(sipe_private,
2407 sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2408 } else {
2409 /* do not let offline status switch us off */
2410 sipe_status_set_activity(sipe_private,
2411 SIPE_ACTIVITY_INVISIBLE);
2415 if (do_update_status) {
2416 sipe_status_and_note(sipe_private, NULL);
2419 g_free(to);
2420 g_free(activity_token);
2424 * for Access levels menu
2426 #define INDENT_FMT " %s"
2429 * Member is indirectly belong to access level container.
2430 * For example 'sameEnterprise' is in the container and user
2431 * belongs to that same enterprise.
2433 #define INDENT_MARKED_INHERITED_FMT "= %s"
2435 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2436 struct sipe_backend_buddy_menu *menu,
2437 const gchar *member_type,
2438 const gchar *member_value,
2439 const gboolean extra_menu)
2441 unsigned int i;
2442 gboolean is_group_access = FALSE;
2443 int container_id;
2445 if (!menu)
2446 menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2448 container_id = sipe_ocs2007_find_access_level(sipe_private,
2449 member_type,
2450 member_value,
2451 &is_group_access);
2453 for (i = 1; i <= CONTAINERS_LEN; i++) {
2455 * Blocked should remain in the first place
2456 * in the containers[] array.
2458 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
2459 int container_j = containers[j];
2460 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2461 struct sipe_container *container = create_container(j,
2462 member_type,
2463 member_value,
2464 FALSE);
2465 gchar *label;
2467 /* libpurple memory leak workaround */
2468 blist_menu_remember_container(sipe_private, container);
2470 /* current container/access level */
2471 if (container_j == container_id) {
2472 label = is_group_access ?
2473 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2474 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2475 } else {
2476 label = g_strdup_printf(INDENT_FMT, acc_level_name);
2479 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2480 menu,
2481 label,
2482 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2483 container);
2484 g_free(label);
2487 if (extra_menu && (container_id >= 0) && !is_group_access) {
2488 struct sipe_container *container = create_container(0,
2489 member_type,
2490 member_value,
2491 TRUE);
2492 gchar *label;
2494 /* separator */
2495 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2496 menu,
2497 " --------------");
2500 /* libpurple memory leak workaround */
2501 blist_menu_remember_container(sipe_private, container);
2503 /* Translators: remove (clear) previously assigned access level */
2504 label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2505 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2506 menu,
2507 label,
2508 SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2509 container);
2510 g_free(label);
2513 return(menu);
2516 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2518 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2519 GSList *access_domains, *entry;
2521 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2522 menu,
2523 _("People in my company"),
2524 access_levels_menu(sipe_private,
2525 NULL,
2526 "sameEnterprise",
2527 NULL,
2528 FALSE));
2530 /* this is original name, don't edit */
2531 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2532 menu,
2533 _("People in domains connected with my company"),
2534 access_levels_menu(sipe_private,
2535 NULL,
2536 "federated",
2537 NULL,
2538 FALSE));
2540 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2541 menu,
2542 _("People in public domains"),
2543 access_levels_menu(sipe_private,
2544 NULL,
2545 "publicCloud",
2546 NULL,
2547 TRUE));
2549 entry = access_domains = get_access_domains(sipe_private);
2550 while (entry) {
2551 gchar *domain = entry->data;
2552 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2554 /* takes over ownership of entry->data (= domain) */
2555 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2556 menu,
2557 menu_name,
2558 access_levels_menu(sipe_private,
2559 NULL,
2560 "domain",
2561 domain,
2562 TRUE));
2563 g_free(menu_name);
2565 entry = entry->next;
2567 g_slist_free(access_domains);
2569 /* separator */
2570 /* People in domains connected with my company */
2571 menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2572 menu,
2573 "-------------------------------------------");
2575 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2576 menu,
2577 _("Add new domain..."),
2578 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2579 NULL);
2581 return(menu);
2584 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2585 const gchar *buddy_name)
2587 struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2588 gchar *label;
2591 * Workaround for missing libpurple API to release resources allocated
2592 * during blist_node_menu() callback. See also:
2594 * <http://developer.pidgin.im/ticket/12597>
2596 * We remember all memory blocks in a list and deallocate them when
2598 * - the next time we enter the callback, or
2599 * - the account is disconnected
2601 * That means that after the buddy menu has been closed we have unused
2602 * resources but at least we don't leak them anymore...
2604 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2606 label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2607 menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2608 menu,
2609 label,
2610 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2611 NULL);
2612 g_free(label);
2614 label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2615 menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2616 menu,
2617 label,
2618 access_groups_menu(sipe_private));
2619 g_free(label);
2621 menu = access_levels_menu(sipe_private,
2622 menu,
2623 "user",
2624 sipe_get_no_sip_uri(buddy_name),
2625 TRUE);
2627 return(menu);
2631 Local Variables:
2632 mode: c
2633 c-file-style: "bsd"
2634 indent-tabs-mode: t
2635 tab-width: 8
2636 End: