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"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-buddy.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
48 #include "sipe-groupchat.h"
50 #include "sipe-ocs2007.h"
51 #include "sipe-schedule.h"
52 #include "sipe-status.h"
53 #include "sipe-utils.h"
56 /** MS-PRES publication */
57 struct sipe_publication
{
62 /** for 'state' category */
64 /** for 'state:calendarState' category */
66 /** for 'note' category */
68 /** for 'calendarData' category; 300(Team) container */
69 char *working_hours_xml_str
;
71 char *free_busy_base64
;
75 * 2007-style Activity and Availability.
79 * Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
81 * The values define the starting point of a range
83 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_ONLINE 3000
84 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY 4500
85 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_ON_PHONE 6000
86 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY 7500
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND 9000 /* do not disturb */
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB 12000 /* be right back */
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY2 15000
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE 18000
91 const gchar
*sipe_ocs2007_status_from_legacy_availability(guint availability
)
95 if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_ONLINE
) {
96 type
= SIPE_ACTIVITY_OFFLINE
;
97 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY
) {
98 type
= SIPE_ACTIVITY_AVAILABLE
;
99 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_ON_PHONE
) {
100 //type = SIPE_ACTIVITY_IDLE;
101 type
= SIPE_ACTIVITY_AVAILABLE
;
102 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY
) {
103 type
= SIPE_ACTIVITY_BUSY
;
104 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_DND
) {
105 //type = SIPE_ACTIVITY_BUSYIDLE;
106 type
= SIPE_ACTIVITY_BUSY
;
107 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB
) {
108 type
= SIPE_ACTIVITY_DND
;
109 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY2
) {
110 type
= SIPE_ACTIVITY_BRB
;
111 } else if (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE
) {
112 type
= SIPE_ACTIVITY_AWAY
;
114 type
= SIPE_ACTIVITY_OFFLINE
;
117 return(sipe_backend_activity_to_token(type
));
120 const gchar
*sipe_ocs2007_legacy_activity_description(guint availability
)
122 if ((availability
>= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY
) &&
123 (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_ON_PHONE
)) {
124 return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE
));
125 } else if ((availability
>= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY
) &&
126 (availability
< SIPE_OCS2007_LEGACY_AVAILIBILITY_DND
)) {
127 return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE
));
134 * @param sipe_status_id (in)
135 * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
136 * requests this token]
138 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN 0
139 #define SIPE_OCS2007_AVAILABILITY_ONLINE 3500
140 #define SIPE_OCS2007_AVAILABILITY_BUSY 6500
141 #define SIPE_OCS2007_AVAILABILITY_DND 9500 /* do not disturb */
142 #define SIPE_OCS2007_AVAILABILITY_BRB 12500 /* be right back */
143 #define SIPE_OCS2007_AVAILABILITY_AWAY 15500
144 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
145 guint
sipe_ocs2007_availability_from_status(const gchar
*sipe_status_id
,
146 const gchar
**activity_token
)
151 if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_AWAY
))) {
152 availability
= SIPE_OCS2007_AVAILABILITY_AWAY
;
153 activity
= SIPE_ACTIVITY_AWAY
;
154 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_BRB
))) {
155 availability
= SIPE_OCS2007_AVAILABILITY_BRB
;
156 activity
= SIPE_ACTIVITY_BRB
;
157 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_DND
))) {
158 availability
= SIPE_OCS2007_AVAILABILITY_DND
;
159 activity
= SIPE_ACTIVITY_DND
;
160 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_BUSY
))) {
161 availability
= SIPE_OCS2007_AVAILABILITY_BUSY
;
162 activity
= SIPE_ACTIVITY_BUSY
;
163 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_AVAILABLE
))) {
164 availability
= SIPE_OCS2007_AVAILABILITY_ONLINE
;
165 activity
= SIPE_ACTIVITY_ONLINE
;
166 } else if (sipe_strequal(sipe_status_id
, sipe_backend_activity_to_token(SIPE_ACTIVITY_UNSET
))) {
167 availability
= SIPE_OCS2007_AVAILABILITY_UNKNOWN
;
168 activity
= SIPE_ACTIVITY_UNSET
;
170 /* Offline or invisible */
171 availability
= SIPE_OCS2007_AVAILABILITY_OFFLINE
;
172 activity
= SIPE_ACTIVITY_OFFLINE
;
175 if (activity_token
) {
176 *activity_token
= sipe_backend_activity_to_token(activity
);
179 return(availability
);
182 gboolean
sipe_ocs2007_status_is_busy(const gchar
*status_id
)
184 return(SIPE_OCS2007_AVAILABILITY_BUSY
>=
185 sipe_ocs2007_availability_from_status(status_id
, NULL
));
189 gboolean
sipe_ocs2007_availability_is_away2(guint availability
)
191 return(availability
>= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY2
);
194 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
195 const char *publications
);
197 static void free_publication(struct sipe_publication
*publication
)
199 g_free(publication
->category
);
200 g_free(publication
->cal_event_hash
);
201 g_free(publication
->note
);
203 g_free(publication
->working_hours_xml_str
);
204 g_free(publication
->fb_start_str
);
205 g_free(publication
->free_busy_base64
);
210 struct hash_table_delete_payload
{
211 GHashTable
*hash_table
;
215 static void sipe_remove_category_container_publications_cb(const gchar
*name
,
216 struct sipe_publication
*publication
,
217 struct hash_table_delete_payload
*payload
)
219 if (publication
->container
== payload
->container
) {
220 g_hash_table_remove(payload
->hash_table
, name
);
224 static void sipe_remove_category_container_publications(GHashTable
*our_publications
,
225 const gchar
*category
,
228 struct hash_table_delete_payload payload
;
229 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
231 if (!payload
.hash_table
) return;
233 payload
.container
= container
;
234 g_hash_table_foreach(payload
.hash_table
,
235 (GHFunc
)sipe_remove_category_container_publications_cb
,
239 /** MS-PRES container */
240 struct sipe_container
{
246 /** MS-PRES container member */
247 struct sipe_container_member
{
248 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
253 static const guint containers
[] = {32000, 400, 300, 200, 100};
254 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
256 static void free_container_member(struct sipe_container_member
*member
)
260 g_free(member
->type
);
261 g_free(member
->value
);
265 static void sipe_ocs2007_free_container(struct sipe_container
*container
)
269 if (!container
) return;
271 entry
= container
->members
;
273 void *data
= entry
->data
;
274 entry
= g_slist_remove(entry
, data
);
275 free_container_member((struct sipe_container_member
*)data
);
280 void sipe_core_buddy_menu_free(struct sipe_core_public
*sipe_public
)
282 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
283 GSList
*entry
= sipe_private
->blist_menu_containers
;
285 sipe_ocs2007_free_container(entry
->data
);
288 g_slist_free(sipe_private
->blist_menu_containers
);
289 sipe_private
->blist_menu_containers
= NULL
;
292 static void blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
293 struct sipe_container
*container
)
295 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
299 static struct sipe_container
*create_container(guint index
,
300 const gchar
*member_type
,
301 const gchar
*member_value
,
304 struct sipe_container
*container
= g_new0(struct sipe_container
, 1);
305 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
307 container
->id
= is_group
? (guint
) -1 : containers
[index
];
308 container
->members
= g_slist_append(container
->members
, member
);
309 member
->type
= g_strdup(member_type
);
310 member
->value
= g_strdup(member_value
);
315 void sipe_ocs2007_free(struct sipe_core_private
*sipe_private
)
317 if (sipe_private
->containers
) {
318 GSList
*entry
= sipe_private
->containers
;
320 sipe_ocs2007_free_container((struct sipe_container
*)entry
->data
);
324 g_slist_free(sipe_private
->containers
);
328 * Finds locally stored MS-PRES container member
330 static struct sipe_container_member
*
331 sipe_find_container_member(struct sipe_container
*container
,
335 struct sipe_container_member
*member
;
338 if (container
== NULL
|| type
== NULL
) {
342 entry
= container
->members
;
344 member
= entry
->data
;
345 if (sipe_strcase_equal(member
->type
, type
) &&
346 sipe_strcase_equal(member
->value
, value
))
356 * Finds locally stored MS-PRES container by id
358 static struct sipe_container
*sipe_find_container(struct sipe_core_private
*sipe_private
,
361 GSList
*entry
= sipe_private
->containers
;
363 struct sipe_container
*container
= entry
->data
;
364 if (id
== container
->id
) {
372 static int sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
377 const gchar
*value_mod
= value
;
379 if (!type
) return -1;
381 if (sipe_strequal("user", type
)) {
382 value_mod
= sipe_get_no_sip_uri(value
);
385 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
386 struct sipe_container_member
*member
;
387 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
388 if (!container
) continue;
390 member
= sipe_find_container_member(container
, type
, value_mod
);
391 if (member
) return containers
[i
];
398 * Returns pointer to domain part in provided Email URL
400 * @param email an email URL. Example: first.last@hq.company.com
401 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
403 * Doesn't allocate memory
405 static const gchar
*sipe_get_domain(const gchar
*email
)
409 if (!email
) return NULL
;
411 tmp
= strstr(email
, "@");
413 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
420 /* @TODO: replace with binary search for faster access? */
421 /** source: http://support.microsoft.com/kb/897567 */
422 static const gchar
* const public_domains
[] = {
423 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
424 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
425 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
426 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
427 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
428 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
429 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
430 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
431 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
432 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
433 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
434 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
435 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
439 static gboolean
sipe_is_public_domain(const gchar
*domain
)
442 while (public_domains
[i
]) {
443 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
459 const gchar
*sipe_ocs2007_access_level_name(guint id
)
462 case 32000: return _("Blocked");
463 case 400: return _("Personal");
464 case 300: return _("Team");
465 case 200: return _("Company");
466 case 100: return _("Public");
471 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
472 int sipe_ocs2007_find_access_level(struct sipe_core_private
*sipe_private
,
475 gboolean
*is_group_access
)
477 int container_id
= -1;
479 if (sipe_strequal("user", type
)) {
481 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
483 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
484 if (container_id
>= 0) {
485 if (is_group_access
) *is_group_access
= FALSE
;
489 domain
= sipe_get_domain(no_sip_uri
);
490 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
491 if (container_id
>= 0) {
492 if (is_group_access
) *is_group_access
= TRUE
;
496 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
497 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
498 if (is_group_access
) *is_group_access
= TRUE
;
502 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
503 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
504 if (is_group_access
) *is_group_access
= TRUE
;
508 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
509 if ((container_id
>= 0)) {
510 if (is_group_access
) *is_group_access
= TRUE
;
514 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
515 if (is_group_access
) *is_group_access
= FALSE
;
521 static GSList
*get_access_domains(struct sipe_core_private
*sipe_private
)
523 struct sipe_container
*container
;
524 struct sipe_container_member
*member
;
529 entry
= sipe_private
->containers
;
531 container
= entry
->data
;
533 entry2
= container
->members
;
535 member
= entry2
->data
;
536 if (sipe_strcase_equal(member
->type
, "domain"))
538 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
540 entry2
= entry2
->next
;
547 static void sipe_send_container_members_prepare(const guint container_id
,
548 const guint container_version
,
552 char **container_xmls
)
554 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
557 if (!container_xmls
) return;
559 body
= g_strdup_printf(
560 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
568 if ((*container_xmls
) == NULL
) {
569 *container_xmls
= body
;
571 char *tmp
= *container_xmls
;
573 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
579 static void sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
580 char *container_xmls
)
587 if (!container_xmls
) return;
589 self
= sip_uri_self(sipe_private
);
590 body
= g_strdup_printf(
591 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
593 "</setContainerMembers>",
596 contact
= get_contact(sipe_private
);
597 hdr
= g_strdup_printf("Contact: %s\r\n"
598 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
601 sip_transport_service(sipe_private
,
613 * @param container_id a new access level. If -1 then current access level
614 * is just removed (I.e. the member is removed from all containers).
615 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
616 * @param value a value for member. E.g. SIP URI for "user" member type.
618 void sipe_ocs2007_change_access_level(struct sipe_core_private
*sipe_private
,
619 const int container_id
,
624 int current_container_id
= -1;
625 char *container_xmls
= NULL
;
627 /* for each container: find/delete */
628 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
629 struct sipe_container_member
*member
;
630 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
632 if (!container
) continue;
634 member
= sipe_find_container_member(container
, type
, value
);
636 current_container_id
= containers
[i
];
637 /* delete/publish current access level */
638 if (container_id
< 0 || container_id
!= current_container_id
) {
639 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
640 /* remove member from our cache, to be able to recalculate AL below */
641 container
->members
= g_slist_remove(container
->members
, member
);
642 current_container_id
= -1;
647 /* recalculate AL below */
648 current_container_id
= sipe_ocs2007_find_access_level(sipe_private
, type
, value
, NULL
);
650 /* assign/publish new access level */
651 if (container_id
!= current_container_id
&& container_id
>= 0) {
652 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
653 guint version
= container
? container
->version
: 0;
655 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
658 if (container_xmls
) {
659 sipe_send_set_container_members(sipe_private
, container_xmls
);
661 g_free(container_xmls
);
664 void sipe_core_change_access_level_from_container(struct sipe_core_public
*sipe_public
,
667 struct sipe_container
*container
= parameter
;
668 struct sipe_container_member
*member
;
670 if (!container
|| !container
->members
) return;
672 member
= ((struct sipe_container_member
*)container
->members
->data
);
674 if (!member
->type
) return;
676 SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
677 container
->id
, member
->type
, member
->value
? member
->value
: "");
679 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE
,
686 void sipe_core_change_access_level_for_domain(struct sipe_core_public
*sipe_public
,
690 /* move Blocked first */
691 guint i
= (index
== 4) ? 0 : index
+ 1;
692 guint container_id
= containers
[i
];
694 SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
695 domain
? domain
: "", index
, container_id
);
697 sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE
,
704 * Schedules process of self status publish
705 * based on own calendar information.
706 * Should be scheduled to the beginning of every
707 * 15 min interval, like:
708 * 13:00, 13:15, 13:30, 13:45, etc.
711 static void schedule_publish_update(struct sipe_core_private
*sipe_private
,
712 time_t calculate_from
)
715 /** start of the beginning of closest 5 min interval. */
716 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
718 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
719 asctime(localtime(&calculate_from
)));
720 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
721 asctime(localtime(&next_start
)));
723 sipe_schedule_seconds(sipe_private
,
724 "<+2007-cal-status>",
726 next_start
- time(NULL
),
727 sipe_ocs2007_presence_publish
,
732 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
733 * @param availability (%d) Ex.: 6500
735 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
736 "<availability>%d</availability>"
738 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
739 * @param token (%s) Ex.: in-a-meeting
740 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
741 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
743 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
744 "<activity token=\"%s\" %s %s></activity>"
746 * Publishes 'calendarState' category.
747 * @param instance (%u) Ex.: 1339299275
748 * @param version (%u) Ex.: 1
749 * @param uri (%s) Ex.: john@contoso.com
750 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
751 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
752 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
753 * @param meeting_subject (%s) Ex.: Customer Meeting
754 * @param meeting_location (%s) Ex.: Conf Room 100
756 * @param instance (%u) Ex.: 1339299275
757 * @param version (%u) Ex.: 1
758 * @param uri (%s) Ex.: john@contoso.com
759 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
760 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
761 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
762 * @param meeting_subject (%s) Ex.: Customer Meeting
763 * @param meeting_location (%s) Ex.: Conf Room 100
765 #define SIPE_PUB_XML_STATE_CALENDAR \
766 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
767 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
770 "<endpointLocation/>"\
771 "<meetingSubject>%s</meetingSubject>"\
772 "<meetingLocation>%s</meetingLocation>"\
775 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
776 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
779 "<endpointLocation/>"\
780 "<meetingSubject>%s</meetingSubject>"\
781 "<meetingLocation>%s</meetingLocation>"\
785 * Publishes to clear 'calendarState' category
786 * @param instance (%u) Ex.: 1251210982
787 * @param version (%u) Ex.: 1
789 #define SIPE_PUB_XML_STATE_CALENDAR_CLEAR \
790 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
791 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
794 * Publishes to clear any category
795 * @param category_name (%s) Ex.: state
796 * @param instance (%u) Ex.: 536870912
797 * @param container (%u) Ex.: 3
798 * @param version (%u) Ex.: 1
799 * @param expireType (%s) Ex.: static
801 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
802 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
805 * Publishes 'note' category.
806 * @param instance (%u) Ex.: 2135971629; 0 for personal
807 * @param container (%u) Ex.: 200
808 * @param version (%u) Ex.: 2
809 * @param type (%s) Ex.: personal or OOF
810 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
811 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
812 * @param body (%s) Ex.: In the office
814 #define SIPE_PUB_XML_NOTE \
815 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
816 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
817 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
822 * Only Busy and OOF calendar event are published.
823 * Different instances are used for that.
825 * Must be g_free'd after use.
827 static gchar
*sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
828 struct sipe_cal_event
*event
,
832 gchar
*start_time_str
;
833 int availability
= 0;
836 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
837 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
838 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
840 /* key is <category><instance><container> */
841 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
842 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
843 struct sipe_publication
*publication_2
=
844 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_2
);
845 struct sipe_publication
*publication_3
=
846 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_3
);
851 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
852 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
853 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
859 (publication_3
->availability
== availability
) &&
860 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
863 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
864 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
865 return NULL
; /* nothing to update */
870 (event
->cal_status
== SIPE_CAL_BUSY
||
871 event
->cal_status
== SIPE_CAL_OOF
))
873 gchar
*availability_xml_str
= NULL
;
874 gchar
*activity_xml_str
= NULL
;
875 gchar
*escaped_subject
= event
->subject
? g_markup_escape_text(event
->subject
, -1) : NULL
;
876 gchar
*escaped_location
= event
->location
? g_markup_escape_text(event
->location
, -1) : NULL
;
878 if (event
->cal_status
== SIPE_CAL_BUSY
) {
879 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
,
880 SIPE_OCS2007_AVAILABILITY_BUSY
);
883 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
884 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
885 sipe_backend_activity_to_token(SIPE_ACTIVITY_IN_MEETING
),
886 "minAvailability=\"6500\"",
887 "maxAvailability=\"8999\"");
888 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
889 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
890 sipe_backend_activity_to_token(SIPE_ACTIVITY_OOF
),
891 "minAvailability=\"12000\"",
894 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
896 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
898 publication_2
? publication_2
->version
: 0,
901 availability_xml_str
? availability_xml_str
: "",
902 activity_xml_str
? activity_xml_str
: "",
903 escaped_subject
? escaped_subject
: "",
904 escaped_location
? escaped_location
: "",
907 publication_3
? publication_3
->version
: 0,
910 availability_xml_str
? availability_xml_str
: "",
911 activity_xml_str
? activity_xml_str
: "",
912 escaped_subject
? escaped_subject
: "",
913 escaped_location
? escaped_location
: ""
915 g_free(escaped_location
);
916 g_free(escaped_subject
);
917 g_free(start_time_str
);
918 g_free(availability_xml_str
);
919 g_free(activity_xml_str
);
922 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
924 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
926 publication_2
? publication_2
->version
: 0,
929 publication_3
? publication_3
->version
: 0
937 * Returns 'note' XML part for publication.
938 * Must be g_free'd after use.
940 * Protocol format for Note is plain text.
942 * @param note a note in Sipe internal HTML format
943 * @param note_type either personal or OOF
945 static gchar
*sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
946 const char *note
, /* html */
947 const char *note_type
,
951 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
952 /* key is <category><instance><container> */
953 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
954 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
955 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
957 struct sipe_publication
*publication_note_200
=
958 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "note"), key_note_200
);
959 struct sipe_publication
*publication_note_300
=
960 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "note"), key_note_300
);
961 struct sipe_publication
*publication_note_400
=
962 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "note"), key_note_400
);
964 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
965 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
966 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
967 char *res
, *tmp1
, *tmp2
, *tmp3
;
968 char *start_time_attr
;
973 g_free(key_note_200
);
974 g_free(key_note_300
);
975 g_free(key_note_400
);
977 /* we even need to republish empty note */
978 if (sipe_strequal(n1
, n2
))
980 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
982 return NULL
; /* nothing to update */
985 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
988 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
992 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
995 publication_note_200
? publication_note_200
->version
: 0,
997 start_time_attr
? start_time_attr
: "",
998 end_time_attr
? end_time_attr
: "",
1001 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
1004 publication_note_300
? publication_note_300
->version
: 0,
1006 start_time_attr
? start_time_attr
: "",
1007 end_time_attr
? end_time_attr
: "",
1010 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
1013 publication_note_400
? publication_note_400
->version
: 0,
1015 start_time_attr
? start_time_attr
: "",
1016 end_time_attr
? end_time_attr
: "",
1019 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
1023 publication_note_200
? publication_note_200
->version
: 0,
1025 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
1029 publication_note_200
? publication_note_200
->version
: 0,
1031 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
1035 publication_note_200
? publication_note_200
->version
: 0,
1038 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
1040 g_free(start_time_attr
);
1041 g_free(end_time_attr
);
1051 * Publishes 'calendarData' category's WorkingHours.
1053 * @param version (%u) Ex.: 1
1054 * @param email (%s) Ex.: alice@cosmo.local
1055 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
1057 * @param version (%u)
1059 * @param version (%u)
1061 * @param working_hours_xml_str (%s)
1063 * @param version (%u)
1065 * @param working_hours_xml_str (%s)
1067 * @param version (%u)
1069 * @param working_hours_xml_str (%s)
1071 * @param version (%u)
1073 #define SIPE_PUB_XML_WORKING_HOURS \
1074 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1075 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1078 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1079 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1081 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1082 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1085 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1086 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1089 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1090 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1093 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1094 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1098 * Returns 'calendarData' XML part with WorkingHours for publication.
1099 * Must be g_free'd after use.
1101 static gchar
*sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
1103 struct sipe_calendar
* cal
= sipe_private
->calendar
;
1105 /* key is <category><instance><container> */
1106 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1107 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1108 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1109 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1110 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1111 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1113 struct sipe_publication
*publication_cal_1
=
1114 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_1
);
1115 struct sipe_publication
*publication_cal_100
=
1116 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_100
);
1117 struct sipe_publication
*publication_cal_200
=
1118 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_200
);
1119 struct sipe_publication
*publication_cal_300
=
1120 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_300
);
1121 struct sipe_publication
*publication_cal_400
=
1122 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_400
);
1123 struct sipe_publication
*publication_cal_32000
=
1124 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_32000
);
1126 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
1127 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
1130 g_free(key_cal_100
);
1131 g_free(key_cal_200
);
1132 g_free(key_cal_300
);
1133 g_free(key_cal_400
);
1134 g_free(key_cal_32000
);
1136 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
1137 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1141 if (sipe_strequal(n1
, n2
))
1143 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1144 return NULL
; /* nothing to update */
1147 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
1149 publication_cal_1
? publication_cal_1
->version
: 0,
1151 cal
->working_hours_xml_str
,
1153 publication_cal_100
? publication_cal_100
->version
: 0,
1155 publication_cal_200
? publication_cal_200
->version
: 0,
1157 cal
->working_hours_xml_str
,
1159 publication_cal_300
? publication_cal_300
->version
: 0,
1161 cal
->working_hours_xml_str
,
1162 /* 400 - Personal */
1163 publication_cal_400
? publication_cal_400
->version
: 0,
1165 cal
->working_hours_xml_str
,
1166 /* 32000 - Blocked */
1167 publication_cal_32000
? publication_cal_32000
->version
: 0
1172 * Publishes 'calendarData' category's FreeBusy.
1174 * @param instance (%u) Ex.: 1300372959
1175 * @param version (%u) Ex.: 1
1177 * @param instance (%u) Ex.: 1300372959
1178 * @param version (%u) Ex.: 1
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
1194 * @param email (%s) Ex.: alice@cosmo.local
1195 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
1196 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1198 * @param instance (%u) Ex.: 1300372959
1199 * @param version (%u) Ex.: 1
1201 #define SIPE_PUB_XML_FREE_BUSY \
1202 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1203 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1205 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1206 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1208 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1209 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1210 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1213 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1214 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1215 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1218 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1219 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1220 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1223 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1224 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1228 * Returns 'calendarData' XML part with FreeBusy for publication.
1229 * Must be g_free'd after use.
1231 static gchar
*sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
1233 struct sipe_calendar
* cal
= sipe_private
->calendar
;
1234 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
1236 char *free_busy_base64
;
1237 /* const char *st; */
1238 /* const char *fb; */
1241 /* key is <category><instance><container> */
1242 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
1243 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
1244 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
1245 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
1246 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
1247 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
1249 struct sipe_publication
*publication_cal_1
=
1250 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_1
);
1251 struct sipe_publication
*publication_cal_100
=
1252 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_100
);
1253 struct sipe_publication
*publication_cal_200
=
1254 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_200
);
1255 struct sipe_publication
*publication_cal_300
=
1256 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_300
);
1257 struct sipe_publication
*publication_cal_400
=
1258 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_400
);
1259 struct sipe_publication
*publication_cal_32000
=
1260 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "calendarData"), key_cal_32000
);
1263 g_free(key_cal_100
);
1264 g_free(key_cal_200
);
1265 g_free(key_cal_300
);
1266 g_free(key_cal_400
);
1267 g_free(key_cal_32000
);
1269 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
1270 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1274 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
1275 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
1277 /* we will rebuplish the same data to refresh publication time,
1278 * so if data from multiple sources, most recent will be choosen
1280 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1281 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1283 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1285 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1286 // g_free(fb_start_str);
1287 // g_free(free_busy_base64);
1288 // return NULL; /* nothing to update */
1291 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
1294 publication_cal_1
? publication_cal_1
->version
: 0,
1297 publication_cal_100
? publication_cal_100
->version
: 0,
1300 publication_cal_200
? publication_cal_200
->version
: 0,
1306 publication_cal_300
? publication_cal_300
->version
: 0,
1310 /* 400 - Personal */
1312 publication_cal_400
? publication_cal_400
->version
: 0,
1316 /* 32000 - Blocked */
1318 publication_cal_32000
? publication_cal_32000
->version
: 0
1321 g_free(fb_start_str
);
1322 g_free(free_busy_base64
);
1328 * Publishes 'device' category.
1329 * @param instance (%u) Ex.: 1938468728
1330 * @param version (%u) Ex.: 1
1331 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1332 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1333 * @param timezone (%s) Ex.: 00:00:00+01:00
1334 * @param machineName (%s) Ex.: BOSTON-OCS07
1336 #define SIPE_PUB_XML_DEVICE \
1337 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1338 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1339 "<capabilities preferred=\"false\" uri=\"%s\">"\
1340 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1341 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1342 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1344 "<timezone>%s</timezone>"\
1345 "<machineName>%s</machineName>"\
1350 * Returns 'device' XML part for publication.
1351 * Must be g_free'd after use.
1353 static gchar
*sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
1357 gchar
*uuid
= get_uuid(sipe_private
);
1358 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
1359 /* key is <category><instance><container> */
1360 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
1361 struct sipe_publication
*publication
=
1362 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "device"), key
);
1366 uri
= sip_uri_self(sipe_private
);
1367 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
1369 publication
? publication
->version
: 0,
1372 "00:00:00+01:00", /* @TODO make timezone real*/
1383 * Publishes 'machineState' category.
1384 * @param instance (%u) Ex.: 926460663
1385 * @param version (%u) Ex.: 22
1386 * @param availability (%d) Ex.: 3500
1387 * @param instance (%u) Ex.: 926460663
1388 * @param version (%u) Ex.: 22
1389 * @param availability (%d) Ex.: 3500
1391 #define SIPE_PUB_XML_STATE_MACHINE \
1392 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" 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/>"\
1398 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1399 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1400 "<availability>%d</availability>"\
1401 "<endpointLocation/>"\
1406 * Publishes 'userState' category.
1407 * @param instance (%u) User. Ex.: 536870912
1408 * @param version (%u) User Container 2. Ex.: 22
1409 * @param availability (%d) User Container 2. Ex.: 15500
1410 * @param instance (%u) User. Ex.: 536870912
1411 * @param version (%u) User Container 3.Ex.: 22
1412 * @param availability (%d) User Container 3. Ex.: 15500
1414 #define SIPE_PUB_XML_STATE_USER \
1415 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" 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/>"\
1421 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1422 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1423 "<availability>%d</availability>"\
1424 "<endpointLocation/>"\
1429 * A service method - use
1430 * - send_publish_get_category_state_machine and
1431 * - send_publish_get_category_state_user instead.
1432 * Must be g_free'd after use.
1434 static gchar
*sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
1435 gboolean is_user_state
)
1437 int availability
= sipe_ocs2007_availability_from_status(sipe_private
->status
, NULL
);
1438 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
1439 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
1440 /* key is <category><instance><container> */
1441 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
1442 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
1443 struct sipe_publication
*publication_2
=
1444 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_2
);
1445 struct sipe_publication
*publication_3
=
1446 g_hash_table_lookup(g_hash_table_lookup(sipe_private
->our_publications
, "state"), key_3
);
1451 if (publication_2
&& (publication_2
->availability
== availability
))
1453 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1454 return NULL
; /* nothing to update */
1457 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
1459 publication_2
? publication_2
->version
: 0,
1462 publication_3
? publication_3
->version
: 0,
1467 * Returns 'machineState' XML part for publication.
1468 * Must be g_free'd after use.
1470 static gchar
*sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
1472 return sipe_publish_get_category_state(sipe_private
, FALSE
);
1476 * Returns 'userState' XML part for publication.
1477 * Must be g_free'd after use.
1479 static gchar
*sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
1481 return sipe_publish_get_category_state(sipe_private
, TRUE
);
1484 static void send_publish_category_initial(struct sipe_core_private
*sipe_private
)
1486 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
1488 gchar
*publications
;
1490 sipe_status_set_activity(sipe_private
, SIPE_ACTIVITY_AVAILABLE
);
1492 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
1493 publications
= g_strdup_printf("%s%s",
1495 pub_machine
? pub_machine
: "");
1497 g_free(pub_machine
);
1499 send_presence_publish(sipe_private
, publications
);
1500 g_free(publications
);
1503 static gboolean
process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
1505 struct transaction
*trans
)
1507 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1509 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
1511 const sipe_xml
*node
;
1515 gboolean has_device_publication
= FALSE
;
1517 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1519 /* test if version mismatch fault */
1520 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
1521 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
1522 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
1529 /* accumulating information about faulty versions */
1530 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
1531 for (node
= sipe_xml_child(xml
, "details/operation");
1533 node
= sipe_xml_twin(node
))
1535 const gchar
*index
= sipe_xml_attribute(node
, "index");
1536 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
1538 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
1539 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
1543 /* here we are parsing our own request to figure out what publication
1544 * referenced here only by index went wrong
1546 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
1549 for (node
= sipe_xml_child(xml
, "publications/publication"),
1550 index_our
= 1; /* starts with 1 - our first publication */
1552 node
= sipe_xml_twin(node
), index_our
++)
1554 gchar
*idx
= g_strdup_printf("%d", index_our
);
1555 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
1556 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
1559 if (sipe_strequal("device", categoryName
)) {
1560 has_device_publication
= TRUE
;
1563 if (curVersion
) { /* fault exist on this index */
1564 const gchar
*container
= sipe_xml_attribute(node
, "container");
1565 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
1566 /* key is <category><instance><container> */
1567 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
1568 GHashTable
*category
= g_hash_table_lookup(sipe_private
->our_publications
, categoryName
);
1571 struct sipe_publication
*publication
=
1572 g_hash_table_lookup(category
, key
);
1574 SIPE_DEBUG_INFO("key is %s", key
);
1577 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1578 key
, curVersion
, publication
->version
);
1579 /* updating publication's version to the correct one */
1580 publication
->version
= atoi(curVersion
);
1583 /* We somehow lost this category from our publications... */
1584 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
1585 publication
->category
= g_strdup(categoryName
);
1586 publication
->instance
= atoi(instance
);
1587 publication
->container
= atoi(container
);
1588 publication
->version
= atoi(curVersion
);
1589 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1590 g_free
, (GDestroyNotify
)free_publication
);
1591 g_hash_table_insert(category
, g_strdup(key
), publication
);
1592 g_hash_table_insert(sipe_private
->our_publications
, g_strdup(categoryName
), category
);
1593 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
1599 g_hash_table_destroy(faults
);
1601 /* rebublishing with right versions */
1602 if (has_device_publication
) {
1603 send_publish_category_initial(sipe_private
);
1605 sipe_status_update(sipe_private
, NULL
);
1612 * Publishes categories.
1613 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
1614 * @param publications (%s) XML publications
1616 #define SIPE_SEND_PRESENCE \
1617 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1618 "<publications uri=\"%s\">"\
1623 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
1624 const char *publications
)
1631 uri
= sip_uri_self(sipe_private
);
1632 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
1636 tmp
= get_contact(sipe_private
);
1637 hdr
= g_strdup_printf("Contact: %s\r\n"
1638 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
1640 sip_transport_service(sipe_private
,
1644 process_send_presence_category_publish_response
);
1653 * Publishes self status
1654 * based on own calendar information.
1656 void sipe_ocs2007_presence_publish(struct sipe_core_private
*sipe_private
,
1657 SIPE_UNUSED_PARAMETER
void *unused
)
1659 struct sipe_calendar
* cal
= sipe_private
->calendar
;
1660 struct sipe_cal_event
* event
= NULL
;
1661 gchar
*pub_cal_working_hours
= NULL
;
1662 gchar
*pub_cal_free_busy
= NULL
;
1663 gchar
*pub_calendar
= NULL
;
1664 gchar
*pub_calendar2
= NULL
;
1665 gchar
*pub_oof_note
= NULL
;
1666 const gchar
*oof_note
;
1667 time_t oof_start
= 0;
1671 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1675 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1676 if (cal
->cal_events
) {
1677 event
= sipe_cal_get_event(cal
->cal_events
, time(NULL
));
1681 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1683 char *desc
= sipe_cal_event_describe(event
);
1684 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
1690 OOF publish, Busy clean
1692 OOF clean, Busy publish
1694 OOF clean, Busy clean
1696 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
1697 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, cal
->email
, SIPE_CAL_OOF
);
1698 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_BUSY
);
1699 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
1700 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_OOF
);
1701 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, cal
->email
, SIPE_CAL_BUSY
);
1703 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_OOF
);
1704 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, cal
->email
, SIPE_CAL_BUSY
);
1707 oof_note
= sipe_ews_get_oof_note(cal
);
1708 if (sipe_strequal("Scheduled", cal
->oof_state
)) {
1709 oof_start
= cal
->oof_start
;
1710 oof_end
= cal
->oof_end
;
1712 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
1714 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
1715 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
1717 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
1718 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1720 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
1721 pub_cal_working_hours
? pub_cal_working_hours
: "",
1722 pub_cal_free_busy
? pub_cal_free_busy
: "",
1723 pub_calendar
? pub_calendar
: "",
1724 pub_calendar2
? pub_calendar2
: "",
1725 pub_oof_note
? pub_oof_note
: "");
1727 send_presence_publish(sipe_private
, publications
);
1728 g_free(publications
);
1731 g_free(pub_cal_working_hours
);
1732 g_free(pub_cal_free_busy
);
1733 g_free(pub_calendar
);
1734 g_free(pub_calendar2
);
1735 g_free(pub_oof_note
);
1737 /* repeat scheduling */
1738 schedule_publish_update(sipe_private
, time(NULL
));
1741 void sipe_ocs2007_category_publish(struct sipe_core_private
*sipe_private
)
1743 gchar
*pub_state
= sipe_status_changed_by_user(sipe_private
) ?
1744 sipe_publish_get_category_state_user(sipe_private
) :
1745 sipe_publish_get_category_state_machine(sipe_private
);
1746 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
1748 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE
) ? "OOF" : "personal",
1751 gchar
*publications
;
1753 if (!pub_state
&& !pub_note
) {
1754 SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1758 publications
= g_strdup_printf("%s%s",
1759 pub_state
? pub_state
: "",
1760 pub_note
? pub_note
: "");
1765 send_presence_publish(sipe_private
, publications
);
1766 g_free(publications
);
1769 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
1773 struct sipe_publication
*publication
= value
;
1775 g_string_append_printf( str
,
1776 SIPE_PUB_XML_PUBLICATION_CLEAR
,
1777 publication
->category
,
1778 publication
->instance
,
1779 publication
->container
,
1780 publication
->version
,
1784 void sipe_ocs2007_reset_status(struct sipe_core_private
*sipe_private
)
1787 gchar
*publications
;
1789 if (!sipe_private
->user_state_publications
|| g_hash_table_size(sipe_private
->user_state_publications
) == 0) {
1790 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1794 str
= g_string_new(NULL
);
1795 g_hash_table_foreach(sipe_private
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
1796 publications
= g_string_free(str
, FALSE
);
1798 send_presence_publish(sipe_private
, publications
);
1799 g_free(publications
);
1802 /* key is <category><instance><container> */
1803 static gboolean
sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
1808 /* filling keys for our publications if not yet cached */
1809 if (!sipe_private
->our_publication_keys
) {
1810 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
1811 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
1812 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
1813 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
1814 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
1815 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
1816 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
1818 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1819 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
1820 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
1821 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
1822 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
1823 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
1824 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
1825 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
1826 SIPE_DEBUG_INFO("\tNote : %u", 0);
1827 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1830 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1831 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
1833 /* state:machineState */
1834 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1835 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
1836 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1837 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
1839 /* state:userState */
1840 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1841 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
1842 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1843 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
1845 /* state:calendarState */
1846 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1847 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
1848 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1849 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
1851 /* state:calendarState OOF */
1852 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1853 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
1854 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1855 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
1858 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1859 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1860 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1861 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1862 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1863 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
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
, 200));
1868 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1869 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
1870 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1871 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
1873 /* calendarData:WorkingHours */
1874 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1875 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1876 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1877 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1878 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1879 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1880 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1881 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1882 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1883 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1884 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1885 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1887 /* calendarData:FreeBusy */
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
, 1));
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
, 100));
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
, 200));
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
, 300));
1896 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1897 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
1898 sipe_private
->our_publication_keys
= g_slist_append(sipe_private
->our_publication_keys
,
1899 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
1901 //SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
1902 // sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
1905 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1907 entry
= sipe_private
->our_publication_keys
;
1909 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1910 if (sipe_strequal(entry
->data
, key
)) {
1913 entry
= entry
->next
;
1918 static void sipe_refresh_blocked_status_cb(char *buddy_name
,
1919 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1920 struct sipe_core_private
*sipe_private
)
1922 int container_id
= sipe_ocs2007_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1923 gboolean blocked
= (container_id
== 32000);
1924 gboolean blocked_in_blist
= sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC
, buddy_name
);
1926 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1927 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1929 if (blocked
!= blocked_in_blist
) {
1930 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC
, buddy_name
, blocked
);
1934 static void sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1936 g_hash_table_foreach(sipe_private
->buddies
,
1937 (GHFunc
) sipe_refresh_blocked_status_cb
,
1942 * When we receive some self (BE) NOTIFY with a new subscriber
1943 * we sends a setSubscribers request to him [SIP-PRES] 4.8
1946 void sipe_ocs2007_process_roaming_self(struct sipe_core_private
*sipe_private
,
1952 const sipe_xml
*node
;
1953 const sipe_xml
*node2
;
1954 char *display_name
= NULL
;
1956 GSList
*category_names
= NULL
;
1957 int aggreg_avail
= 0;
1958 gboolean do_update_status
= FALSE
;
1959 gboolean has_note_cleaned
= FALSE
;
1960 GHashTable
*devices
;
1962 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
1964 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1967 contact
= get_contact(sipe_private
);
1968 to
= sip_uri_self(sipe_private
);
1971 /* set list of categories participating in this XML */
1972 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
1973 const gchar
*name
= sipe_xml_attribute(node
, "name");
1974 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
1976 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
1977 category_names
? (int) g_slist_length(category_names
) : -1);
1978 /* drop category information */
1979 if (category_names
) {
1980 GSList
*entry
= category_names
;
1982 GHashTable
*cat_publications
;
1983 const gchar
*category
= entry
->data
;
1984 entry
= entry
->next
;
1985 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category
);
1986 cat_publications
= g_hash_table_lookup(sipe_private
->our_publications
, category
);
1987 if (cat_publications
) {
1988 g_hash_table_remove(sipe_private
->our_publications
, category
);
1989 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category
);
1993 g_slist_free(category_names
);
1995 /* filling our categories reflected in roaming data */
1996 devices
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1998 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2000 const gchar
*name
= sipe_xml_attribute(node
, "name");
2001 guint container
= sipe_xml_int_attribute(node
, "container", -1);
2002 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2003 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2004 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2005 sipe_utils_str_to_time(tmp
) : 0;
2007 GHashTable
*cat_publications
= g_hash_table_lookup(sipe_private
->our_publications
, name
);
2009 /* Ex. clear note: <category name="note"/> */
2010 if (container
== (guint
)-1) {
2011 g_free(sipe_private
->note
);
2012 sipe_private
->note
= NULL
;
2013 do_update_status
= TRUE
;
2017 /* Ex. clear note: <category name="note" container="200"/> */
2018 if (instance
== (guint
)-1) {
2019 if (container
== 200) {
2020 g_free(sipe_private
->note
);
2021 sipe_private
->note
= NULL
;
2022 do_update_status
= TRUE
;
2024 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name
, container
);
2025 sipe_remove_category_container_publications(
2026 sipe_private
->our_publications
, name
, container
);
2030 /* key is <category><instance><container> */
2031 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2032 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key
, version
);
2034 /* capture all userState publication for later clean up if required */
2035 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2036 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2038 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2039 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2040 publication
->category
= g_strdup(name
);
2041 publication
->instance
= instance
;
2042 publication
->container
= container
;
2043 publication
->version
= version
;
2045 if (!sipe_private
->user_state_publications
) {
2046 sipe_private
->user_state_publications
= g_hash_table_new_full(
2047 g_str_hash
, g_str_equal
,
2048 g_free
, (GDestroyNotify
)free_publication
);
2050 g_hash_table_insert(sipe_private
->user_state_publications
, g_strdup(key
), publication
);
2051 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2056 /* count each client instance only once */
2057 if (sipe_strequal(name
, "device"))
2058 g_hash_table_replace(devices
, g_strdup_printf("%u", instance
), NULL
);
2060 if (sipe_is_our_publication(sipe_private
, key
)) {
2061 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2063 publication
->category
= g_strdup(name
);
2064 publication
->instance
= instance
;
2065 publication
->container
= container
;
2066 publication
->version
= version
;
2068 /* filling publication->availability */
2069 if (sipe_strequal(name
, "state")) {
2070 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2071 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2074 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2076 publication
->availability
= atoi(avail_str
);
2080 /* for calendarState */
2081 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2082 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2083 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2085 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2087 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2088 sipe_backend_activity_to_token(SIPE_ACTIVITY_IN_MEETING
)))
2090 event
->is_meeting
= TRUE
;
2093 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2094 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2096 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2097 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2098 publication
->cal_event_hash
);
2099 sipe_cal_event_free(event
);
2102 /* filling publication->note */
2103 if (sipe_strequal(name
, "note")) {
2104 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2106 if (!has_note_cleaned
) {
2107 has_note_cleaned
= TRUE
;
2109 g_free(sipe_private
->note
);
2110 sipe_private
->note
= NULL
;
2111 sipe_private
->note_since
= publish_time
;
2113 do_update_status
= TRUE
;
2116 g_free(publication
->note
);
2117 publication
->note
= NULL
;
2121 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2123 if (publish_time
>= sipe_private
->note_since
) {
2124 g_free(sipe_private
->note
);
2125 sipe_private
->note
= g_strdup(publication
->note
);
2126 sipe_private
->note_since
= publish_time
;
2127 if (sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF"))
2128 SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE
);
2130 SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE
);
2132 do_update_status
= TRUE
;
2137 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2138 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2139 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2140 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2142 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2143 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2145 if (xn_working_hours
) {
2146 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2150 if (!cat_publications
) {
2151 cat_publications
= g_hash_table_new_full(
2152 g_str_hash
, g_str_equal
,
2153 g_free
, (GDestroyNotify
)free_publication
);
2154 g_hash_table_insert(sipe_private
->our_publications
, g_strdup(name
), cat_publications
);
2155 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name
);
2157 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2158 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key
, version
);
2162 /* aggregateState (not an our publication) from 2-nd container */
2163 if (sipe_strequal(name
, "state") && container
== 2) {
2164 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2166 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2167 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2170 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2172 aggreg_avail
= atoi(avail_str
);
2177 do_update_status
= TRUE
;
2181 /* userProperties published by server from AD */
2182 if (!sipe_private
->csta
&&
2183 sipe_strequal(name
, "userProperties")) {
2184 const sipe_xml
*line
;
2185 /* line, for Remote Call Control (RCC) */
2186 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2187 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2188 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2191 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2193 line_uri
= sipe_xml_data(line
);
2195 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2196 sip_csta_open(sipe_private
, line_uri
, line_server
);
2204 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2205 sipe_private
->our_publications
? (int) g_hash_table_size(sipe_private
->our_publications
) : -1);
2207 /* active clients for user account */
2208 if (g_hash_table_size(devices
) > 1) {
2209 SIPE_CORE_PRIVATE_FLAG_SET(MPOP
);
2210 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2211 g_hash_table_size(devices
));
2213 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP
);
2214 SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2216 g_hash_table_destroy(devices
);
2219 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2220 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2221 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2224 sipe_private
->containers
= g_slist_remove(sipe_private
->containers
, container
);
2225 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2226 sipe_ocs2007_free_container(container
);
2228 container
= g_new0(struct sipe_container
, 1);
2230 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2231 sipe_private
->containers
= g_slist_append(sipe_private
->containers
, container
);
2232 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2234 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2235 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2236 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2237 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2238 container
->members
= g_slist_append(container
->members
, member
);
2239 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2240 member
->type
, member
->value
? member
->value
: "");
2244 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2245 SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET
) ? "TRUE" : "FALSE");
2246 if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET
) && sipe_xml_child(xml
, "containers")) {
2247 char *container_xmls
= NULL
;
2248 int sameEnterpriseAL
= sipe_ocs2007_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2249 int federatedAL
= sipe_ocs2007_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2251 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2252 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL
);
2253 /* initial set-up to let counterparties see your status */
2254 if (sameEnterpriseAL
< 0) {
2255 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2256 guint version
= container
? container
->version
: 0;
2257 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2259 if (federatedAL
< 0) {
2260 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2261 guint version
= container
? container
->version
: 0;
2262 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2264 SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET
);
2266 if (container_xmls
) {
2267 sipe_send_set_container_members(sipe_private
, container_xmls
);
2269 g_free(container_xmls
);
2272 /* Refresh contacts' blocked status */
2273 sipe_refresh_blocked_status(sipe_private
);
2276 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2278 const char *acknowledged
;
2282 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2283 if (!user
) continue;
2284 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user
);
2285 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2286 uri
= sip_uri_from_name(user
);
2288 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
2290 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2291 if(sipe_strcase_equal(acknowledged
,"false")){
2292 SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user
);
2293 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
)) {
2294 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC
, uri
, display_name
);
2297 hdr
= g_strdup_printf(
2299 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2301 body
= g_strdup_printf(
2302 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2303 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2304 "</setSubscribers>", user
);
2306 sip_transport_service(sipe_private
,
2314 g_free(display_name
);
2321 /* Publish initial state if not yet.
2322 * Assuming this happens on initial responce to subscription to roaming-self
2323 * so we've already updated our roaming data in full.
2326 if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH
)) {
2327 send_publish_category_initial(sipe_private
);
2328 sipe_groupchat_init(sipe_private
);
2329 SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH
);
2331 sipe_cal_delayed_calendar_update(sipe_private
);
2332 do_update_status
= FALSE
;
2333 } else if (aggreg_avail
) {
2336 (aggreg_avail
< SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE
)) {
2338 sipe_status_set_token(sipe_private
,
2339 sipe_ocs2007_status_from_legacy_availability(aggreg_avail
));
2341 /* do not let offline status switch us off */
2342 sipe_status_set_activity(sipe_private
,
2343 SIPE_ACTIVITY_INVISIBLE
);
2347 if (do_update_status
) {
2348 sipe_status_and_note(sipe_private
, NULL
);
2355 * for Access levels menu
2357 #define INDENT_FMT " %s"
2360 * Member is indirectly belong to access level container.
2361 * For example 'sameEnterprise' is in the container and user
2362 * belongs to that same enterprise.
2364 #define INDENT_MARKED_INHERITED_FMT "= %s"
2366 static struct sipe_backend_buddy_menu
*access_levels_menu(struct sipe_core_private
*sipe_private
,
2367 struct sipe_backend_buddy_menu
*menu
,
2368 const gchar
*member_type
,
2369 const gchar
*member_value
,
2370 const gboolean extra_menu
)
2373 menu
= sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC
);
2376 (void) member_value
;
2378 (void) blist_menu_remember_container
;
2379 (void) create_container
;
2383 static struct sipe_backend_buddy_menu
*access_groups_menu(struct sipe_core_private
*sipe_private
)
2385 struct sipe_backend_buddy_menu
*menu
= sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC
);
2386 GSList
*access_domains
, *entry
;
2388 menu
= sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC
,
2390 _("People in my company"),
2391 access_levels_menu(sipe_private
,
2397 /* this is original name, don't edit */
2398 menu
= sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC
,
2400 _("People in domains connected with my company"),
2401 access_levels_menu(sipe_private
,
2407 menu
= sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC
,
2409 _("People in public domains"),
2410 access_levels_menu(sipe_private
,
2416 entry
= access_domains
= get_access_domains(sipe_private
);
2418 gchar
*domain
= entry
->data
;
2419 gchar
*menu_name
= g_strdup_printf(_("People at %s"), domain
);
2421 /* takes over ownership of entry->data (= domain) */
2422 menu
= sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC
,
2425 access_levels_menu(sipe_private
,
2432 entry
= entry
->next
;
2434 g_slist_free(access_domains
);
2437 /* People in domains connected with my company */
2438 menu
= sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC
,
2440 "-------------------------------------------");
2442 menu
= sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC
,
2444 _("Add new domain..."),
2445 SIPE_BUDDY_MENU_ADD_NEW_DOMAIN
,
2451 struct sipe_backend_buddy_menu
*sipe_ocs2007_access_control_menu(struct sipe_core_private
*sipe_private
,
2452 const gchar
*buddy_name
)
2454 struct sipe_backend_buddy_menu
*menu
= sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC
);
2458 * Workaround for missing libpurple API to release resources allocated
2459 * during blist_node_menu() callback. See also:
2461 * <http://developer.pidgin.im/ticket/12597>
2463 * We remember all memory blocks in a list and deallocate them when
2465 * - the next time we enter the callback, or
2466 * - the account is disconnected
2468 * That means that after the buddy menu has been closed we have unused
2469 * resources but at least we don't leak them anymore...
2471 sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC
);
2473 label
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
2474 menu
= sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC
,
2477 SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP
,
2481 label
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
2482 menu
= sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC
,
2485 access_groups_menu(sipe_private
));
2488 menu
= access_levels_menu(sipe_private
,
2491 sipe_get_no_sip_uri(buddy_name
),