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
38 #include "sipe-common.h"
39 #include "http-conn.h"
42 #include "sip-transport.h"
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
46 #include "sipe-core.h"
47 #include "sipe-core-private.h"
49 #include "sipe-groupchat.h"
51 #include "sipe-ocs2007.h"
52 #include "sipe-schedule.h"
53 #include "sipe-status.h"
54 #include "sipe-utils.h"
59 /** MS-PRES publication */
60 struct sipe_publication
{
65 /** for 'state' category */
67 /** for 'state:calendarState' category */
69 /** for 'note' category */
71 /** for 'calendarData' category; 300(Team) container */
72 char *working_hours_xml_str
;
74 char *free_busy_base64
;
78 * 2007-style Activity and Availability.
82 * Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
84 * The values define the starting point of a range
86 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_ONLINE 3000
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 4500
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_ON_PHONE 6000
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 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_AWAY2 15000
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
94 const gchar
*sipe_ocs2007_status_from_legacy_availability(guint availability
)
98 if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_ONLINE
) {
99 type
= SIPE_ACTIVITY_OFFLINE
;
100 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY
) {
101 type
= SIPE_ACTIVITY_AVAILABLE
;
102 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_ON_PHONE
) {
103 //type = SIPE_ACTIVITY_IDLE;
104 type
= SIPE_ACTIVITY_AVAILABLE
;
105 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY
) {
106 type
= SIPE_ACTIVITY_BUSY
;
107 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_DND
) {
108 //type = SIPE_ACTIVITY_BUSYIDLE;
109 type
= SIPE_ACTIVITY_BUSY
;
110 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB
) {
111 type
= SIPE_ACTIVITY_DND
;
112 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY2
) {
113 type
= SIPE_ACTIVITY_BRB
;
114 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE
) {
115 type
= SIPE_ACTIVITY_AWAY
;
117 type
= SIPE_ACTIVITY_OFFLINE
;
120 return(sipe_backend_activity_to_token(type
));
123 const gchar
*sipe_ocs2007_legacy_activity_description(guint availability
)
125 if ((availability
>= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY
) &&
126 (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_ON_PHONE
)) {
127 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE
));
128 } else if ((availability
>= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY
) &&
129 (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_DND
)) {
130 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE
));
137 * @param sipe_status_id (in)
138 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
139 * requests this token]
141 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
142 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
143 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
144 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
145 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
146 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
147 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
148 guint
sipe_ocs2007_availability_from_status(const gchar
*sipe_status_id
,
149 const gchar
**activity_token
)
154 if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_AWAY
))) {
155 availability
= SIPE_OCS2007_AVAILABILITY_AWAY
;
156 activity
= SIPE_ACTIVITY_AWAY
;
157 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_BRB
))) {
158 availability
= SIPE_OCS2007_AVAILABILITY_BRB
;
159 activity
= SIPE_ACTIVITY_BRB
;
160 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_DND
))) {
161 availability
= SIPE_OCS2007_AVAILABILITY_DND
;
162 activity
= SIPE_ACTIVITY_DND
;
163 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_BUSY
))) {
164 availability
= SIPE_OCS2007_AVAILABILITY_BUSY
;
165 activity
= SIPE_ACTIVITY_BUSY
;
166 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_AVAILABLE
))) {
167 availability
= SIPE_OCS2007_AVAILABILITY_ONLINE
;
168 activity
= SIPE_ACTIVITY_ONLINE
;
169 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_UNSET
))) {
170 availability
= SIPE_OCS2007_AVAILABILITY_UNKNOWN
;
171 activity
= SIPE_ACTIVITY_UNSET
;
173 /* Offline or invisible */
174 availability
= SIPE_OCS2007_AVAILABILITY_OFFLINE
;
175 activity
= SIPE_ACTIVITY_OFFLINE
;
178 if (activity_token
) {
179 *activity_token
= sipe_backend_activity_to_token(activity
);
182 return(availability
);
185 gboolean
sipe_ocs2007_status_is_busy(const gchar
*status_id
)
187 return(SIPE_OCS2007_AVAILABILITY_BUSY
>=
188 sipe_ocs2007_availability_from_status(status_id
, NULL
));
192 gboolean
sipe_ocs2007_availability_is_away2(guint availability
)
194 return(availability
>= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY2
);
197 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
198 const char *publications
);
200 static void free_publication(struct sipe_publication
*publication
)
202 g_free(publication
->category
);
203 g_free(publication
->cal_event_hash
);
204 g_free(publication
->note
);
206 g_free(publication
->working_hours_xml_str
);
207 g_free(publication
->fb_start_str
);
208 g_free(publication
->free_busy_base64
);
213 struct hash_table_delete_payload
{
214 GHashTable
*hash_table
;
218 static void sipe_remove_category_container_publications_cb(const gchar
*name
,
219 struct sipe_publication
*publication
,
220 struct hash_table_delete_payload
*payload
)
222 if (publication
->container
== payload
->container
) {
223 g_hash_table_remove(payload
->hash_table
, name
);
227 static void sipe_remove_category_container_publications(GHashTable
*our_publications
,
228 const gchar
*category
,
231 struct hash_table_delete_payload payload
;
232 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
234 if (!payload
.hash_table
) return;
236 payload
.container
= container
;
237 g_hash_table_foreach(payload
.hash_table
,
238 (GHFunc
)sipe_remove_category_container_publications_cb
,
242 /** MS-PRES container */
243 struct sipe_container
{
249 /** MS-PRES container member */
250 struct sipe_container_member
{
251 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
256 static const guint containers
[] = {32000, 400, 300, 200, 100};
257 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
259 guint
sipe_ocs2007_containers(void)
261 return(CONTAINERS_LEN
);
264 static void free_container_member(struct sipe_container_member
*member
)
268 g_free(member
->type
);
269 g_free(member
->value
);
273 void sipe_ocs2007_free_container(struct sipe_container
*container
)
277 if (!container
) return;
279 entry
= container
->members
;
281 void *data
= entry
->data
;
282 entry
= g_slist_remove(entry
, data
);
283 free_container_member((struct sipe_container_member
*)data
);
288 struct sipe_container
*sipe_ocs2007_create_container(guint index
,
289 const gchar
*member_type
,
290 const gchar
*member_value
,
293 struct sipe_container
*container
= g_new0(struct sipe_container
, 1);
294 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
296 container
->id
= is_group
? (guint
) -1 : containers
[index
];
297 container
->members
= g_slist_append(container
->members
, member
);
298 member
->type
= g_strdup(member_type
);
299 member
->value
= g_strdup(member_value
);
304 void sipe_ocs2007_free(struct sipe_core_private
*sipe_private
)
306 if (sipe_private
->containers
) {
307 GSList
*entry
= sipe_private
->containers
;
309 sipe_ocs2007_free_container((struct sipe_container
*)entry
->data
);
313 g_slist_free(sipe_private
->containers
);
317 * Finds locally stored MS-PRES container member
319 static struct sipe_container_member
*
320 sipe_find_container_member(struct sipe_container
*container
,
324 struct sipe_container_member
*member
;
327 if (container
== NULL
|| type
== NULL
) {
331 entry
= container
->members
;
333 member
= entry
->data
;
334 if (sipe_strcase_equal(member
->type
, type
) &&
335 sipe_strcase_equal(member
->value
, value
))
345 * Finds locally stored MS-PRES container by id
347 static struct sipe_container
*sipe_find_container(struct sipe_core_private
*sipe_private
,
350 GSList
*entry
= sipe_private
->containers
;
352 struct sipe_container
*container
= entry
->data
;
353 if (id
== container
->id
) {
361 static int sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
366 const gchar
*value_mod
= value
;
368 if (!type
) return -1;
370 if (sipe_strequal("user", type
)) {
371 value_mod
= sipe_get_no_sip_uri(value
);
374 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
375 struct sipe_container_member
*member
;
376 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
377 if (!container
) continue;
379 member
= sipe_find_container_member(container
, type
, value_mod
);
380 if (member
) return containers
[i
];
387 * Returns pointer to domain part in provided Email URL
389 * @param email an email URL. Example: first.last@hq.company.com
390 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
392 * Doesn't allocate memory
394 static const gchar
*sipe_get_domain(const gchar
*email
)
398 if (!email
) return NULL
;
400 tmp
= strstr(email
, "@");
402 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
409 /* @TODO: replace with binary search for faster access? */
410 /** source: http://support.microsoft.com/kb/897567 */
411 static const gchar
* const public_domains
[] = {
412 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
413 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
414 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
415 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
416 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
417 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
418 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
419 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
420 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
421 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
422 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
423 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
424 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
428 static gboolean
sipe_is_public_domain(const gchar
*domain
)
431 while (public_domains
[i
]) {
432 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
448 const gchar
*sipe_ocs2007_access_level_name(guint id
)
451 case 32000: return _("Blocked");
452 case 400: return _("Personal");
453 case 300: return _("Team");
454 case 200: return _("Company");
455 case 100: return _("Public");
460 int sipe_ocs2007_container_id(guint index
)
462 return(containers
[index
]);
465 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
466 int sipe_ocs2007_find_access_level(struct sipe_core_private
*sipe_private
,
469 gboolean
*is_group_access
)
471 int container_id
= -1;
473 if (sipe_strequal("user", type
)) {
475 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
477 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
478 if (container_id
>= 0) {
479 if (is_group_access
) *is_group_access
= FALSE
;
483 domain
= sipe_get_domain(no_sip_uri
);
484 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
485 if (container_id
>= 0) {
486 if (is_group_access
) *is_group_access
= TRUE
;
490 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
491 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
492 if (is_group_access
) *is_group_access
= TRUE
;
496 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
497 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
498 if (is_group_access
) *is_group_access
= TRUE
;
502 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
503 if ((container_id
>= 0)) {
504 if (is_group_access
) *is_group_access
= TRUE
;
508 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
509 if (is_group_access
) *is_group_access
= FALSE
;
515 GSList
*sipe_ocs2007_get_access_domains(struct sipe_core_private
*sipe_private
)
517 struct sipe_container
*container
;
518 struct sipe_container_member
*member
;
523 entry
= sipe_private
->containers
;
525 container
= entry
->data
;
527 entry2
= container
->members
;
529 member
= entry2
->data
;
530 if (sipe_strcase_equal(member
->type
, "domain"))
532 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
534 entry2
= entry2
->next
;
541 static void sipe_send_container_members_prepare(const guint container_id
,
542 const guint container_version
,
546 char **container_xmls
)
548 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
551 if (!container_xmls
) return;
553 body
= g_strdup_printf(
554 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
562 if ((*container_xmls
) == NULL
) {
563 *container_xmls
= body
;
565 char *tmp
= *container_xmls
;
567 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
573 static void sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
574 char *container_xmls
)
581 if (!container_xmls
) return;
583 self
= sip_uri_self(sipe_private
);
584 body
= g_strdup_printf(
585 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
587 "</setContainerMembers>",
590 contact
= get_contact(sipe_private
);
591 hdr
= g_strdup_printf("Contact: %s\r\n"
592 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
595 sip_transport_service(sipe_private
,
607 * @param container_id a new access level. If -1 then current access level
608 * is just removed (I.e. the member is removed from all containers).
609 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
610 * @param value a value for member. E.g. SIP URI for "user" member type.
612 void sipe_ocs2007_change_access_level(struct sipe_core_private
*sipe_private
,
613 const int container_id
,
618 int current_container_id
= -1;
619 char *container_xmls
= NULL
;
621 /* for each container: find/delete */
622 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
623 struct sipe_container_member
*member
;
624 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
626 if (!container
) continue;
628 member
= sipe_find_container_member(container
, type
, value
);
630 current_container_id
= containers
[i
];
631 /* delete/publish current access level */
632 if (container_id
< 0 || container_id
!= current_container_id
) {
633 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
634 /* remove member from our cache, to be able to recalculate AL below */
635 container
->members
= g_slist_remove(container
->members
, member
);
636 current_container_id
= -1;
641 /* recalculate AL below */
642 current_container_id
= sipe_ocs2007_find_access_level(sipe_private
, type
, value
, NULL
);
644 /* assign/publish new access level */
645 if (container_id
!= current_container_id
&& container_id
>= 0) {
646 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
647 guint version
= container
? container
->version
: 0;
649 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
652 if (container_xmls
) {
653 sipe_send_set_container_members(sipe_private
, container_xmls
);
655 g_free(container_xmls
);
658 void sipe_ocs2007_change_access_level_from_container(struct sipe_core_private
*sipe_private
,
659 struct sipe_container
*container
)
661 struct sipe_container_member
*member
;
663 if (!container
|| !container
->members
) return;
665 member
= ((struct sipe_container_member
*)container
->members
->data
);
667 if (!member
->type
) return;
669 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
670 container
->id
, member
->type
, member
->value
? member
->value
: "");
672 sipe_ocs2007_change_access_level(sipe_private
,
679 void sipe_ocs2007_change_access_level_for_domain(struct sipe_core_private
*sipe_private
,
683 /* move Blocked first */
684 guint i
= (index
== 4) ? 0 : index
+ 1;
685 guint container_id
= containers
[i
];
687 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_id: domain=%s, container_id=(%d)%d",
688 domain
? domain
: "", index
, container_id
);
690 sipe_ocs2007_change_access_level(sipe_private
,
698 * Schedules process of self status publish
699 * based on own calendar information.
700 * Should be scheduled to the beginning of every
701 * 15 min interval, like:
702 * 13:00, 13:15, 13:30, 13:45, etc.
705 static void schedule_publish_update(struct sipe_core_private
*sipe_private
,
706 time_t calculate_from
)
709 /** start of the beginning of closest 5 min interval. */
710 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
712 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
713 asctime(localtime(&calculate_from
)));
714 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
715 asctime(localtime(&next_start
)));
717 sipe_schedule_seconds(sipe_private
,
718 "<+2007-cal-status>",
720 next_start
- time(NULL
),
721 sipe_ocs2007_presence_publish
,
726 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
727 * @param availability (%d) Ex.: 6500
729 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
730 "<availability>%d</availability>"
732 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
733 * @param token (%s) Ex.: in-a-meeting
734 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
735 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
737 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
738 "<activity token=\"%s\" %s %s></activity>"
740 * Publishes 'calendarState' category.
741 * @param instance (%u) Ex.: 1339299275
742 * @param version (%u) Ex.: 1
743 * @param uri (%s) Ex.: john@contoso.com
744 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
745 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
746 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
747 * @param meeting_subject (%s) Ex.: Customer Meeting
748 * @param meeting_location (%s) Ex.: Conf Room 100
750 * @param instance (%u) Ex.: 1339299275
751 * @param version (%u) Ex.: 1
752 * @param uri (%s) Ex.: john@contoso.com
753 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
754 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
755 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
756 * @param meeting_subject (%s) Ex.: Customer Meeting
757 * @param meeting_location (%s) Ex.: Conf Room 100
759 #define SIPE_PUB_XML_STATE_CALENDAR \
760 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
761 "<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\">"\
764 "<endpointLocation/>"\
765 "<meetingSubject>%s</meetingSubject>"\
766 "<meetingLocation>%s</meetingLocation>"\
769 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
770 "<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\">"\
773 "<endpointLocation/>"\
774 "<meetingSubject>%s</meetingSubject>"\
775 "<meetingLocation>%s</meetingLocation>"\
779 * Publishes to clear 'calendarState' category
780 * @param instance (%u) Ex.: 1251210982
781 * @param version (%u) Ex.: 1
783 #define SIPE_PUB_XML_STATE_CALENDAR_CLEAR \
784 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
785 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
788 * Publishes to clear any category
789 * @param category_name (%s) Ex.: state
790 * @param instance (%u) Ex.: 536870912
791 * @param container (%u) Ex.: 3
792 * @param version (%u) Ex.: 1
793 * @param expireType (%s) Ex.: static
795 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
796 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
799 * Publishes 'note' category.
800 * @param instance (%u) Ex.: 2135971629; 0 for personal
801 * @param container (%u) Ex.: 200
802 * @param version (%u) Ex.: 2
803 * @param type (%s) Ex.: personal or OOF
804 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
805 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
806 * @param body (%s) Ex.: In the office
808 #define SIPE_PUB_XML_NOTE \
809 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
810 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
811 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
816 * Only Busy and OOF calendar event are published.
817 * Different instances are used for that.
819 * Must be g_free'd after use.
821 static gchar
*sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
822 struct sipe_cal_event
*event
,
826 gchar
*start_time_str
;
827 int availability
= 0;
830 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
831 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
832 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
834 /* key is <category><instance><container> */
835 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
836 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
837 struct sipe_publication
*publication_2
=
838 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_2
);
839 struct sipe_publication
*publication_3
=
840 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_3
);
845 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
846 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
847 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
853 (publication_3
->availability
== availability
) &&
854 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
857 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
858 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
859 return NULL
; /* nothing to update */
864 (event
->cal_status
== SIPE_CAL_BUSY
||
865 event
->cal_status
== SIPE_CAL_OOF
))
867 gchar
*availability_xml_str
= NULL
;
868 gchar
*activity_xml_str
= NULL
;
869 gchar
*escaped_subject
= event
->subject
? g_markup_escape_text(event
->subject
, -1) : NULL
;
870 gchar
*escaped_location
= event
->location
? g_markup_escape_text(event
->location
, -1) : NULL
;
872 if (event
->cal_status
== SIPE_CAL_BUSY
) {
873 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
,
874 SIPE_OCS2007_AVAILABILITY_BUSY
);
877 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
878 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
879 sipe_backend_activity_to_token(SIPE_ACTIVITY_IN_MEETING
),
880 "minAvailability=\"6500\"",
881 "maxAvailability=\"8999\"");
882 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
883 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
884 sipe_backend_activity_to_token(SIPE_ACTIVITY_OOF
),
885 "minAvailability=\"12000\"",
888 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
890 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
892 publication_2
? publication_2
->version
: 0,
895 availability_xml_str
? availability_xml_str
: "",
896 activity_xml_str
? activity_xml_str
: "",
897 escaped_subject
? escaped_subject
: "",
898 escaped_location
? escaped_location
: "",
901 publication_3
? publication_3
->version
: 0,
904 availability_xml_str
? availability_xml_str
: "",
905 activity_xml_str
? activity_xml_str
: "",
906 escaped_subject
? escaped_subject
: "",
907 escaped_location
? escaped_location
: ""
909 g_free(escaped_location
);
910 g_free(escaped_subject
);
911 g_free(start_time_str
);
912 g_free(availability_xml_str
);
913 g_free(activity_xml_str
);
916 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
918 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
920 publication_2
? publication_2
->version
: 0,
923 publication_3
? publication_3
->version
: 0
931 * Returns 'note' XML part for publication.
932 * Must be g_free'd after use.
934 * Protocol format for Note is plain text.
936 * @param note a note in Sipe internal HTML format
937 * @param note_type either personal or OOF
939 static gchar
*sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
940 const char *note
, /* html */
941 const char *note_type
,
945 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
946 /* key is <category><instance><container> */
947 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
948 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
949 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
951 struct sipe_publication
*publication_note_200
=
952 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "note"), key_note_200
);
953 struct sipe_publication
*publication_note_300
=
954 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "note"), key_note_300
);
955 struct sipe_publication
*publication_note_400
=
956 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "note"), key_note_400
);
958 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
959 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
960 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
961 char *res
, *tmp1
, *tmp2
, *tmp3
;
962 char *start_time_attr
;
967 g_free(key_note_200
);
968 g_free(key_note_300
);
969 g_free(key_note_400
);
971 /* we even need to republish empty note */
972 if (sipe_strequal(n1
, n2
))
974 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
976 return NULL
; /* nothing to update */
979 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
982 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
986 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
989 publication_note_200
? publication_note_200
->version
: 0,
991 start_time_attr
? start_time_attr
: "",
992 end_time_attr
? end_time_attr
: "",
995 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
998 publication_note_300
? publication_note_300
->version
: 0,
1000 start_time_attr
? start_time_attr
: "",
1001 end_time_attr
? end_time_attr
: "",
1004 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
1007 publication_note_400
? publication_note_400
->version
: 0,
1009 start_time_attr
? start_time_attr
: "",
1010 end_time_attr
? end_time_attr
: "",
1013 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
1017 publication_note_200
? publication_note_200
->version
: 0,
1019 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
1023 publication_note_200
? publication_note_200
->version
: 0,
1025 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
1029 publication_note_200
? publication_note_200
->version
: 0,
1032 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
1034 g_free(start_time_attr
);
1035 g_free(end_time_attr
);
1045 * Publishes 'calendarData' category's WorkingHours.
1047 * @param version (%u) Ex.: 1
1048 * @param email (%s) Ex.: alice@cosmo.local
1049 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1051 * @param version (%u)
1053 * @param version (%u)
1055 * @param working_hours_xml_str (%s)
1057 * @param version (%u)
1059 * @param working_hours_xml_str (%s)
1061 * @param version (%u)
1063 * @param working_hours_xml_str (%s)
1065 * @param version (%u)
1067 #define SIPE_PUB_XML_WORKING_HOURS \
1068 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1069 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1072 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1073 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1075 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1076 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1079 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1080 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1083 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1084 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1087 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1088 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1092 * Returns 'calendarData' XML part with WorkingHours for publication.
1093 * Must be g_free'd after use.
1095 static gchar
*sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
1097 struct sipe_calendar
* cal
= sipe_private
->calendar
;
1099 /* key is <category><instance><container> */
1100 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1101 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1102 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1103 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1104 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1105 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1107 struct sipe_publication
*publication_cal_1
=
1108 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_1
);
1109 struct sipe_publication
*publication_cal_100
=
1110 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_100
);
1111 struct sipe_publication
*publication_cal_200
=
1112 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_200
);
1113 struct sipe_publication
*publication_cal_300
=
1114 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_300
);
1115 struct sipe_publication
*publication_cal_400
=
1116 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_400
);
1117 struct sipe_publication
*publication_cal_32000
=
1118 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_32000
);
1120 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
1121 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
1124 g_free(key_cal_100
);
1125 g_free(key_cal_200
);
1126 g_free(key_cal_300
);
1127 g_free(key_cal_400
);
1128 g_free(key_cal_32000
);
1130 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
1131 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1135 if (sipe_strequal(n1
, n2
))
1137 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1138 return NULL
; /* nothing to update */
1141 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
1143 publication_cal_1
? publication_cal_1
->version
: 0,
1145 cal
->working_hours_xml_str
,
1147 publication_cal_100
? publication_cal_100
->version
: 0,
1149 publication_cal_200
? publication_cal_200
->version
: 0,
1151 cal
->working_hours_xml_str
,
1153 publication_cal_300
? publication_cal_300
->version
: 0,
1155 cal
->working_hours_xml_str
,
1156 /* 400 - Personal */
1157 publication_cal_400
? publication_cal_400
->version
: 0,
1159 cal
->working_hours_xml_str
,
1160 /* 32000 - Blocked */
1161 publication_cal_32000
? publication_cal_32000
->version
: 0
1166 * Publishes 'calendarData' category's FreeBusy.
1168 * @param instance (%u) Ex.: 1300372959
1169 * @param version (%u) Ex.: 1
1171 * @param instance (%u) Ex.: 1300372959
1172 * @param version (%u) Ex.: 1
1174 * @param instance (%u) Ex.: 1300372959
1175 * @param version (%u) Ex.: 1
1176 * @param email (%s) Ex.: alice@cosmo.local
1177 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1178 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1180 * @param instance (%u) Ex.: 1300372959
1181 * @param version (%u) Ex.: 1
1182 * @param email (%s) Ex.: alice@cosmo.local
1183 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1184 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1186 * @param instance (%u) Ex.: 1300372959
1187 * @param version (%u) Ex.: 1
1188 * @param email (%s) Ex.: alice@cosmo.local
1189 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1190 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1192 * @param instance (%u) Ex.: 1300372959
1193 * @param version (%u) Ex.: 1
1195 #define SIPE_PUB_XML_FREE_BUSY \
1196 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1197 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1199 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1200 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1202 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1203 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1204 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1207 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1208 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1209 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1212 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1213 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1214 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1217 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1218 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1222 * Returns 'calendarData' XML part with FreeBusy for publication.
1223 * Must be g_free'd after use.
1225 static gchar
*sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
1227 struct sipe_calendar
* cal
= sipe_private
->calendar
;
1228 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
1230 char *free_busy_base64
;
1231 /* const char *st; */
1232 /* const char *fb; */
1235 /* key is <category><instance><container> */
1236 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
1237 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
1238 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
1239 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
1240 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
1241 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
1243 struct sipe_publication
*publication_cal_1
=
1244 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_1
);
1245 struct sipe_publication
*publication_cal_100
=
1246 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_100
);
1247 struct sipe_publication
*publication_cal_200
=
1248 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_200
);
1249 struct sipe_publication
*publication_cal_300
=
1250 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_300
);
1251 struct sipe_publication
*publication_cal_400
=
1252 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_400
);
1253 struct sipe_publication
*publication_cal_32000
=
1254 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_32000
);
1257 g_free(key_cal_100
);
1258 g_free(key_cal_200
);
1259 g_free(key_cal_300
);
1260 g_free(key_cal_400
);
1261 g_free(key_cal_32000
);
1263 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
1264 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1268 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
1269 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
1271 /* we will rebuplish the same data to refresh publication time,
1272 * so if data from multiple sources, most recent will be choosen
1274 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1275 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1277 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1279 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1280 // g_free(fb_start_str);
1281 // g_free(free_busy_base64);
1282 // return NULL; /* nothing to update */
1285 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
1288 publication_cal_1
? publication_cal_1
->version
: 0,
1291 publication_cal_100
? publication_cal_100
->version
: 0,
1294 publication_cal_200
? publication_cal_200
->version
: 0,
1300 publication_cal_300
? publication_cal_300
->version
: 0,
1304 /* 400 - Personal */
1306 publication_cal_400
? publication_cal_400
->version
: 0,
1310 /* 32000 - Blocked */
1312 publication_cal_32000
? publication_cal_32000
->version
: 0
1315 g_free(fb_start_str
);
1316 g_free(free_busy_base64
);
1322 * Publishes 'device' category.
1323 * @param instance (%u) Ex.: 1938468728
1324 * @param version (%u) Ex.: 1
1325 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1326 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1327 * @param timezone (%s) Ex.: 00:00:00+01:00
1328 * @param machineName (%s) Ex.: BOSTON-OCS07
1330 #define SIPE_PUB_XML_DEVICE \
1331 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1332 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1333 "<capabilities preferred=\"false\" uri=\"%s\">"\
1334 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1335 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1336 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1338 "<timezone>%s</timezone>"\
1339 "<machineName>%s</machineName>"\
1344 * Returns 'device' XML part for publication.
1345 * Must be g_free'd after use.
1347 static gchar
*sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
1351 gchar
*uuid
= get_uuid(sipe_private
);
1352 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
1353 /* key is <category><instance><container> */
1354 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
1355 struct sipe_publication
*publication
=
1356 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "device"), key
);
1360 uri
= sip_uri_self(sipe_private
);
1361 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
1363 publication
? publication
->version
: 0,
1366 "00:00:00+01:00", /* @TODO make timezone real*/
1377 * Publishes 'machineState' category.
1378 * @param instance (%u) Ex.: 926460663
1379 * @param version (%u) Ex.: 22
1380 * @param availability (%d) Ex.: 3500
1381 * @param instance (%u) Ex.: 926460663
1382 * @param version (%u) Ex.: 22
1383 * @param availability (%d) Ex.: 3500
1385 #define SIPE_PUB_XML_STATE_MACHINE \
1386 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1387 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1388 "<availability>%d</availability>"\
1389 "<endpointLocation/>"\
1392 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1393 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1394 "<availability>%d</availability>"\
1395 "<endpointLocation/>"\
1400 * Publishes 'userState' category.
1401 * @param instance (%u) User. Ex.: 536870912
1402 * @param version (%u) User Container 2. Ex.: 22
1403 * @param availability (%d) User Container 2. Ex.: 15500
1404 * @param instance (%u) User. Ex.: 536870912
1405 * @param version (%u) User Container 3.Ex.: 22
1406 * @param availability (%d) User Container 3. Ex.: 15500
1408 #define SIPE_PUB_XML_STATE_USER \
1409 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1410 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1411 "<availability>%d</availability>"\
1412 "<endpointLocation/>"\
1415 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1416 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1417 "<availability>%d</availability>"\
1418 "<endpointLocation/>"\
1423 * A service method - use
1424 * - send_publish_get_category_state_machine and
1425 * - send_publish_get_category_state_user instead.
1426 * Must be g_free'd after use.
1428 static gchar
*sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
1429 gboolean is_user_state
)
1431 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1432 int availability
= sipe_ocs2007_availability_from_status(sip
->status
, NULL
);
1433 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
1434 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
1435 /* key is <category><instance><container> */
1436 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
1437 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
1438 struct sipe_publication
*publication_2
=
1439 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_2
);
1440 struct sipe_publication
*publication_3
=
1441 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_3
);
1446 if (publication_2
&& (publication_2
->availability
== availability
))
1448 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1449 return NULL
; /* nothing to update */
1452 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
1454 publication_2
? publication_2
->version
: 0,
1457 publication_3
? publication_3
->version
: 0,
1462 * Returns 'machineState' XML part for publication.
1463 * Must be g_free'd after use.
1465 static gchar
*sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
1467 return sipe_publish_get_category_state(sipe_private
, FALSE
);
1471 * Returns 'userState' XML part for publication.
1472 * Must be g_free'd after use.
1474 static gchar
*sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
1476 return sipe_publish_get_category_state(sipe_private
, TRUE
);
1479 static void send_publish_category_initial(struct sipe_core_private
*sipe_private
)
1481 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
1483 gchar
*publications
;
1485 sipe_status_set_activity(sipe_private
, SIPE_ACTIVITY_AVAILABLE
);
1487 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
1488 publications
= g_strdup_printf("%s%s",
1490 pub_machine
? pub_machine
: "");
1492 g_free(pub_machine
);
1494 send_presence_publish(sipe_private
, publications
);
1495 g_free(publications
);
1498 static gboolean
process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
1500 struct transaction
*trans
)
1502 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1504 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
1506 const sipe_xml
*node
;
1510 gboolean has_device_publication
= FALSE
;
1512 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1514 /* test if version mismatch fault */
1515 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
1516 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
1517 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
1524 /* accumulating information about faulty versions */
1525 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
1526 for (node
= sipe_xml_child(xml
, "details/operation");
1528 node
= sipe_xml_twin(node
))
1530 const gchar
*index
= sipe_xml_attribute(node
, "index");
1531 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
1533 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
1534 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
1538 /* here we are parsing our own request to figure out what publication
1539 * referenced here only by index went wrong
1541 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
1544 for (node
= sipe_xml_child(xml
, "publications/publication"),
1545 index_our
= 1; /* starts with 1 - our first publication */
1547 node
= sipe_xml_twin(node
), index_our
++)
1549 gchar
*idx
= g_strdup_printf("%d", index_our
);
1550 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
1551 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
1554 if (sipe_strequal("device", categoryName
)) {
1555 has_device_publication
= TRUE
;
1558 if (curVersion
) { /* fault exist on this index */
1559 const gchar
*container
= sipe_xml_attribute(node
, "container");
1560 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
1561 /* key is <category><instance><container> */
1562 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
1563 GHashTable
*category
= g_hash_table_lookup(sipe_private
->our_publications
, categoryName
);
1566 struct sipe_publication
*publication
=
1567 g_hash_table_lookup(category
, key
);
1569 SIPE_DEBUG_INFO("key is %s", key
);
1572 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1573 key
, curVersion
, publication
->version
);
1574 /* updating publication's version to the correct one */
1575 publication
->version
= atoi(curVersion
);
1578 /* We somehow lost this category from our publications... */
1579 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
1580 publication
->category
= g_strdup(categoryName
);
1581 publication
->instance
= atoi(instance
);
1582 publication
->container
= atoi(container
);
1583 publication
->version
= atoi(curVersion
);
1584 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1585 g_free
, (GDestroyNotify
)free_publication
);
1586 g_hash_table_insert(category
, g_strdup(key
), publication
);
1587 g_hash_table_insert(sipe_private
->our_publications
, g_strdup(categoryName
), category
);
1588 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
1594 g_hash_table_destroy(faults
);
1596 /* rebublishing with right versions */
1597 if (has_device_publication
) {
1598 send_publish_category_initial(sipe_private
);
1600 sipe_status_update(sipe_private
, NULL
);
1607 * Publishes categories.
1608 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1609 * @param publications (%s) XML publications
1611 #define SIPE_SEND_PRESENCE \
1612 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1613 "<publications uri=\"%s\">"\
1618 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
1619 const char *publications
)
1626 uri
= sip_uri_self(sipe_private
);
1627 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
1631 tmp
= get_contact(sipe_private
);
1632 hdr
= g_strdup_printf("Contact: %s\r\n"
1633 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
1635 sip_transport_service(sipe_private
,
1639 process_send_presence_category_publish_response
);
1648 * Publishes self status
1649 * based on own calendar information.
1651 void sipe_ocs2007_presence_publish(struct sipe_core_private
*sipe_private
,
1652 SIPE_UNUSED_PARAMETER
void *unused
)
1654 struct sipe_calendar
* cal
= sipe_private
->calendar
;
1655 struct sipe_cal_event
* event
= NULL
;
1656 gchar
*pub_cal_working_hours
= NULL
;
1657 gchar
*pub_cal_free_busy
= NULL
;
1658 gchar
*pub_calendar
= NULL
;
1659 gchar
*pub_calendar2
= NULL
;
1660 gchar
*pub_oof_note
= NULL
;
1661 const gchar
*oof_note
;
1662 time_t oof_start
= 0;
1666 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1670 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1671 if (cal
->cal_events
) {
1672 event
= sipe_cal_get_event(cal
->cal_events
, time(NULL
));
1676 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1678 char *desc
= sipe_cal_event_describe(event
);
1679 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
1685 OOF publish, Busy clean
1687 OOF clean, Busy publish
1689 OOF clean, Busy clean
1691 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
1692 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, cal
->email
, SIPE_CAL_OOF
);
1693 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_BUSY
);
1694 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
1695 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_OOF
);
1696 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, cal
->email
, SIPE_CAL_BUSY
);
1698 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_OOF
);
1699 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_BUSY
);
1702 oof_note
= sipe_ews_get_oof_note(cal
);
1703 if (sipe_strequal("Scheduled", cal
->oof_state
)) {
1704 oof_start
= cal
->oof_start
;
1705 oof_end
= cal
->oof_end
;
1707 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
1709 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
1710 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
1712 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
1713 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1715 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
1716 pub_cal_working_hours
? pub_cal_working_hours
: "",
1717 pub_cal_free_busy
? pub_cal_free_busy
: "",
1718 pub_calendar
? pub_calendar
: "",
1719 pub_calendar2
? pub_calendar2
: "",
1720 pub_oof_note
? pub_oof_note
: "");
1722 send_presence_publish(sipe_private
, publications
);
1723 g_free(publications
);
1726 g_free(pub_cal_working_hours
);
1727 g_free(pub_cal_free_busy
);
1728 g_free(pub_calendar
);
1729 g_free(pub_calendar2
);
1730 g_free(pub_oof_note
);
1732 /* repeat scheduling */
1733 schedule_publish_update(sipe_private
, time(NULL
));
1736 void sipe_ocs2007_category_publish(struct sipe_core_private
*sipe_private
)
1738 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1739 gchar
*pub_state
= sipe_status_changed_by_user(sipe_private
) ?
1740 sipe_publish_get_category_state_user(sipe_private
) :
1741 sipe_publish_get_category_state_machine(sipe_private
);
1742 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
1744 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE
) ? "OOF" : "personal",
1747 gchar
*publications
;
1749 if (!pub_state
&& !pub_note
) {
1750 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1754 publications
= g_strdup_printf("%s%s",
1755 pub_state
? pub_state
: "",
1756 pub_note
? pub_note
: "");
1761 send_presence_publish(sipe_private
, publications
);
1762 g_free(publications
);
1765 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
1769 struct sipe_publication
*publication
= value
;
1771 g_string_append_printf( str
,
1772 SIPE_PUB_XML_PUBLICATION_CLEAR
,
1773 publication
->category
,
1774 publication
->instance
,
1775 publication
->container
,
1776 publication
->version
,
1780 void sipe_ocs2007_reset_status(struct sipe_core_private
*sipe_private
)
1783 gchar
*publications
;
1785 if (!sipe_private
->user_state_publications
|| g_hash_table_size(sipe_private
->user_state_publications
) == 0) {
1786 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1790 str
= g_string_new(NULL
);
1791 g_hash_table_foreach(sipe_private
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
1792 publications
= g_string_free(str
, FALSE
);
1794 send_presence_publish(sipe_private
, publications
);
1795 g_free(publications
);
1798 /* key is <category><instance><container> */
1799 static gboolean
sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
1804 /* filling keys for our publications if not yet cached */
1805 if (!sipe_private
->our_publication_keys
) {
1806 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
1807 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
1808 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
1809 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
1810 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
1811 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
1812 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
1814 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1815 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
1816 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
1817 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
1818 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
1819 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
1820 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
1821 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
1822 SIPE_DEBUG_INFO("\tNote : %u", 0);
1823 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1826 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1827 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
1829 /* state:machineState */
1830 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1831 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
1832 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1833 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
1835 /* state:userState */
1836 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1837 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
1838 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1839 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
1841 /* state:calendarState */
1842 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1843 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
1844 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1845 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
1847 /* state:calendarState OOF */
1848 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1849 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
1850 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1851 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
1854 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1855 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1856 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1857 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1858 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1859 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1862 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1863 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
1864 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1865 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
1866 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1867 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
1869 /* calendarData:WorkingHours */
1870 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1871 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1872 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1873 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1874 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1875 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1876 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1877 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1878 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1879 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1880 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1881 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1883 /* calendarData:FreeBusy */
1884 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1885 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
1886 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1887 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
1888 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1889 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
1890 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1891 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
1892 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1893 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
1894 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1895 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
1897 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
1898 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
1901 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1903 entry
= sipe_private
->our_publication_keys
;
1905 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1906 if (sipe_strequal(entry
->data
, key
)) {
1909 entry
= entry
->next
;
1914 static void sipe_refresh_blocked_status_cb(char *buddy_name
,
1915 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1916 struct sipe_core_private
*sipe_private
)
1918 int container_id
= sipe_ocs2007_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1919 gboolean blocked
= (container_id
== 32000);
1920 gboolean blocked_in_blist
= sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC
, buddy_name
);
1922 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1923 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1925 if (blocked
!= blocked_in_blist
) {
1926 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC
, buddy_name
, blocked
);
1930 static void sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1932 g_hash_table_foreach(sipe_private
->buddies
,
1933 (GHFunc
) sipe_refresh_blocked_status_cb
,
1938 * When we receive some self (BE) NOTIFY with a new subscriber
1939 * we sends a setSubscribers request to him [SIP-PRES] 4.8
1942 void sipe_ocs2007_process_roaming_self(struct sipe_core_private
*sipe_private
,
1945 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1949 const sipe_xml
*node
;
1950 const sipe_xml
*node2
;
1951 char *display_name
= NULL
;
1953 GSList
*category_names
= NULL
;
1954 int aggreg_avail
= 0;
1955 gboolean do_update_status
= FALSE
;
1956 gboolean has_note_cleaned
= FALSE
;
1957 GHashTable
*devices
;
1959 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
1961 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1964 contact
= get_contact(sipe_private
);
1965 to
= sip_uri_self(sipe_private
);
1968 /* set list of categories participating in this XML */
1969 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
1970 const gchar
*name
= sipe_xml_attribute(node
, "name");
1971 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
1973 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
1974 category_names
? (int) g_slist_length(category_names
) : -1);
1975 /* drop category information */
1976 if (category_names
) {
1977 GSList
*entry
= category_names
;
1979 GHashTable
*cat_publications
;
1980 const gchar
*category
= entry
->data
;
1981 entry
= entry
->next
;
1982 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category
);
1983 cat_publications
= g_hash_table_lookup(sipe_private
->our_publications
, category
);
1984 if (cat_publications
) {
1985 g_hash_table_remove(sipe_private
->our_publications
, category
);
1986 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category
);
1990 g_slist_free(category_names
);
1992 /* filling our categories reflected in roaming data */
1993 devices
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1995 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
1997 const gchar
*name
= sipe_xml_attribute(node
, "name");
1998 guint container
= sipe_xml_int_attribute(node
, "container", -1);
1999 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2000 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2001 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2002 sipe_utils_str_to_time(tmp
) : 0;
2004 GHashTable
*cat_publications
= g_hash_table_lookup(sipe_private
->our_publications
, name
);
2006 /* Ex. clear note: <category name="note"/> */
2007 if (container
== (guint
)-1) {
2010 do_update_status
= TRUE
;
2014 /* Ex. clear note: <category name="note" container="200"/> */
2015 if (instance
== (guint
)-1) {
2016 if (container
== 200) {
2019 do_update_status
= TRUE
;
2021 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name
, container
);
2022 sipe_remove_category_container_publications(
2023 sipe_private
->our_publications
, name
, container
);
2027 /* key is <category><instance><container> */
2028 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2029 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key
, version
);
2031 /* capture all userState publication for later clean up if required */
2032 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2033 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2035 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2036 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2037 publication
->category
= g_strdup(name
);
2038 publication
->instance
= instance
;
2039 publication
->container
= container
;
2040 publication
->version
= version
;
2042 if (!sipe_private
->user_state_publications
) {
2043 sipe_private
->user_state_publications
= g_hash_table_new_full(
2044 g_str_hash
, g_str_equal
,
2045 g_free
, (GDestroyNotify
)free_publication
);
2047 g_hash_table_insert(sipe_private
->user_state_publications
, g_strdup(key
), publication
);
2048 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2053 /* count each client instance only once */
2054 if (sipe_strequal(name
, "device"))
2055 g_hash_table_replace(devices
, g_strdup_printf("%u", instance
), NULL
);
2057 if (sipe_is_our_publication(sipe_private
, key
)) {
2058 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2060 publication
->category
= g_strdup(name
);
2061 publication
->instance
= instance
;
2062 publication
->container
= container
;
2063 publication
->version
= version
;
2065 /* filling publication->availability */
2066 if (sipe_strequal(name
, "state")) {
2067 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2068 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2071 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2073 publication
->availability
= atoi(avail_str
);
2077 /* for calendarState */
2078 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2079 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2080 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2082 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2084 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2085 sipe_backend_activity_to_token(SIPE_ACTIVITY_IN_MEETING
)))
2087 event
->is_meeting
= TRUE
;
2090 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2091 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2093 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2094 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2095 publication
->cal_event_hash
);
2096 sipe_cal_event_free(event
);
2099 /* filling publication->note */
2100 if (sipe_strequal(name
, "note")) {
2101 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2103 if (!has_note_cleaned
) {
2104 has_note_cleaned
= TRUE
;
2108 sip
->note_since
= publish_time
;
2110 do_update_status
= TRUE
;
2113 g_free(publication
->note
);
2114 publication
->note
= NULL
;
2118 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2120 if (publish_time
>= sip
->note_since
) {
2122 sip
->note
= g_strdup(publication
->note
);
2123 sip
->note_since
= publish_time
;
2124 if (sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF"))
2125 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE
);
2127 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE
);
2129 do_update_status
= TRUE
;
2134 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2135 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2136 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2137 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2139 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2140 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2142 if (xn_working_hours
) {
2143 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2147 if (!cat_publications
) {
2148 cat_publications
= g_hash_table_new_full(
2149 g_str_hash
, g_str_equal
,
2150 g_free
, (GDestroyNotify
)free_publication
);
2151 g_hash_table_insert(sipe_private
->our_publications
, g_strdup(name
), cat_publications
);
2152 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name
);
2154 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2155 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key
, version
);
2159 /* aggregateState (not an our publication) from 2-nd container */
2160 if (sipe_strequal(name
, "state") && container
== 2) {
2161 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2163 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2164 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2167 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2169 aggreg_avail
= atoi(avail_str
);
2174 do_update_status
= TRUE
;
2178 /* userProperties published by server from AD */
2179 if (!sipe_private
->csta
&&
2180 sipe_strequal(name
, "userProperties")) {
2181 const sipe_xml
*line
;
2182 /* line, for Remote Call Control (RCC) */
2183 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2184 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2185 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2188 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2190 line_uri
= sipe_xml_data(line
);
2192 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2193 sip_csta_open(sipe_private
, line_uri
, line_server
);
2201 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2202 sipe_private
->our_publications
? (int) g_hash_table_size(sipe_private
->our_publications
) : -1);
2204 /* active clients for user account */
2205 if (g_hash_table_size(devices
) > 1) {
2206 SIPE_CORE_PRIVATE_FLAG_SET(MPOP
);
2207 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2208 g_hash_table_size(devices
));
2210 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP
);
2211 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2213 g_hash_table_destroy(devices
);
2216 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2217 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2218 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2221 sipe_private
->containers
= g_slist_remove(sipe_private
->containers
, container
);
2222 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2223 sipe_ocs2007_free_container(container
);
2225 container
= g_new0(struct sipe_container
, 1);
2227 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2228 sipe_private
->containers
= g_slist_append(sipe_private
->containers
, container
);
2229 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2231 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2232 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2233 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2234 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2235 container
->members
= g_slist_append(container
->members
, member
);
2236 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2237 member
->type
, member
->value
? member
->value
: "");
2241 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sip->access_level_set=%s",
2242 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET
) ? "TRUE" : "FALSE");
2243 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET
) && sipe_xml_child(xml
, "containers")) {
2244 char *container_xmls
= NULL
;
2245 int sameEnterpriseAL
= sipe_ocs2007_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2246 int federatedAL
= sipe_ocs2007_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2248 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2249 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL
);
2250 /* initial set-up to let counterparties see your status */
2251 if (sameEnterpriseAL
< 0) {
2252 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2253 guint version
= container
? container
->version
: 0;
2254 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2256 if (federatedAL
< 0) {
2257 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2258 guint version
= container
? container
->version
: 0;
2259 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2261 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET
);
2263 if (container_xmls
) {
2264 sipe_send_set_container_members(sipe_private
, container_xmls
);
2266 g_free(container_xmls
);
2269 /* Refresh contacts' blocked status */
2270 sipe_refresh_blocked_status(sipe_private
);
2273 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2275 const char *acknowledged
;
2279 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2280 if (!user
) continue;
2281 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user
);
2282 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2283 uri
= sip_uri_from_name(user
);
2285 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
2287 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2288 if(sipe_strcase_equal(acknowledged
,"false")){
2289 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user
);
2290 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
)) {
2291 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC
, uri
, display_name
);
2294 hdr
= g_strdup_printf(
2296 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2298 body
= g_strdup_printf(
2299 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2300 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2301 "</setSubscribers>", user
);
2303 sip_transport_service(sipe_private
,
2311 g_free(display_name
);
2318 /* Publish initial state if not yet.
2319 * Assuming this happens on initial responce to subscription to roaming-self
2320 * so we've already updated our roaming data in full.
2323 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH
)) {
2324 send_publish_category_initial(sipe_private
);
2325 sipe_groupchat_init(sipe_private
);
2326 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH
);
2328 sipe_cal_delayed_calendar_update(sipe_private
);
2329 do_update_status
= FALSE
;
2330 } else if (aggreg_avail
) {
2333 (aggreg_avail
< SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE
)) {
2335 sipe_status_set_token(sipe_private
,
2336 sipe_ocs2007_status_from_legacy_availability(aggreg_avail
));
2338 /* do not let offline status switch us off */
2339 sipe_status_set_activity(sipe_private
,
2340 SIPE_ACTIVITY_INVISIBLE
);
2344 if (do_update_status
) {
2345 sipe_status_and_note(sipe_private
, NULL
);