core cleanup: separate code for sipe_backend_account_status_and_note()
[siplcs.git] / src / core / sipe-ocs2007.c
blob92256934b9dee1eec4a3388e68a368cc2cdeb4b7
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 "http-conn.h"
40 #include "sipmsg.h"
41 #include "sip-csta.h"
42 #include "sip-transport.h"
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
45 #include "sipe-cal.h"
46 #include "sipe-core.h"
47 #include "sipe-core-private.h"
48 #include "sipe-ews.h"
49 #include "sipe-groupchat.h"
50 #include "sipe-nls.h"
51 #include "sipe-ocs2007.h"
52 #include "sipe-schedule.h"
53 #include "sipe-status.h"
54 #include "sipe-utils.h"
55 #include "sipe-xml.h"
57 #define _SIPE_NEED_ACTIVITIES /* ugly hack :-( */
58 #include "sipe.h"
60 static void send_presence_publish(struct sipe_core_private *sipe_private,
61 const char *publications);
63 static void free_publication(struct sipe_publication *publication)
65 g_free(publication->category);
66 g_free(publication->cal_event_hash);
67 g_free(publication->note);
69 g_free(publication->working_hours_xml_str);
70 g_free(publication->fb_start_str);
71 g_free(publication->free_busy_base64);
73 g_free(publication);
76 struct hash_table_delete_payload {
77 GHashTable *hash_table;
78 guint container;
81 static void sipe_remove_category_container_publications_cb(const gchar *name,
82 struct sipe_publication *publication,
83 struct hash_table_delete_payload *payload)
85 if (publication->container == payload->container) {
86 g_hash_table_remove(payload->hash_table, name);
90 static void sipe_remove_category_container_publications(GHashTable *our_publications,
91 const gchar *category,
92 guint container)
94 struct hash_table_delete_payload payload;
95 payload.hash_table = g_hash_table_lookup(our_publications, category);
97 if (!payload.hash_table) return;
99 payload.container = container;
100 g_hash_table_foreach(payload.hash_table,
101 (GHFunc)sipe_remove_category_container_publications_cb,
102 &payload);
105 /** MS-PRES container */
106 struct sipe_container {
107 guint id;
108 guint version;
109 GSList *members;
112 /** MS-PRES container member */
113 struct sipe_container_member {
114 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
115 gchar *type;
116 gchar *value;
119 static const guint containers[] = {32000, 400, 300, 200, 100};
120 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
122 guint sipe_ocs2007_containers(void)
124 return(CONTAINERS_LEN);
127 static void free_container_member(struct sipe_container_member *member)
129 if (!member) return;
131 g_free(member->type);
132 g_free(member->value);
133 g_free(member);
136 void sipe_ocs2007_free_container(struct sipe_container *container)
138 GSList *entry;
140 if (!container) return;
142 entry = container->members;
143 while (entry) {
144 void *data = entry->data;
145 entry = g_slist_remove(entry, data);
146 free_container_member((struct sipe_container_member *)data);
148 g_free(container);
151 struct sipe_container *sipe_ocs2007_create_container(guint index,
152 const gchar *member_type,
153 const gchar *member_value,
154 gboolean is_group)
156 struct sipe_container *container = g_new0(struct sipe_container, 1);
157 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
159 container->id = is_group ? (guint) -1 : containers[index];
160 container->members = g_slist_append(container->members, member);
161 member->type = g_strdup(member_type);
162 member->value = g_strdup(member_value);
164 return(container);
167 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
169 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
171 if (sip->containers) {
172 GSList *entry = sip->containers;
173 while (entry) {
174 sipe_ocs2007_free_container((struct sipe_container *)entry->data);
175 entry = entry->next;
178 g_slist_free(sip->containers);
182 * Finds locally stored MS-PRES container member
184 static struct sipe_container_member *
185 sipe_find_container_member(struct sipe_container *container,
186 const gchar *type,
187 const gchar *value)
189 struct sipe_container_member *member;
190 GSList *entry;
192 if (container == NULL || type == NULL) {
193 return NULL;
196 entry = container->members;
197 while (entry) {
198 member = entry->data;
199 if (sipe_strcase_equal(member->type, type) &&
200 sipe_strcase_equal(member->value, value))
202 return member;
204 entry = entry->next;
206 return NULL;
210 * Finds locally stored MS-PRES container by id
212 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
213 guint id)
215 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
216 struct sipe_container *container;
217 GSList *entry;
219 if (sip == NULL) {
220 return NULL;
223 entry = sip->containers;
224 while (entry) {
225 container = entry->data;
226 if (id == container->id) {
227 return container;
229 entry = entry->next;
231 return NULL;
234 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
235 const gchar *type,
236 const gchar *value)
238 unsigned int i = 0;
239 const gchar *value_mod = value;
241 if (!type) return -1;
243 if (sipe_strequal("user", type)) {
244 value_mod = sipe_get_no_sip_uri(value);
247 for (i = 0; i < CONTAINERS_LEN; i++) {
248 struct sipe_container_member *member;
249 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
250 if (!container) continue;
252 member = sipe_find_container_member(container, type, value_mod);
253 if (member) return containers[i];
256 return -1;
260 * Returns pointer to domain part in provided Email URL
262 * @param email an email URL. Example: first.last@hq.company.com
263 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
265 * Doesn't allocate memory
267 static const gchar *sipe_get_domain(const gchar *email)
269 gchar *tmp;
271 if (!email) return NULL;
273 tmp = strstr(email, "@");
275 if (tmp && ((tmp+1) < (email + strlen(email)))) {
276 return tmp+1;
277 } else {
278 return NULL;
282 /* @TODO: replace with binary search for faster access? */
283 /** source: http://support.microsoft.com/kb/897567 */
284 static const gchar * const public_domains[] = {
285 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
286 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
287 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
288 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
289 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
290 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
291 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
292 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
293 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
294 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
295 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
296 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
297 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
298 "yahoo.com",
299 NULL};
301 static gboolean sipe_is_public_domain(const gchar *domain)
303 int i = 0;
304 while (public_domains[i]) {
305 if (sipe_strcase_equal(public_domains[i], domain)) {
306 return TRUE;
308 i++;
310 return FALSE;
314 * Access Levels
315 * 32000 - Blocked
316 * 400 - Personal
317 * 300 - Team
318 * 200 - Company
319 * 100 - Public
321 const gchar *sipe_ocs2007_access_level_name(guint id)
323 switch (id) {
324 case 32000: return _("Blocked");
325 case 400: return _("Personal");
326 case 300: return _("Team");
327 case 200: return _("Company");
328 case 100: return _("Public");
330 return _("Unknown");
333 int sipe_ocs2007_container_id(guint index)
335 return(containers[index]);
338 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
339 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
340 const gchar *type,
341 const gchar *value,
342 gboolean *is_group_access)
344 int container_id = -1;
346 if (sipe_strequal("user", type)) {
347 const char *domain;
348 const char *no_sip_uri = sipe_get_no_sip_uri(value);
350 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
351 if (container_id >= 0) {
352 if (is_group_access) *is_group_access = FALSE;
353 return container_id;
356 domain = sipe_get_domain(no_sip_uri);
357 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
358 if (container_id >= 0) {
359 if (is_group_access) *is_group_access = TRUE;
360 return container_id;
363 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
364 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
365 if (is_group_access) *is_group_access = TRUE;
366 return container_id;
369 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
370 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
371 if (is_group_access) *is_group_access = TRUE;
372 return container_id;
375 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
376 if ((container_id >= 0)) {
377 if (is_group_access) *is_group_access = TRUE;
378 return container_id;
380 } else {
381 container_id = sipe_find_member_access_level(sipe_private, type, value);
382 if (is_group_access) *is_group_access = FALSE;
385 return container_id;
388 GSList *sipe_ocs2007_get_access_domains(struct sipe_core_private *sipe_private)
390 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
391 struct sipe_container *container;
392 struct sipe_container_member *member;
393 GSList *entry;
394 GSList *entry2;
395 GSList *res = NULL;
397 if (!sip) return NULL;
399 entry = sip->containers;
400 while (entry) {
401 container = entry->data;
403 entry2 = container->members;
404 while (entry2) {
405 member = entry2->data;
406 if (sipe_strcase_equal(member->type, "domain"))
408 res = slist_insert_unique_sorted(res, g_strdup(member->value), (GCompareFunc)g_ascii_strcasecmp);
410 entry2 = entry2->next;
412 entry = entry->next;
414 return res;
417 static void sipe_send_container_members_prepare(const guint container_id,
418 const guint container_version,
419 const gchar *action,
420 const gchar *type,
421 const gchar *value,
422 char **container_xmls)
424 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
425 gchar *body;
427 if (!container_xmls) return;
429 body = g_strdup_printf(
430 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
431 container_id,
432 container_version,
433 action,
434 type,
435 value_str);
436 g_free(value_str);
438 if ((*container_xmls) == NULL) {
439 *container_xmls = body;
440 } else {
441 char *tmp = *container_xmls;
443 *container_xmls = g_strconcat(*container_xmls, body, NULL);
444 g_free(tmp);
445 g_free(body);
449 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
450 char *container_xmls)
452 gchar *self;
453 gchar *contact;
454 gchar *hdr;
455 gchar *body;
457 if (!container_xmls) return;
459 self = sip_uri_self(sipe_private);
460 body = g_strdup_printf(
461 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
462 "%s"
463 "</setContainerMembers>",
464 container_xmls);
466 contact = get_contact(sipe_private);
467 hdr = g_strdup_printf("Contact: %s\r\n"
468 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
469 g_free(contact);
471 sip_transport_service(sipe_private,
472 self,
473 hdr,
474 body,
475 NULL);
477 g_free(hdr);
478 g_free(body);
479 g_free(self);
483 * @param container_id a new access level. If -1 then current access level
484 * is just removed (I.e. the member is removed from all containers).
485 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
486 * @param value a value for member. E.g. SIP URI for "user" member type.
488 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
489 const int container_id,
490 const gchar *type,
491 const gchar *value)
493 unsigned int i;
494 int current_container_id = -1;
495 char *container_xmls = NULL;
497 /* for each container: find/delete */
498 for (i = 0; i < CONTAINERS_LEN; i++) {
499 struct sipe_container_member *member;
500 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
502 if (!container) continue;
504 member = sipe_find_container_member(container, type, value);
505 if (member) {
506 current_container_id = containers[i];
507 /* delete/publish current access level */
508 if (container_id < 0 || container_id != current_container_id) {
509 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
510 /* remove member from our cache, to be able to recalculate AL below */
511 container->members = g_slist_remove(container->members, member);
512 current_container_id = -1;
517 /* recalculate AL below */
518 current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
520 /* assign/publish new access level */
521 if (container_id != current_container_id && container_id >= 0) {
522 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
523 guint version = container ? container->version : 0;
525 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
528 if (container_xmls) {
529 sipe_send_set_container_members(sipe_private, container_xmls);
531 g_free(container_xmls);
534 void sipe_ocs2007_change_access_level_from_container(struct sipe_core_private *sipe_private,
535 struct sipe_container *container)
537 struct sipe_container_member *member;
539 if (!container || !container->members) return;
541 member = ((struct sipe_container_member *)container->members->data);
543 if (!member->type) return;
545 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
546 container->id, member->type, member->value ? member->value : "");
548 sipe_ocs2007_change_access_level(sipe_private,
549 container->id,
550 member->type,
551 member->value);
555 void sipe_ocs2007_change_access_level_for_domain(struct sipe_core_private *sipe_private,
556 const gchar *domain,
557 guint index)
559 /* move Blocked first */
560 guint i = (index == 4) ? 0 : index + 1;
561 guint container_id = containers[i];
563 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_id: domain=%s, container_id=(%d)%d",
564 domain ? domain : "", index, container_id);
566 sipe_ocs2007_change_access_level(sipe_private,
567 container_id,
568 "domain",
569 domain);
574 * Schedules process of self status publish
575 * based on own calendar information.
576 * Should be scheduled to the beginning of every
577 * 15 min interval, like:
578 * 13:00, 13:15, 13:30, 13:45, etc.
581 static void schedule_publish_update(struct sipe_core_private *sipe_private,
582 time_t calculate_from)
584 int interval = 5*60;
585 /** start of the beginning of closest 5 min interval. */
586 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
588 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
589 asctime(localtime(&calculate_from)));
590 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
591 asctime(localtime(&next_start)));
593 sipe_schedule_seconds(sipe_private,
594 "<+2007-cal-status>",
595 NULL,
596 next_start - time(NULL),
597 sipe_ocs2007_presence_publish,
598 NULL);
602 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
603 * @param availability (%d) Ex.: 6500
605 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
606 "<availability>%d</availability>"
608 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
609 * @param token (%s) Ex.: in-a-meeting
610 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
611 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
613 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
614 "<activity token=\"%s\" %s %s></activity>"
616 * Publishes 'calendarState' category.
617 * @param instance (%u) Ex.: 1339299275
618 * @param version (%u) Ex.: 1
619 * @param uri (%s) Ex.: john@contoso.com
620 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
621 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
622 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
623 * @param meeting_subject (%s) Ex.: Customer Meeting
624 * @param meeting_location (%s) Ex.: Conf Room 100
626 * @param instance (%u) Ex.: 1339299275
627 * @param version (%u) Ex.: 1
628 * @param uri (%s) Ex.: john@contoso.com
629 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
630 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
631 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
632 * @param meeting_subject (%s) Ex.: Customer Meeting
633 * @param meeting_location (%s) Ex.: Conf Room 100
635 #define SIPE_PUB_XML_STATE_CALENDAR \
636 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
637 "<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\">"\
638 "%s"\
639 "%s"\
640 "<endpointLocation/>"\
641 "<meetingSubject>%s</meetingSubject>"\
642 "<meetingLocation>%s</meetingLocation>"\
643 "</state>"\
644 "</publication>"\
645 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
646 "<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\">"\
647 "%s"\
648 "%s"\
649 "<endpointLocation/>"\
650 "<meetingSubject>%s</meetingSubject>"\
651 "<meetingLocation>%s</meetingLocation>"\
652 "</state>"\
653 "</publication>"
655 * Publishes to clear 'calendarState' category
656 * @param instance (%u) Ex.: 1251210982
657 * @param version (%u) Ex.: 1
659 #define SIPE_PUB_XML_STATE_CALENDAR_CLEAR \
660 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
661 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
664 * Publishes to clear any category
665 * @param category_name (%s) Ex.: state
666 * @param instance (%u) Ex.: 536870912
667 * @param container (%u) Ex.: 3
668 * @param version (%u) Ex.: 1
669 * @param expireType (%s) Ex.: static
671 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
672 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
675 * Publishes 'note' category.
676 * @param instance (%u) Ex.: 2135971629; 0 for personal
677 * @param container (%u) Ex.: 200
678 * @param version (%u) Ex.: 2
679 * @param type (%s) Ex.: personal or OOF
680 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
681 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
682 * @param body (%s) Ex.: In the office
684 #define SIPE_PUB_XML_NOTE \
685 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
686 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
687 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
688 "</note>"\
689 "</publication>"
692 * Only Busy and OOF calendar event are published.
693 * Different instances are used for that.
695 * Must be g_free'd after use.
697 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
698 struct sipe_cal_event *event,
699 const char *uri,
700 int cal_satus)
702 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
703 gchar *start_time_str;
704 int availability = 0;
705 gchar *res;
706 gchar *tmp = NULL;
707 guint instance = (cal_satus == SIPE_CAL_OOF) ?
708 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
709 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
711 /* key is <category><instance><container> */
712 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
713 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
714 struct sipe_publication *publication_2 =
715 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_2);
716 struct sipe_publication *publication_3 =
717 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_3);
719 g_free(key_2);
720 g_free(key_3);
722 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
723 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
724 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
725 return NULL;
728 if (event &&
729 publication_3 &&
730 (publication_3->availability == availability) &&
731 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
733 g_free(tmp);
734 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
735 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
736 return NULL; /* nothing to update */
738 g_free(tmp);
740 if (event &&
741 (event->cal_status == SIPE_CAL_BUSY ||
742 event->cal_status == SIPE_CAL_OOF))
744 gchar *availability_xml_str = NULL;
745 gchar *activity_xml_str = NULL;
746 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
747 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
749 if (event->cal_status == SIPE_CAL_BUSY) {
750 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL, 6500);
753 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
754 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
755 sipe_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
756 "minAvailability=\"6500\"",
757 "maxAvailability=\"8999\"");
758 } else if (event->cal_status == SIPE_CAL_OOF) {
759 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
760 sipe_activity_to_token(SIPE_ACTIVITY_OOF),
761 "minAvailability=\"12000\"",
762 "");
764 start_time_str = sipe_utils_time_to_str(event->start_time);
766 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
767 instance,
768 publication_2 ? publication_2->version : 0,
769 uri,
770 start_time_str,
771 availability_xml_str ? availability_xml_str : "",
772 activity_xml_str ? activity_xml_str : "",
773 escaped_subject ? escaped_subject : "",
774 escaped_location ? escaped_location : "",
776 instance,
777 publication_3 ? publication_3->version : 0,
778 uri,
779 start_time_str,
780 availability_xml_str ? availability_xml_str : "",
781 activity_xml_str ? activity_xml_str : "",
782 escaped_subject ? escaped_subject : "",
783 escaped_location ? escaped_location : ""
785 g_free(escaped_location);
786 g_free(escaped_subject);
787 g_free(start_time_str);
788 g_free(availability_xml_str);
789 g_free(activity_xml_str);
792 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
794 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR,
795 instance,
796 publication_2 ? publication_2->version : 0,
798 instance,
799 publication_3 ? publication_3->version : 0
803 return res;
807 * Returns 'note' XML part for publication.
808 * Must be g_free'd after use.
810 * Protocol format for Note is plain text.
812 * @param note a note in Sipe internal HTML format
813 * @param note_type either personal or OOF
815 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
816 const char *note, /* html */
817 const char *note_type,
818 time_t note_start,
819 time_t note_end)
821 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
822 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
823 /* key is <category><instance><container> */
824 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
825 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
826 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
828 struct sipe_publication *publication_note_200 =
829 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "note"), key_note_200);
830 struct sipe_publication *publication_note_300 =
831 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "note"), key_note_300);
832 struct sipe_publication *publication_note_400 =
833 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "note"), key_note_400);
835 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
836 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
837 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
838 char *res, *tmp1, *tmp2, *tmp3;
839 char *start_time_attr;
840 char *end_time_attr;
842 g_free(tmp);
843 tmp = NULL;
844 g_free(key_note_200);
845 g_free(key_note_300);
846 g_free(key_note_400);
848 /* we even need to republish empty note */
849 if (sipe_strequal(n1, n2))
851 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
852 g_free(n1);
853 return NULL; /* nothing to update */
856 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
857 g_free(tmp);
858 tmp = NULL;
859 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
860 g_free(tmp);
862 if (n1) {
863 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
864 instance,
865 200,
866 publication_note_200 ? publication_note_200->version : 0,
867 note_type,
868 start_time_attr ? start_time_attr : "",
869 end_time_attr ? end_time_attr : "",
870 n1);
872 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
873 instance,
874 300,
875 publication_note_300 ? publication_note_300->version : 0,
876 note_type,
877 start_time_attr ? start_time_attr : "",
878 end_time_attr ? end_time_attr : "",
879 n1);
881 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
882 instance,
883 400,
884 publication_note_400 ? publication_note_400->version : 0,
885 note_type,
886 start_time_attr ? start_time_attr : "",
887 end_time_attr ? end_time_attr : "",
888 n1);
889 } else {
890 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
891 "note",
892 instance,
893 200,
894 publication_note_200 ? publication_note_200->version : 0,
895 "static");
896 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
897 "note",
898 instance,
899 300,
900 publication_note_200 ? publication_note_200->version : 0,
901 "static");
902 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
903 "note",
904 instance,
905 400,
906 publication_note_200 ? publication_note_200->version : 0,
907 "static");
909 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
911 g_free(start_time_attr);
912 g_free(end_time_attr);
913 g_free(tmp1);
914 g_free(tmp2);
915 g_free(tmp3);
916 g_free(n1);
918 return res;
922 * Publishes 'calendarData' category's WorkingHours.
924 * @param version (%u) Ex.: 1
925 * @param email (%s) Ex.: alice@cosmo.local
926 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
928 * @param version (%u)
930 * @param version (%u)
931 * @param email (%s)
932 * @param working_hours_xml_str (%s)
934 * @param version (%u)
935 * @param email (%s)
936 * @param working_hours_xml_str (%s)
938 * @param version (%u)
939 * @param email (%s)
940 * @param working_hours_xml_str (%s)
942 * @param version (%u)
944 #define SIPE_PUB_XML_WORKING_HOURS \
945 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
946 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
947 "</calendarData>"\
948 "</publication>"\
949 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
950 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
951 "</publication>"\
952 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
953 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
954 "</calendarData>"\
955 "</publication>"\
956 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
957 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
958 "</calendarData>"\
959 "</publication>"\
960 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
961 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
962 "</calendarData>"\
963 "</publication>"\
964 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
965 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
966 "</publication>"
969 * Returns 'calendarData' XML part with WorkingHours for publication.
970 * Must be g_free'd after use.
972 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
974 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
975 struct sipe_calendar* cal = sip->cal;
977 /* key is <category><instance><container> */
978 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
979 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
980 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
981 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
982 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
983 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
985 struct sipe_publication *publication_cal_1 =
986 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_1);
987 struct sipe_publication *publication_cal_100 =
988 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_100);
989 struct sipe_publication *publication_cal_200 =
990 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_200);
991 struct sipe_publication *publication_cal_300 =
992 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_300);
993 struct sipe_publication *publication_cal_400 =
994 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_400);
995 struct sipe_publication *publication_cal_32000 =
996 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_32000);
998 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
999 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1001 g_free(key_cal_1);
1002 g_free(key_cal_100);
1003 g_free(key_cal_200);
1004 g_free(key_cal_300);
1005 g_free(key_cal_400);
1006 g_free(key_cal_32000);
1008 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1009 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1010 return NULL;
1013 if (sipe_strequal(n1, n2))
1015 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1016 return NULL; /* nothing to update */
1019 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1020 /* 1 */
1021 publication_cal_1 ? publication_cal_1->version : 0,
1022 cal->email,
1023 cal->working_hours_xml_str,
1024 /* 100 - Public */
1025 publication_cal_100 ? publication_cal_100->version : 0,
1026 /* 200 - Company */
1027 publication_cal_200 ? publication_cal_200->version : 0,
1028 cal->email,
1029 cal->working_hours_xml_str,
1030 /* 300 - Team */
1031 publication_cal_300 ? publication_cal_300->version : 0,
1032 cal->email,
1033 cal->working_hours_xml_str,
1034 /* 400 - Personal */
1035 publication_cal_400 ? publication_cal_400->version : 0,
1036 cal->email,
1037 cal->working_hours_xml_str,
1038 /* 32000 - Blocked */
1039 publication_cal_32000 ? publication_cal_32000->version : 0
1044 * Publishes 'calendarData' category's FreeBusy.
1046 * @param instance (%u) Ex.: 1300372959
1047 * @param version (%u) Ex.: 1
1049 * @param instance (%u) Ex.: 1300372959
1050 * @param version (%u) Ex.: 1
1052 * @param instance (%u) Ex.: 1300372959
1053 * @param version (%u) Ex.: 1
1054 * @param email (%s) Ex.: alice@cosmo.local
1055 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1056 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1058 * @param instance (%u) Ex.: 1300372959
1059 * @param version (%u) Ex.: 1
1060 * @param email (%s) Ex.: alice@cosmo.local
1061 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1062 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1064 * @param instance (%u) Ex.: 1300372959
1065 * @param version (%u) Ex.: 1
1066 * @param email (%s) Ex.: alice@cosmo.local
1067 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1068 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1070 * @param instance (%u) Ex.: 1300372959
1071 * @param version (%u) Ex.: 1
1073 #define SIPE_PUB_XML_FREE_BUSY \
1074 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1075 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1076 "</publication>"\
1077 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1078 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1079 "</publication>"\
1080 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1081 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1082 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1083 "</calendarData>"\
1084 "</publication>"\
1085 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1086 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1087 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1088 "</calendarData>"\
1089 "</publication>"\
1090 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1091 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1092 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1093 "</calendarData>"\
1094 "</publication>"\
1095 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1096 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1097 "</publication>"
1100 * Returns 'calendarData' XML part with FreeBusy for publication.
1101 * Must be g_free'd after use.
1103 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1105 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1106 struct sipe_calendar* cal = sip->cal;
1107 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1108 char *fb_start_str;
1109 char *free_busy_base64;
1110 /* const char *st; */
1111 /* const char *fb; */
1112 char *res;
1114 /* key is <category><instance><container> */
1115 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1116 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1117 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1118 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1119 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1120 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1122 struct sipe_publication *publication_cal_1 =
1123 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_1);
1124 struct sipe_publication *publication_cal_100 =
1125 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_100);
1126 struct sipe_publication *publication_cal_200 =
1127 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_200);
1128 struct sipe_publication *publication_cal_300 =
1129 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_300);
1130 struct sipe_publication *publication_cal_400 =
1131 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_400);
1132 struct sipe_publication *publication_cal_32000 =
1133 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_32000);
1135 g_free(key_cal_1);
1136 g_free(key_cal_100);
1137 g_free(key_cal_200);
1138 g_free(key_cal_300);
1139 g_free(key_cal_400);
1140 g_free(key_cal_32000);
1142 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1143 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1144 return NULL;
1147 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1148 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1150 /* we will rebuplish the same data to refresh publication time,
1151 * so if data from multiple sources, most recent will be choosen
1153 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1154 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1156 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1158 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1159 // g_free(fb_start_str);
1160 // g_free(free_busy_base64);
1161 // return NULL; /* nothing to update */
1164 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1165 /* 1 */
1166 cal_data_instance,
1167 publication_cal_1 ? publication_cal_1->version : 0,
1168 /* 100 - Public */
1169 cal_data_instance,
1170 publication_cal_100 ? publication_cal_100->version : 0,
1171 /* 200 - Company */
1172 cal_data_instance,
1173 publication_cal_200 ? publication_cal_200->version : 0,
1174 cal->email,
1175 fb_start_str,
1176 free_busy_base64,
1177 /* 300 - Team */
1178 cal_data_instance,
1179 publication_cal_300 ? publication_cal_300->version : 0,
1180 cal->email,
1181 fb_start_str,
1182 free_busy_base64,
1183 /* 400 - Personal */
1184 cal_data_instance,
1185 publication_cal_400 ? publication_cal_400->version : 0,
1186 cal->email,
1187 fb_start_str,
1188 free_busy_base64,
1189 /* 32000 - Blocked */
1190 cal_data_instance,
1191 publication_cal_32000 ? publication_cal_32000->version : 0
1194 g_free(fb_start_str);
1195 g_free(free_busy_base64);
1196 return res;
1201 * Publishes 'device' category.
1202 * @param instance (%u) Ex.: 1938468728
1203 * @param version (%u) Ex.: 1
1204 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1205 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1206 * @param timezone (%s) Ex.: 00:00:00+01:00
1207 * @param machineName (%s) Ex.: BOSTON-OCS07
1209 #define SIPE_PUB_XML_DEVICE \
1210 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1211 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1212 "<capabilities preferred=\"false\" uri=\"%s\">"\
1213 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1214 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1215 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1216 "</capabilities>"\
1217 "<timezone>%s</timezone>"\
1218 "<machineName>%s</machineName>"\
1219 "</device>"\
1220 "</publication>"
1223 * Returns 'device' XML part for publication.
1224 * Must be g_free'd after use.
1226 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1228 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1229 gchar *uri;
1230 gchar *doc;
1231 gchar *uuid = get_uuid(sipe_private);
1232 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1233 /* key is <category><instance><container> */
1234 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1235 struct sipe_publication *publication =
1236 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "device"), key);
1238 g_free(key);
1240 uri = sip_uri_self(sipe_private);
1241 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1242 device_instance,
1243 publication ? publication->version : 0,
1244 uuid,
1245 uri,
1246 "00:00:00+01:00", /* @TODO make timezone real*/
1247 g_get_host_name()
1250 g_free(uri);
1251 g_free(uuid);
1253 return doc;
1257 * Publishes 'machineState' category.
1258 * @param instance (%u) Ex.: 926460663
1259 * @param version (%u) Ex.: 22
1260 * @param availability (%d) Ex.: 3500
1261 * @param instance (%u) Ex.: 926460663
1262 * @param version (%u) Ex.: 22
1263 * @param availability (%d) Ex.: 3500
1265 #define SIPE_PUB_XML_STATE_MACHINE \
1266 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1267 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1268 "<availability>%d</availability>"\
1269 "<endpointLocation/>"\
1270 "</state>"\
1271 "</publication>"\
1272 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1273 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1274 "<availability>%d</availability>"\
1275 "<endpointLocation/>"\
1276 "</state>"\
1277 "</publication>"
1280 * Publishes 'userState' category.
1281 * @param instance (%u) User. Ex.: 536870912
1282 * @param version (%u) User Container 2. Ex.: 22
1283 * @param availability (%d) User Container 2. Ex.: 15500
1284 * @param instance (%u) User. Ex.: 536870912
1285 * @param version (%u) User Container 3.Ex.: 22
1286 * @param availability (%d) User Container 3. Ex.: 15500
1288 #define SIPE_PUB_XML_STATE_USER \
1289 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1290 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1291 "<availability>%d</availability>"\
1292 "<endpointLocation/>"\
1293 "</state>"\
1294 "</publication>"\
1295 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1296 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1297 "<availability>%d</availability>"\
1298 "<endpointLocation/>"\
1299 "</state>"\
1300 "</publication>"
1303 * A service method - use
1304 * - send_publish_get_category_state_machine and
1305 * - send_publish_get_category_state_user instead.
1306 * Must be g_free'd after use.
1308 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1309 gboolean is_user_state)
1311 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1312 int availability = sipe_get_availability_by_status(sip->status, NULL);
1313 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1314 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1315 /* key is <category><instance><container> */
1316 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1317 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1318 struct sipe_publication *publication_2 =
1319 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_2);
1320 struct sipe_publication *publication_3 =
1321 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_3);
1323 g_free(key_2);
1324 g_free(key_3);
1326 if (publication_2 && (publication_2->availability == availability))
1328 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1329 return NULL; /* nothing to update */
1332 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1333 instance,
1334 publication_2 ? publication_2->version : 0,
1335 availability,
1336 instance,
1337 publication_3 ? publication_3->version : 0,
1338 availability);
1342 * Returns 'machineState' XML part for publication.
1343 * Must be g_free'd after use.
1345 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
1347 return sipe_publish_get_category_state(sipe_private, FALSE);
1351 * Returns 'userState' XML part for publication.
1352 * Must be g_free'd after use.
1354 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
1356 return sipe_publish_get_category_state(sipe_private, TRUE);
1359 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1361 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
1362 gchar *pub_machine;
1363 gchar *publications;
1365 sipe_set_initial_status(sipe_private);
1367 pub_machine = sipe_publish_get_category_state_machine(sipe_private);
1368 publications = g_strdup_printf("%s%s",
1369 pub_device,
1370 pub_machine ? pub_machine : "");
1371 g_free(pub_device);
1372 g_free(pub_machine);
1374 send_presence_publish(sipe_private, publications);
1375 g_free(publications);
1378 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1379 struct sipmsg *msg,
1380 struct transaction *trans)
1382 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1384 if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1385 sipe_xml *xml;
1386 const sipe_xml *node;
1387 gchar *fault_code;
1388 GHashTable *faults;
1389 int index_our;
1390 gboolean has_device_publication = FALSE;
1392 xml = sipe_xml_parse(msg->body, msg->bodylen);
1394 /* test if version mismatch fault */
1395 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1396 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1397 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1398 g_free(fault_code);
1399 sipe_xml_free(xml);
1400 return TRUE;
1402 g_free(fault_code);
1404 /* accumulating information about faulty versions */
1405 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1406 for (node = sipe_xml_child(xml, "details/operation");
1407 node;
1408 node = sipe_xml_twin(node))
1410 const gchar *index = sipe_xml_attribute(node, "index");
1411 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1413 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1414 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1416 sipe_xml_free(xml);
1418 /* here we are parsing our own request to figure out what publication
1419 * referenced here only by index went wrong
1421 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1423 /* publication */
1424 for (node = sipe_xml_child(xml, "publications/publication"),
1425 index_our = 1; /* starts with 1 - our first publication */
1426 node;
1427 node = sipe_xml_twin(node), index_our++)
1429 gchar *idx = g_strdup_printf("%d", index_our);
1430 const gchar *curVersion = g_hash_table_lookup(faults, idx);
1431 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1432 g_free(idx);
1434 if (sipe_strequal("device", categoryName)) {
1435 has_device_publication = TRUE;
1438 if (curVersion) { /* fault exist on this index */
1439 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1440 const gchar *container = sipe_xml_attribute(node, "container");
1441 const gchar *instance = sipe_xml_attribute(node, "instance");
1442 /* key is <category><instance><container> */
1443 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1444 GHashTable *category = g_hash_table_lookup(sip->our_publications, categoryName);
1446 if (category) {
1447 struct sipe_publication *publication =
1448 g_hash_table_lookup(category, key);
1450 SIPE_DEBUG_INFO("key is %s", key);
1452 if (publication) {
1453 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1454 key, curVersion, publication->version);
1455 /* updating publication's version to the correct one */
1456 publication->version = atoi(curVersion);
1458 } else {
1459 /* We somehow lost this category from our publications... */
1460 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1461 publication->category = g_strdup(categoryName);
1462 publication->instance = atoi(instance);
1463 publication->container = atoi(container);
1464 publication->version = atoi(curVersion);
1465 category = g_hash_table_new_full(g_str_hash, g_str_equal,
1466 g_free, (GDestroyNotify)free_publication);
1467 g_hash_table_insert(category, g_strdup(key), publication);
1468 g_hash_table_insert(sip->our_publications, g_strdup(categoryName), category);
1469 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1471 g_free(key);
1474 sipe_xml_free(xml);
1475 g_hash_table_destroy(faults);
1477 /* rebublishing with right versions */
1478 if (has_device_publication) {
1479 send_publish_category_initial(sipe_private);
1480 } else {
1481 send_presence_status(sipe_private, NULL);
1484 return TRUE;
1488 * Publishes categories.
1489 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1490 * @param publications (%s) XML publications
1492 #define SIPE_SEND_PRESENCE \
1493 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1494 "<publications uri=\"%s\">"\
1495 "%s"\
1496 "</publications>"\
1497 "</publish>"
1499 static void send_presence_publish(struct sipe_core_private *sipe_private,
1500 const char *publications)
1502 gchar *uri;
1503 gchar *doc;
1504 gchar *tmp;
1505 gchar *hdr;
1507 uri = sip_uri_self(sipe_private);
1508 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1509 uri,
1510 publications);
1512 tmp = get_contact(sipe_private);
1513 hdr = g_strdup_printf("Contact: %s\r\n"
1514 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1516 sip_transport_service(sipe_private,
1517 uri,
1518 hdr,
1519 doc,
1520 process_send_presence_category_publish_response);
1522 g_free(tmp);
1523 g_free(hdr);
1524 g_free(uri);
1525 g_free(doc);
1529 * Publishes self status
1530 * based on own calendar information.
1532 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1533 SIPE_UNUSED_PARAMETER void *unused)
1535 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1536 struct sipe_cal_event* event = NULL;
1537 gchar *pub_cal_working_hours = NULL;
1538 gchar *pub_cal_free_busy = NULL;
1539 gchar *pub_calendar = NULL;
1540 gchar *pub_calendar2 = NULL;
1541 gchar *pub_oof_note = NULL;
1542 const gchar *oof_note;
1543 time_t oof_start = 0;
1544 time_t oof_end = 0;
1546 if (!sip->cal) {
1547 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1548 return;
1551 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1552 if (sip->cal->cal_events) {
1553 event = sipe_cal_get_event(sip->cal->cal_events, time(NULL));
1556 if (!event) {
1557 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1558 } else {
1559 char *desc = sipe_cal_event_describe(event);
1560 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
1561 g_free(desc);
1564 /* Logic
1565 if OOF
1566 OOF publish, Busy clean
1567 ilse if Busy
1568 OOF clean, Busy publish
1569 else
1570 OOF clean, Busy clean
1572 if (event && event->cal_status == SIPE_CAL_OOF) {
1573 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, sip->cal->email, SIPE_CAL_OOF);
1574 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_BUSY);
1575 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
1576 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_OOF);
1577 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, sip->cal->email, SIPE_CAL_BUSY);
1578 } else {
1579 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_OOF);
1580 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_BUSY);
1583 oof_note = sipe_ews_get_oof_note(sip->cal);
1584 if (sipe_strequal("Scheduled", sip->cal->oof_state)) {
1585 oof_start = sip->cal->oof_start;
1586 oof_end = sip->cal->oof_end;
1588 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
1590 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1591 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1593 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1594 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1595 } else {
1596 gchar *publications = g_strdup_printf("%s%s%s%s%s",
1597 pub_cal_working_hours ? pub_cal_working_hours : "",
1598 pub_cal_free_busy ? pub_cal_free_busy : "",
1599 pub_calendar ? pub_calendar : "",
1600 pub_calendar2 ? pub_calendar2 : "",
1601 pub_oof_note ? pub_oof_note : "");
1603 send_presence_publish(sipe_private, publications);
1604 g_free(publications);
1607 g_free(pub_cal_working_hours);
1608 g_free(pub_cal_free_busy);
1609 g_free(pub_calendar);
1610 g_free(pub_calendar2);
1611 g_free(pub_oof_note);
1613 /* repeat scheduling */
1614 schedule_publish_update(sipe_private, time(NULL));
1617 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private)
1619 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1620 gchar *pub_state = sipe_is_user_state(sipe_private) ?
1621 sipe_publish_get_category_state_user(sipe_private) :
1622 sipe_publish_get_category_state_machine(sipe_private);
1623 gchar *pub_note = sipe_publish_get_category_note(sipe_private,
1624 sip->note,
1625 sip->is_oof_note ? "OOF" : "personal",
1628 gchar *publications;
1630 if (!pub_state && !pub_note) {
1631 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1632 return;
1635 publications = g_strdup_printf("%s%s",
1636 pub_state ? pub_state : "",
1637 pub_note ? pub_note : "");
1639 g_free(pub_state);
1640 g_free(pub_note);
1642 send_presence_publish(sipe_private, publications);
1643 g_free(publications);
1646 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1647 gpointer value,
1648 GString* str)
1650 struct sipe_publication *publication = value;
1652 g_string_append_printf( str,
1653 SIPE_PUB_XML_PUBLICATION_CLEAR,
1654 publication->category,
1655 publication->instance,
1656 publication->container,
1657 publication->version,
1658 "static");
1661 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1663 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1664 GString* str;
1665 gchar *publications;
1667 if (!sip->user_state_publications || g_hash_table_size(sip->user_state_publications) == 0) {
1668 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1669 return;
1672 str = g_string_new(NULL);
1673 g_hash_table_foreach(sip->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1674 publications = g_string_free(str, FALSE);
1676 send_presence_publish(sipe_private, publications);
1677 g_free(publications);
1680 /* key is <category><instance><container> */
1681 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1682 const gchar *key)
1684 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1685 GSList *entry;
1687 /* filling keys for our publications if not yet cached */
1688 if (!sip->our_publication_keys) {
1689 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1690 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1691 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1692 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1693 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1694 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1695 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1697 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1698 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1699 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1700 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1701 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1702 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1703 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1704 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1705 SIPE_DEBUG_INFO("\tNote : %u", 0);
1706 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1708 /* device */
1709 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1710 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1712 /* state:machineState */
1713 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1714 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1715 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1716 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1718 /* state:userState */
1719 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1720 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1721 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1722 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1724 /* state:calendarState */
1725 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1726 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1727 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1728 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1730 /* state:calendarState OOF */
1731 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1732 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1733 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1734 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1736 /* note */
1737 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1738 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1739 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1740 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1741 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1742 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1744 /* note OOF */
1745 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1746 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1747 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1748 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1749 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1750 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1752 /* calendarData:WorkingHours */
1753 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1754 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1755 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1756 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1757 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1758 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1759 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1760 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1761 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1762 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1763 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1764 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1766 /* calendarData:FreeBusy */
1767 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1768 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
1769 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1770 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
1771 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1772 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
1773 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1774 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
1775 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1776 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
1777 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1778 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
1780 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
1781 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
1784 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1786 entry = sip->our_publication_keys;
1787 while (entry) {
1788 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1789 if (sipe_strequal(entry->data, key)) {
1790 return TRUE;
1792 entry = entry->next;
1794 return FALSE;
1797 static void sipe_refresh_blocked_status_cb(char *buddy_name,
1798 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
1799 struct sipe_core_private *sipe_private)
1801 int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
1802 gboolean blocked = (container_id == 32000);
1803 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
1805 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1806 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1808 if (blocked != blocked_in_blist) {
1809 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
1813 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
1815 g_hash_table_foreach(sipe_private->buddies,
1816 (GHFunc) sipe_refresh_blocked_status_cb,
1817 sipe_private);
1821 * When we receive some self (BE) NOTIFY with a new subscriber
1822 * we sends a setSubscribers request to him [SIP-PRES] 4.8
1825 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
1826 struct sipmsg *msg)
1828 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1829 gchar *contact;
1830 gchar *to;
1831 sipe_xml *xml;
1832 const sipe_xml *node;
1833 const sipe_xml *node2;
1834 char *display_name = NULL;
1835 char *uri;
1836 GSList *category_names = NULL;
1837 int aggreg_avail = 0;
1838 gboolean do_update_status = FALSE;
1839 gboolean has_note_cleaned = FALSE;
1840 GHashTable *devices;
1842 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
1844 xml = sipe_xml_parse(msg->body, msg->bodylen);
1845 if (!xml) return;
1847 contact = get_contact(sipe_private);
1848 to = sip_uri_self(sipe_private);
1850 /* categories */
1851 /* set list of categories participating in this XML */
1852 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
1853 const gchar *name = sipe_xml_attribute(node, "name");
1854 category_names = slist_insert_unique_sorted(category_names, (gchar *)name, (GCompareFunc)strcmp);
1856 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
1857 category_names ? (int) g_slist_length(category_names) : -1);
1858 /* drop category information */
1859 if (category_names) {
1860 GSList *entry = category_names;
1861 while (entry) {
1862 GHashTable *cat_publications;
1863 const gchar *category = entry->data;
1864 entry = entry->next;
1865 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
1866 cat_publications = g_hash_table_lookup(sip->our_publications, category);
1867 if (cat_publications) {
1868 g_hash_table_remove(sip->our_publications, category);
1869 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
1873 g_slist_free(category_names);
1875 /* filling our categories reflected in roaming data */
1876 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
1877 g_free, NULL);
1878 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
1879 const char *tmp;
1880 const gchar *name = sipe_xml_attribute(node, "name");
1881 guint container = sipe_xml_int_attribute(node, "container", -1);
1882 guint instance = sipe_xml_int_attribute(node, "instance", -1);
1883 guint version = sipe_xml_int_attribute(node, "version", 0);
1884 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
1885 sipe_utils_str_to_time(tmp) : 0;
1886 gchar *key;
1887 GHashTable *cat_publications = g_hash_table_lookup(sip->our_publications, name);
1889 /* Ex. clear note: <category name="note"/> */
1890 if (container == (guint)-1) {
1891 g_free(sip->note);
1892 sip->note = NULL;
1893 do_update_status = TRUE;
1894 continue;
1897 /* Ex. clear note: <category name="note" container="200"/> */
1898 if (instance == (guint)-1) {
1899 if (container == 200) {
1900 g_free(sip->note);
1901 sip->note = NULL;
1902 do_update_status = TRUE;
1904 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
1905 sipe_remove_category_container_publications(
1906 sip->our_publications, name, container);
1907 continue;
1910 /* key is <category><instance><container> */
1911 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
1912 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
1914 /* capture all userState publication for later clean up if required */
1915 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
1916 const sipe_xml *xn_state = sipe_xml_child(node, "state");
1918 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
1919 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1920 publication->category = g_strdup(name);
1921 publication->instance = instance;
1922 publication->container = container;
1923 publication->version = version;
1925 if (!sip->user_state_publications) {
1926 sip->user_state_publications = g_hash_table_new_full(
1927 g_str_hash, g_str_equal,
1928 g_free, (GDestroyNotify)free_publication);
1930 g_hash_table_insert(sip->user_state_publications, g_strdup(key), publication);
1931 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
1932 key, version);
1936 /* count each client instance only once */
1937 if (sipe_strequal(name, "device"))
1938 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
1940 if (sipe_is_our_publication(sipe_private, key)) {
1941 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1943 publication->category = g_strdup(name);
1944 publication->instance = instance;
1945 publication->container = container;
1946 publication->version = version;
1948 /* filling publication->availability */
1949 if (sipe_strequal(name, "state")) {
1950 const sipe_xml *xn_state = sipe_xml_child(node, "state");
1951 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
1953 if (xn_avail) {
1954 gchar *avail_str = sipe_xml_data(xn_avail);
1955 if (avail_str) {
1956 publication->availability = atoi(avail_str);
1958 g_free(avail_str);
1960 /* for calendarState */
1961 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
1962 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
1963 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
1965 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
1966 if (xn_activity) {
1967 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
1968 sipe_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
1970 event->is_meeting = TRUE;
1973 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
1974 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
1976 publication->cal_event_hash = sipe_cal_event_hash(event);
1977 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
1978 publication->cal_event_hash);
1979 sipe_cal_event_free(event);
1982 /* filling publication->note */
1983 if (sipe_strequal(name, "note")) {
1984 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
1986 if (!has_note_cleaned) {
1987 has_note_cleaned = TRUE;
1989 g_free(sip->note);
1990 sip->note = NULL;
1991 sip->note_since = publish_time;
1993 do_update_status = TRUE;
1996 g_free(publication->note);
1997 publication->note = NULL;
1998 if (xn_body) {
1999 char *tmp;
2001 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2002 g_free(tmp);
2003 if (publish_time >= sip->note_since) {
2004 g_free(sip->note);
2005 sip->note = g_strdup(publication->note);
2006 sip->note_since = publish_time;
2007 sip->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF");
2009 do_update_status = TRUE;
2014 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2015 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2016 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2017 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2018 if (xn_free_busy) {
2019 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2020 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2022 if (xn_working_hours) {
2023 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2027 if (!cat_publications) {
2028 cat_publications = g_hash_table_new_full(
2029 g_str_hash, g_str_equal,
2030 g_free, (GDestroyNotify)free_publication);
2031 g_hash_table_insert(sip->our_publications, g_strdup(name), cat_publications);
2032 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2034 g_hash_table_insert(cat_publications, g_strdup(key), publication);
2035 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2037 g_free(key);
2039 /* aggregateState (not an our publication) from 2-nd container */
2040 if (sipe_strequal(name, "state") && container == 2) {
2041 const sipe_xml *xn_state = sipe_xml_child(node, "state");
2043 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2044 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2046 if (xn_avail) {
2047 gchar *avail_str = sipe_xml_data(xn_avail);
2048 if (avail_str) {
2049 aggreg_avail = atoi(avail_str);
2051 g_free(avail_str);
2054 do_update_status = TRUE;
2058 /* userProperties published by server from AD */
2059 if (!sip->csta && sipe_strequal(name, "userProperties")) {
2060 const sipe_xml *line;
2061 /* line, for Remote Call Control (RCC) */
2062 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2063 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2064 const gchar *line_type = sipe_xml_attribute(line, "lineType");
2065 gchar *line_uri;
2067 if (!line_server || !(sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual"))) continue;
2069 line_uri = sipe_xml_data(line);
2070 if (line_uri) {
2071 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s", line_uri, line_server);
2072 sip_csta_open(sipe_private, line_uri, line_server);
2074 g_free(line_uri);
2076 break;
2080 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sip->our_publications size=%d",
2081 sip->our_publications ? (int) g_hash_table_size(sip->our_publications) : -1);
2083 /* active clients for user account */
2084 if (g_hash_table_size(devices) > 1) {
2085 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2086 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2087 g_hash_table_size(devices));
2088 } else {
2089 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2090 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2092 g_hash_table_destroy(devices);
2094 /* containers */
2095 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2096 guint id = sipe_xml_int_attribute(node, "id", 0);
2097 struct sipe_container *container = sipe_find_container(sipe_private, id);
2099 if (container) {
2100 sip->containers = g_slist_remove(sip->containers, container);
2101 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2102 sipe_ocs2007_free_container(container);
2104 container = g_new0(struct sipe_container, 1);
2105 container->id = id;
2106 container->version = sipe_xml_int_attribute(node, "version", 0);
2107 sip->containers = g_slist_append(sip->containers, container);
2108 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2110 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2111 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2112 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2113 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2114 container->members = g_slist_append(container->members, member);
2115 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2116 member->type, member->value ? member->value : "");
2120 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sip->access_level_set=%s", sip->access_level_set ? "TRUE" : "FALSE");
2121 if (!sip->access_level_set && sipe_xml_child(xml, "containers")) {
2122 char *container_xmls = NULL;
2123 int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2124 int federatedAL = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2126 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2127 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2128 /* initial set-up to let counterparties see your status */
2129 if (sameEnterpriseAL < 0) {
2130 struct sipe_container *container = sipe_find_container(sipe_private, 200);
2131 guint version = container ? container->version : 0;
2132 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2134 if (federatedAL < 0) {
2135 struct sipe_container *container = sipe_find_container(sipe_private, 100);
2136 guint version = container ? container->version : 0;
2137 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2139 sip->access_level_set = TRUE;
2141 if (container_xmls) {
2142 sipe_send_set_container_members(sipe_private, container_xmls);
2144 g_free(container_xmls);
2147 /* Refresh contacts' blocked status */
2148 sipe_refresh_blocked_status(sipe_private);
2150 /* subscribers */
2151 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2152 const char *user;
2153 const char *acknowledged;
2154 gchar *hdr;
2155 gchar *body;
2157 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2158 if (!user) continue;
2159 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2160 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2161 uri = sip_uri_from_name(user);
2163 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2165 acknowledged= sipe_xml_attribute(node, "acknowledged");
2166 if(sipe_strcase_equal(acknowledged,"false")){
2167 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2168 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2169 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2172 hdr = g_strdup_printf(
2173 "Contact: %s\r\n"
2174 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2176 body = g_strdup_printf(
2177 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2178 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2179 "</setSubscribers>", user);
2181 sip_transport_service(sipe_private,
2183 hdr,
2184 body,
2185 NULL);
2186 g_free(body);
2187 g_free(hdr);
2189 g_free(display_name);
2190 g_free(uri);
2193 g_free(contact);
2194 sipe_xml_free(xml);
2196 /* Publish initial state if not yet.
2197 * Assuming this happens on initial responce to subscription to roaming-self
2198 * so we've already updated our roaming data in full.
2199 * Only for 2007+
2201 if (!sip->initial_state_published) {
2202 send_publish_category_initial(sipe_private);
2203 sipe_groupchat_init(sipe_private);
2204 sip->initial_state_published = TRUE;
2205 /* dalayed run */
2206 sipe_cal_delayed_calendar_update(sipe_private);
2207 do_update_status = FALSE;
2208 } else if (aggreg_avail) {
2210 if (aggreg_avail && aggreg_avail < 18000) { /* not offline */
2211 g_free(sip->status);
2212 sip->status = g_strdup(sipe_get_status_by_availability(aggreg_avail, NULL));
2213 } else {
2214 sipe_set_invisible_status(sipe_private); /* not not let offline status switch us off */
2218 if (do_update_status) {
2219 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: switch to '%s' for the account", sip->status);
2220 sipe_status_and_note(sipe_private, sip->status);
2223 g_free(to);
2227 Local Variables:
2228 mode: c
2229 c-file-style: "bsd"
2230 indent-tabs-mode: t
2231 tab-width: 8
2232 End: